Skip to content

Commit f275498

Browse files
authored
fix: lazy compilation with only loader module (#11443)
1 parent 39cab6d commit f275498

File tree

5 files changed

+61
-7
lines changed

5 files changed

+61
-7
lines changed

crates/rspack_plugin_lazy_compilation/src/module.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ pub(crate) struct LazyCompilationProxyModule {
4343

4444
source_map_kind: SourceMapKind,
4545
create_data: ModuleFactoryCreateData,
46-
pub resource: String,
46+
resource: String,
47+
virtual_trigger_path: String,
4748

4849
pub active: bool,
4950
pub data: String,
@@ -70,6 +71,7 @@ impl LazyCompilationProxyModule {
7071
lib_ident: Option<String>,
7172
create_data: ModuleFactoryCreateData,
7273
resource: String,
74+
virtual_trigger_path: String,
7375
cacheable: bool,
7476
active: bool,
7577
data: String,
@@ -90,6 +92,7 @@ impl LazyCompilationProxyModule {
9092
create_data,
9193
readable_identifier,
9294
lib_ident,
95+
virtual_trigger_path,
9396
resource,
9497
identifier,
9598
source_map_kind: SourceMapKind::empty(),
@@ -173,7 +176,7 @@ impl Module for LazyCompilationProxyModule {
173176
} else {
174177
let mut files = FxHashSet::default();
175178
files.extend(self.create_data.file_dependencies.clone());
176-
files.insert(Path::new(&self.resource).into());
179+
files.insert(Path::new(&self.virtual_trigger_path).into());
177180
self.build_info.file_dependencies = files;
178181
}
179182

crates/rspack_plugin_lazy_compilation/src/plugin.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use std::{
22
fmt::Debug,
3+
hash::Hash,
34
sync::{Arc, LazyLock},
45
};
56

7+
use rspack_collections::Identifier;
68
use rspack_core::{
79
BoxModule, Compilation, CompilationId, CompilationParams, CompilerCompilation, CompilerId,
810
DependencyType, EntryDependency, LibIdentOptions, Module, ModuleFactory, ModuleFactoryCreateData,
@@ -186,18 +188,18 @@ async fn normal_module_factory_module(
186188
let lib_ident = module.lib_ident(LibIdentOptions {
187189
context: module_factory_create_data.options.context.as_str(),
188190
});
191+
let virtual_trigger_path = create_virtual_trigger_path(&module_identifier);
192+
189193
let info = backend
190-
.module(
191-
module_identifier,
192-
create_data.resource_resolve_data.resource.clone(),
193-
)
194+
.module(module_identifier, virtual_trigger_path.to_string())
194195
.await?;
195196

196197
*module = Box::new(LazyCompilationProxyModule::new(
197198
module_identifier,
198199
lib_ident.map(|ident| ident.into_owned()),
199200
module_factory_create_data.clone(),
200-
create_data.resource_resolve_data.resource.clone(),
201+
create_data.resource_resolve_data.resource.to_string(),
202+
virtual_trigger_path,
201203
self.cacheable,
202204
info.active,
203205
info.data,
@@ -221,3 +223,13 @@ impl<T: Backend + 'static, F: LazyCompilationTestCheck + 'static> Plugin
221223
Ok(())
222224
}
223225
}
226+
227+
// Generates a virtual file path to trigger module graph updates for lazy compilation.
228+
//
229+
// This creates a synthetic file path that can be used as a "changed file" parameter
230+
// to trigger module graph updates, specifically for modules without associated physical
231+
// files (e.g., loader-only imports like `import("./loader!")`).
232+
fn create_virtual_trigger_path(module_identifier: &Identifier) -> String {
233+
let hash = module_identifier.precomputed_hash();
234+
format!("/rspack_lazy_compilation/{:x}", hash)
235+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { expect, test } from "@/fixtures";
2+
3+
test("lazy compilation handles loader-only entries without resource paths", async ({ page }) => {
4+
// Wait for the lazy-compiled content to load
5+
const actionButton = page.getByRole("button", { name: "Click me" });
6+
await expect(actionButton).toBeVisible({ timeout: 1000 });
7+
8+
// Test that the lazy module functions correctly
9+
await actionButton.click();
10+
11+
// Verify successful execution
12+
await expect(page).toHaveURL(/success$/);
13+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = function () {
2+
return `const button = document.createElement("button");
3+
button.textContent = "Click me";
4+
button.onclick = () => {
5+
history.pushState(null, "", "success");
6+
};
7+
document.body.appendChild(button);`
8+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { rspack } = require("@rspack/core");
2+
3+
/** @type { import('@rspack/core').RspackOptions } */
4+
module.exports = {
5+
context: __dirname,
6+
entry: {
7+
main: "./loader!"
8+
},
9+
stats: "none",
10+
mode: "development",
11+
plugins: [new rspack.HtmlRspackPlugin(), new rspack.CssExtractRspackPlugin()],
12+
experiments: {
13+
lazyCompilation: true
14+
},
15+
devServer: {
16+
hot: true
17+
}
18+
};

0 commit comments

Comments
 (0)