Skip to content

Commit b4bf7a8

Browse files
authored
feat(mf): support lazy compilation (#11779)
1 parent 162bfc3 commit b4bf7a8

File tree

15 files changed

+229
-44
lines changed

15 files changed

+229
-44
lines changed

crates/rspack_plugin_mf/src/sharing/consume_shared_runtime_module.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,35 @@ impl RuntimeModule for ConsumeSharedRuntimeModule {
104104
add_module(mid, chunk, &mut initial_consumes);
105105
}
106106
}
107-
if module_id_to_consume_data_mapping.is_empty() {
108-
return Ok("".to_string());
109-
}
110-
let module_id_to_consume_data_mapping = module_id_to_consume_data_mapping
111-
.into_iter()
112-
.map(|(k, v)| format!("{}: {}", json_stringify(&k), v))
113-
.collect::<Vec<_>>()
114-
.join(", ");
107+
let module_id_to_consume_data_mapping = if module_id_to_consume_data_mapping.is_empty() {
108+
"{}".to_string()
109+
} else {
110+
format!(
111+
"{{{}}}",
112+
module_id_to_consume_data_mapping
113+
.into_iter()
114+
.map(|(k, v)| format!("{}: {}", json_stringify(&k), v))
115+
.collect::<Vec<_>>()
116+
.join(", ")
117+
)
118+
};
119+
let chunk_mapping = if chunk_to_module_mapping.is_empty() {
120+
"{}".to_string()
121+
} else {
122+
json_stringify(&chunk_to_module_mapping)
123+
};
124+
let initial_consumes_json = if initial_consumes.is_empty() {
125+
"[]".to_string()
126+
} else {
127+
json_stringify(&initial_consumes)
128+
};
115129
let mut source = format!(
116130
r#"
117-
__webpack_require__.consumesLoadingData = {{ chunkMapping: {chunk_mapping}, moduleIdToConsumeDataMapping: {{ {module_to_consume_data_mapping} }}, initialConsumes: {initial_consumes} }};
131+
__webpack_require__.consumesLoadingData = {{ chunkMapping: {chunk_mapping}, moduleIdToConsumeDataMapping: {module_to_consume_data_mapping}, initialConsumes: {initial_consumes_json} }};
118132
"#,
119-
chunk_mapping = json_stringify(&chunk_to_module_mapping),
133+
chunk_mapping = chunk_mapping,
120134
module_to_consume_data_mapping = module_id_to_consume_data_mapping,
121-
initial_consumes = json_stringify(&initial_consumes),
135+
initial_consumes_json = initial_consumes_json,
122136
);
123137
if self.enhanced {
124138
if ChunkGraph::get_chunk_runtime_requirements(compilation, &chunk_ukey)

packages/rspack/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"zod-validation-error": "3.5.3"
6868
},
6969
"dependencies": {
70-
"@module-federation/runtime-tools": "0.19.1",
70+
"@module-federation/runtime-tools": "0.20.0",
7171
"@rspack/binding": "workspace:*",
7272
"@rspack/lite-tapable": "1.0.1"
7373
},

packages/rspack/src/runtime/moduleFederationDefaultRuntime.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ module.exports = function () {
159159
"chunkMapping",
160160
() => remotesLoadingChunkMapping
161161
);
162+
early(
163+
__webpack_require__.federation.bundlerRuntimeOptions.remotes,
164+
"remoteInfos",
165+
() => __module_federation_remote_infos__
166+
);
162167
early(
163168
__webpack_require__.federation.bundlerRuntimeOptions.remotes,
164169
"idToExternalAndNameMapping",

pnpm-lock.yaml

Lines changed: 29 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { expect, test } from "@/fixtures";
2+
3+
test("should load remote and shared success", async ({ page }) => {
4+
await page.waitForSelector('button:has-text("Click me")');
5+
6+
// trigger lazy-compile
7+
await page.getByText("Click me").click();
8+
9+
// Wait for the component to appear with a more reliable wait
10+
await page.waitForSelector('div:has-text("RemoteComponent")');
11+
12+
// Check that the component was loaded and displayed
13+
const RemoteComponentCount = await page.getByText("RemoteComponent").count();
14+
expect(RemoteComponentCount).toBe(1);
15+
16+
// Wait for the component to appear with a more reliable wait
17+
await page.waitForSelector('div:has-text("SharedReact")');
18+
// Check that the shared component was loaded and displayed
19+
const SharedReactCount = await page.getByText("SharedReact").count();
20+
expect(SharedReactCount).toBe(1);
21+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const { rspack } = require("@rspack/core");
2+
const ReactRefreshPlugin = require("@rspack/plugin-react-refresh");
3+
4+
/** @type { import('@rspack/core').RspackOptions } */
5+
module.exports = {
6+
context: __dirname,
7+
entry: "./src/index.jsx",
8+
mode: "development",
9+
devtool:false,
10+
resolve: {
11+
extensions: ["...", ".jsx"]
12+
},
13+
module: {
14+
rules: [
15+
{
16+
test: /\.(jsx?|tsx?)$/,
17+
use: [
18+
{
19+
loader: "builtin:swc-loader",
20+
options: {
21+
jsc: {
22+
parser: {
23+
syntax: "typescript",
24+
tsx: true
25+
},
26+
transform: {
27+
react: {
28+
runtime: "automatic",
29+
development: true,
30+
refresh: true,
31+
}
32+
},
33+
},
34+
}
35+
},
36+
]
37+
}
38+
]
39+
},
40+
plugins: [new rspack.HtmlRspackPlugin({ template: "./src/index.html" }), new rspack.container.ModuleFederationPlugin({
41+
name:"host",
42+
remotes: {
43+
remote: "remote@http://localhost:5679/remoteEntry.js"
44+
},
45+
// prevent init remote entry
46+
shareStrategy: 'loaded-first',
47+
shared: {
48+
react: {},
49+
'react-dom': {}
50+
},
51+
runtimePlugins: [require.resolve('./runtime-plugin.js')]
52+
}),
53+
new ReactRefreshPlugin(),
54+
55+
],
56+
lazyCompilation:true,
57+
devServer: {
58+
hot: true,
59+
port: 5678,
60+
devMiddleware: {
61+
writeToDisk: true
62+
}
63+
}
64+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = function () {
2+
let component;
3+
return {
4+
name: 'proxy-remote',
5+
async errorLoadRemote() {
6+
if(!component){
7+
component = document.createElement("div");
8+
component.textContent = "RemoteComponent";
9+
document.body.appendChild(component);
10+
}
11+
12+
return ()=>component;
13+
},
14+
};
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from "react";
2+
import ReactDOM from "react-dom/client";
3+
4+
ReactDOM.createRoot(document.getElementById("root")).render(
5+
<React.StrictMode>
6+
<button type="button" onClick={() => {
7+
import("./remote-entry.js");
8+
import("./share-entry.js");
9+
}}>
10+
Click me
11+
</button>
12+
</React.StrictMode>
13+
);
14+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Document</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
</body>
12+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import('./bootstrap.jsx')

0 commit comments

Comments
 (0)