Skip to content

Commit 8be9ecf

Browse files
committed
feat(bridge-react): support react router 8 package layout
1 parent ac78d02 commit 8be9ecf

19 files changed

Lines changed: 578 additions & 55 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@module-federation/bridge-react": patch
3+
"@module-federation/bridge-react-webpack-plugin": patch
4+
"@module-federation/modern-js-v3": patch
5+
---
6+
7+
Support the React Router 8 package layout in the React bridge without requiring react-router-dom.

packages/bridge/bridge-react-webpack-plugin/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,28 @@ export default defineConfig({
7474
});
7575
```
7676
77+
For React Router 8 applications, `react-router-dom` is no longer installed.
78+
Import routing APIs from `react-router`, import DOM-specific APIs from
79+
`react-router/dom`, and alias both entries to the v8 bridge proxy:
80+
81+
```js
82+
//rsbuild.config.ts
83+
const reactRouterPath = path.dirname(require.resolve('react-router/package.json'));
84+
85+
export default defineConfig({
86+
source: {
87+
alias: {
88+
'react-router$': path.resolve(__dirname, 'node_modules/@module-federation/bridge-react/dist/router-v8.es.js'),
89+
'react-router/dom$': path.resolve(__dirname, 'node_modules/@module-federation/bridge-react/dist/router-v8-dom.es.js'),
90+
'react-router/dist/development/index.js': reactRouterPath,
91+
'react-router/dist/production/index.js': reactRouterPath,
92+
'react-router/dist/development/dom-export.js': path.join(reactRouterPath, 'dist/development/dom-export.js'),
93+
'react-router/dist/production/dom-export.js': path.join(reactRouterPath, 'dist/production/dom-export.js'),
94+
},
95+
},
96+
});
97+
```
98+
7799
# 3. Load the module with routing
78100
79101
```js
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "react-router",
3+
"version": "6.30.3"
4+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "react-router",
3+
"version": "8.0.0",
4+
"description": "Declarative routing for React applications",
5+
"keywords": [
6+
"react",
7+
"router",
8+
"route",
9+
"routing"
10+
],
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/remix-run/react-router",
14+
"directory": "packages/react-router"
15+
},
16+
"license": "MIT",
17+
"author": "Remix Software <[email protected]>",
18+
"sideEffects": false,
19+
"exports": {
20+
".": {
21+
"types": "./dist/production/index.d.ts",
22+
"default": "./dist/production/index.js"
23+
},
24+
"./dom": {
25+
"types": "./dist/production/dom-export.d.ts",
26+
"default": "./dist/production/dom-export.js"
27+
}
28+
},
29+
"peerDependencies": {
30+
"react": ">=19.2.7",
31+
"react-dom": ">=19.2.7"
32+
},
33+
"engines": {
34+
"node": ">=22.22.0"
35+
}
36+
}

packages/bridge/bridge-react-webpack-plugin/__tests__/utils.spec.ts

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ const resolveRouterV6 = path.resolve(
1414
__dirname,
1515
'../__tests__/mockRouterDir/router-v6/react-router-dom/dist/main.js',
1616
);
17+
const resolveRouterV6Core = path.resolve(
18+
__dirname,
19+
'../__tests__/mockRouterDir/router-v6/react-router',
20+
);
1721
const resolveRouterV6_PkgPath = path.resolve(
1822
__dirname,
1923
'../__tests__/mockRouterDir/router-v6/react-router-dom/package.json',
@@ -26,6 +30,18 @@ const resolveRouterV7_PkgPath = path.resolve(
2630
__dirname,
2731
'../__tests__/mockRouterDir/router-v7/react-router/package.json',
2832
);
33+
const resolveRouterV8 = path.resolve(
34+
__dirname,
35+
'../__tests__/mockRouterDir/router-v8/react-router',
36+
);
37+
const resolveRouterV8Entry = path.join(
38+
resolveRouterV8,
39+
'dist/production/index.js',
40+
);
41+
const resolveRouterV8_PkgPath = path.resolve(
42+
__dirname,
43+
'../__tests__/mockRouterDir/router-v8/react-router/package.json',
44+
);
2945

3046
describe('test checkVersion: should return the correct major version for react-router-dom', () => {
3147
it('should return 5', () => {
@@ -59,17 +75,26 @@ describe('test checkVersion: should return the correct major version for react-r
5975
it('should return 7', () => {
6076
expect(checkVersion('^7.0.0')).toBe(7);
6177
});
78+
79+
it('should return 8', () => {
80+
expect(checkVersion('8.0.0')).toBe(8);
81+
});
82+
83+
it('should return 8', () => {
84+
expect(checkVersion('^8.0.0')).toBe(8);
85+
});
6286
});
6387

64-
describe('test findPackageJson: should return the correct package.json path for react-router-dom v5, v6 and react-router v7', () => {
88+
describe('test findPackageJson: should return the correct package.json path for react-router-dom v5/v6 and react-router v7/v8', () => {
6589
it('should return the package.json path', () => {
6690
expect(findPackageJson(resolveRouterV5)).toBe(resolveRouterV5_PkgPath);
6791
expect(findPackageJson(resolveRouterV6)).toBe(resolveRouterV6_PkgPath);
6892
expect(findPackageJson(resolveRouterV7)).toBe(resolveRouterV7_PkgPath);
93+
expect(findPackageJson(resolveRouterV8)).toBe(resolveRouterV8_PkgPath);
6994
});
7095
});
7196

72-
describe('test getBridgeRouterAlias: should return the correct alias for react-router-dom v5, v6 and react-router v7', () => {
97+
describe('test getBridgeRouterAlias: should return the correct alias for react-router-dom v5/v6 and react-router v7/v8', () => {
7398
it('should return the correct alias for router v5', () => {
7499
const res = getBridgeRouterAlias(resolveRouterV5);
75100
expect(res).toEqual({
@@ -99,4 +124,69 @@ describe('test getBridgeRouterAlias: should return the correct alias for react-r
99124
'react-router-dom/dist/index.js': resolveRouterV7,
100125
});
101126
});
127+
128+
it('should return the correct alias for router v8', () => {
129+
const res = getBridgeRouterAlias(resolveRouterV8);
130+
expect(res).toEqual({
131+
'react-router$': '@module-federation/bridge-react/dist/router-v8.es.js',
132+
'react-router/dom$':
133+
'@module-federation/bridge-react/dist/router-v8-dom.es.js',
134+
'react-router/dist/development/index.js': resolveRouterV8,
135+
'react-router/dist/production/index.js': resolveRouterV8,
136+
'react-router/dist/development/dom-export.js': path.join(
137+
resolveRouterV8,
138+
'dist/development/dom-export.js',
139+
),
140+
'react-router/dist/production/dom-export.js': path.join(
141+
resolveRouterV8,
142+
'dist/production/dom-export.js',
143+
),
144+
});
145+
});
146+
147+
it('should prefer an explicit react-router alias for router v8', () => {
148+
const res = getBridgeRouterAlias({ reactRouterAlias: resolveRouterV8 });
149+
expect(res['react-router$']).toBe(
150+
'@module-federation/bridge-react/dist/router-v8.es.js',
151+
);
152+
expect(res['react-router/dom$']).toBe(
153+
'@module-federation/bridge-react/dist/router-v8-dom.es.js',
154+
);
155+
});
156+
157+
it('should normalize an explicit react-router entry alias for router v8', () => {
158+
const res = getBridgeRouterAlias({
159+
reactRouterAlias: resolveRouterV8Entry,
160+
});
161+
162+
expect(res['react-router/dist/production/index.js']).toBe(resolveRouterV8);
163+
expect(res['react-router/dist/production/dom-export.js']).toBe(
164+
path.join(resolveRouterV8, 'dist/production/dom-export.js'),
165+
);
166+
});
167+
168+
it('should prefer an explicit react-router-dom alias for router v6 when both aliases exist', () => {
169+
const res = getBridgeRouterAlias({
170+
reactRouterAlias: resolveRouterV6Core,
171+
reactRouterDomAlias: resolveRouterV6,
172+
});
173+
174+
expect(res).toEqual({
175+
'react-router-dom$':
176+
'@module-federation/bridge-react/dist/router-v6.es.js',
177+
'react-router-dom/dist/index.js': resolveRouterV6,
178+
});
179+
});
180+
181+
it('should prefer an explicit react-router alias for router v7 when both aliases exist', () => {
182+
const res = getBridgeRouterAlias({
183+
reactRouterAlias: resolveRouterV7,
184+
reactRouterDomAlias: resolveRouterV6,
185+
});
186+
187+
expect(res['react-router$']).toBe(
188+
'@module-federation/bridge-react/dist/router-v7.es.js',
189+
);
190+
expect(res['react-router-dom/dist/index.js']).toBe(resolveRouterV7);
191+
});
102192
});

packages/bridge/bridge-react-webpack-plugin/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ class ReactBridgeAliasChangerPlugin {
4545
const updatedAlias: Record<string, string> = {
4646
// allow `alias` can be override
4747
// [this.alias]: targetFilePath,
48-
...getBridgeRouterAlias(originalAlias['react-router-dom']),
48+
...getBridgeRouterAlias({
49+
reactRouterAlias: originalAlias['react-router'],
50+
reactRouterDomAlias: originalAlias['react-router-dom'],
51+
}),
4952
...originalAlias,
5053
};
5154

0 commit comments

Comments
 (0)