From 90a711fed8d0dc7d4c81fd526722c3c9b2c22f71 Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Thu, 16 Jul 2026 20:11:35 +0800 Subject: [PATCH 1/5] fix: parse preserved createRequire arguments --- .../common_js_imports_parse_plugin.rs | 69 ++++++++----------- .../src/visitors/dependency/parser/mod.rs | 2 +- .../__snapshots__/esm.snap.txt | 15 +++- .../runtimeModeSnapshot/esm.snap.txt | 15 +++- .../import-meta-url-resolve-disabled/index.js | 6 +- .../preserve-import-meta.js | 5 ++ .../rspack.config.js | 8 +++ .../__snapshots__/esm.snap.txt | 21 +++--- .../runtimeModeSnapshot/esm.snap.txt | 21 +++--- .../import-meta-url-resolve/index.js | 7 +- .../import-meta-url-resolve/test.config.js | 11 +-- 11 files changed, 94 insertions(+), 86 deletions(-) create mode 100644 tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/preserve-import-meta.js diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs index fe74436a4070..40dddeca3627 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs @@ -9,6 +9,7 @@ use rspack_core::{ use rspack_error::{Diagnostic, Severity}; use rspack_util::{SpanExt, json_stringify_str}; use swc_atoms::Atom; +use swc_experimental_allocator::CloneIn; use swc_experimental_ecma_ast::{ AssignExpr, AssignOp, CallExpr, Callee, Expr, ExprOrSpread, GetSpan, Ident, Lit, MemberExpr, MemberProp, NewExpr, Span, UnaryExpr, UnaryOp, VarDeclarator, @@ -60,16 +61,16 @@ struct CreateRequireArgument { } #[derive(Default)] -pub struct CreatedRequireReferencesState { - pending: rustc_hash::FxHashMap, +pub struct CreatedRequireReferencesState<'a> { + pending: rustc_hash::FxHashMap>, exported_locals: rustc_hash::FxHashSet, } -struct PendingCreatedRequire { +struct PendingCreatedRequire<'a> { must_keep: bool, callee: DeferredCreateRequireCallee, - arg_span: Span, - arg_value: String, + // Deferred calls skip this expression until their keep/strip state is known. + argument: Expr<'a>, } struct DeferredCreateRequireCallee { @@ -82,13 +83,12 @@ struct DeferredCreateRequireCallee { branch_guard: Option, } -impl CreatedRequireReferencesState { +impl<'a> CreatedRequireReferencesState<'a> { fn add_pending( &mut self, call_span: Span, callee: DeferredCreateRequireCallee, - arg_span: Span, - arg_value: String, + argument: Expr<'a>, ) { // Normal walk refreshes provisional pre-walk data after earlier references may mark it. let must_keep = self @@ -100,8 +100,7 @@ impl CreatedRequireReferencesState { PendingCreatedRequire { must_keep, callee, - arg_span, - arg_value, + argument, }, ); } @@ -112,7 +111,7 @@ impl CreatedRequireReferencesState { } } - fn take_pending(&mut self) -> Vec<(Span, PendingCreatedRequire)> { + fn take_pending(&mut self) -> Vec<(Span, PendingCreatedRequire<'a>)> { let mut pending = std::mem::take(&mut self.pending) .into_iter() .collect::>(); @@ -1071,29 +1070,17 @@ fn add_deferred_create_require_callee_dependency( ); } -fn keep_deferred_create_require_call( - parser: &mut JavascriptParser, - pending: PendingCreatedRequire, +fn keep_deferred_create_require_call<'a>( + parser: &mut JavascriptParser<'a>, + pending: PendingCreatedRequire<'a>, ) { add_deferred_create_require_callee_dependency(parser, pending.callee); - if parser.compiler_options.output.module { - let import_meta_name = &parser.compiler_options.output.import_meta_name; - if import_meta_name != expr_name::IMPORT_META { - parser.add_presentational_dependency(Box::new(ConstDependency::new( - pending.arg_span.into(), - format!("{import_meta_name}.url").into(), - ))); - } - } else { - parser.add_presentational_dependency(Box::new(ConstDependency::new( - pending.arg_span.into(), - json_stringify_str(&pending.arg_value).into(), - ))); - } + // Let the regular parser plugins own children of a call that survives. + parser.walk_expression(&pending.argument); } -fn pre_tag_created_require_declarator( - parser: &mut JavascriptParser, +fn pre_tag_created_require_declarator<'a>( + parser: &mut JavascriptParser<'a>, declarator: &VarDeclarator, declaration: VariableDeclaration<'_>, ) { @@ -1124,7 +1111,7 @@ fn pre_tag_created_require_declarator( return; }; let CreateRequireArgument { - value, + value: _, context, replace_argument: _, } = argument; @@ -1143,15 +1130,14 @@ fn pre_tag_created_require_declarator( parser.created_require_references.add_pending( call.span, deferred_callee, - call.args[0].expr.span(), - value, + call.args[0].expr.clone_in(parser.ast.allocator), ); } #[cold] #[inline(never)] -fn tag_created_require_declarator( - parser: &mut JavascriptParser, +fn tag_created_require_declarator<'a>( + parser: &mut JavascriptParser<'a>, binding: &Ident, call_span: Span, clear_call: bool, @@ -1178,9 +1164,11 @@ fn tag_created_require_declarator( }), ); if let Some(callee) = deferred_callee { - parser - .created_require_references - .add_pending(call_span, callee, args[0].expr.span(), value); + parser.created_require_references.add_pending( + call_span, + callee, + args[0].expr.clone_in(parser.ast.allocator), + ); } else if clear_call { clear_create_require_call(parser, call_span); } else if replace_argument { @@ -2671,7 +2659,10 @@ impl<'p, 'a> JavascriptParserPlugin<'p, 'a> for CommonJsImportsParserPlugin { } } - for (call_span, pending) in parser.created_require_references.take_pending() { + let mut created_require_references = std::mem::take(&mut parser.created_require_references); + let pending_calls = created_require_references.take_pending(); + parser.created_require_references = created_require_references; + for (call_span, pending) in pending_calls { if pending.must_keep { keep_deferred_create_require_call(parser, pending); } else { diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs index ce8cb5bfcb0a..1a2793855f91 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs @@ -417,7 +417,7 @@ pub struct JavascriptParser<'parser> { pub(crate) destructuring_assignment_properties: DestructuringAssignmentPropertiesMap, pub(crate) dynamic_import_references: ImportsReferencesState, pub(crate) common_js_require_references: RequireReferencesState, - pub(crate) created_require_references: CreatedRequireReferencesState, + pub(crate) created_require_references: CreatedRequireReferencesState<'parser>, pub(crate) worker_index: u32, pub(crate) parser_exports_state: Option, pub(crate) local_modules: Vec, diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/esm.snap.txt index bd453f039cae..e4fb1d8442fe 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/esm.snap.txt +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/esm.snap.txt @@ -26,15 +26,23 @@ const req = /* createRequire() */ undefined; const bundledValue = __webpack_require__(/*! ./dep.js */ "./dep.js"); +// ./preserve-import-meta.js + + +const preserve_import_meta_req = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); + +const preservedResolved = preserve_import_meta_req.resolve("path"); + // ./index.js -const index_req = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); + +const index_req = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/index.js'); const resolved = index_req.resolve("path"); -it("should preserve import.meta.url when requireResolve is disabled", async () => { +it("should delegate import.meta.url parsing when requireResolve is disabled", async () => { const fs = await import(/* webpackIgnore: true */ "node:fs"); const path = await import(/* webpackIgnore: true */ "node:path"); const source = fs.readFileSync(path.join(__rspack_import_meta_dirname__, "main.mjs"), "utf-8"); @@ -42,9 +50,10 @@ it("should preserve import.meta.url when requireResolve is disabled", async () = "(0,external_node_module_namespaceObject." + "createRequire)(import.meta.url)"; expect(source.split(runtimeCreateRequire)).toHaveLength(2); - expect(source).not.toContain("file:" + "//"); + expect(source).toContain("file:" + "//"); expect(source).toContain("/* createRequire() */ undefined"); expect(resolved).toBe("path"); + expect(preservedResolved).toBe("path"); expect(bundledValue).toBe("dep"); }); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/runtimeModeSnapshot/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/runtimeModeSnapshot/esm.snap.txt index 1c0fe0b0823d..49afedd6922b 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/runtimeModeSnapshot/esm.snap.txt +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/__snapshots__/runtimeModeSnapshot/esm.snap.txt @@ -26,15 +26,23 @@ const req = /* createRequire() */ undefined; const bundledValue = __rspack_context.r(/*! ./dep.js */ "./dep.js"); +// ./preserve-import-meta.js + + +const preserve_import_meta_req = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); + +const preservedResolved = preserve_import_meta_req.resolve("path"); + // ./index.js -const index_req = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); + +const index_req = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/index.js'); const resolved = index_req.resolve("path"); -it("should preserve import.meta.url when requireResolve is disabled", async () => { +it("should delegate import.meta.url parsing when requireResolve is disabled", async () => { const fs = await import(/* webpackIgnore: true */ "node:fs"); const path = await import(/* webpackIgnore: true */ "node:path"); const source = fs.readFileSync(path.join(__rspack_import_meta_dirname__, "main.mjs"), "utf-8"); @@ -42,9 +50,10 @@ it("should preserve import.meta.url when requireResolve is disabled", async () = "(0,external_node_module_namespaceObject." + "createRequire)(import.meta.url)"; expect(source.split(runtimeCreateRequire)).toHaveLength(2); - expect(source).not.toContain("file:" + "//"); + expect(source).toContain("file:" + "//"); expect(source).toContain("/* createRequire() */ undefined"); expect(resolved).toBe("path"); + expect(preservedResolved).toBe("path"); expect(bundledValue).toBe("dep"); }); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/index.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/index.js index 8cdb02c768b5..dc66190d155c 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/index.js +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/index.js @@ -1,11 +1,12 @@ import { createRequire } from "node:module"; import { bundledValue } from "./only-require.js"; +import { preservedResolved } from "./preserve-import-meta.js"; const req = createRequire(import.meta.url); export const resolved = req.resolve("path"); -it("should preserve import.meta.url when requireResolve is disabled", async () => { +it("should delegate import.meta.url parsing when requireResolve is disabled", async () => { const fs = await import(/* webpackIgnore: true */ "node:fs"); const path = await import(/* webpackIgnore: true */ "node:path"); const source = fs.readFileSync(path.join(__dirname, "main.mjs"), "utf-8"); @@ -13,8 +14,9 @@ it("should preserve import.meta.url when requireResolve is disabled", async () = "(0,external_node_module_namespaceObject." + "createRequire)(import.meta.url)"; expect(source.split(runtimeCreateRequire)).toHaveLength(2); - expect(source).not.toContain("file:" + "//"); + expect(source).toContain("file:" + "//"); expect(source).toContain("/* createRequire() */ undefined"); expect(resolved).toBe("path"); + expect(preservedResolved).toBe("path"); expect(bundledValue).toBe("dep"); }); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/preserve-import-meta.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/preserve-import-meta.js new file mode 100644 index 000000000000..db8e89c13e40 --- /dev/null +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/preserve-import-meta.js @@ -0,0 +1,5 @@ +import { createRequire } from "node:module"; + +const req = createRequire(import.meta.url); + +export const preservedResolved = req.resolve("path"); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/rspack.config.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/rspack.config.js index d01aed4b2ee3..d483be050f2e 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/rspack.config.js +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve-disabled/rspack.config.js @@ -6,5 +6,13 @@ module.exports = { requireResolve: false, }, }, + rules: [ + { + test: /preserve-import-meta\.js$/, + parser: { + importMeta: false, + }, + }, + ], }, }; diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt index 856efd87793e..f8f8c05aa34d 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt @@ -23,21 +23,21 @@ const external_node_module_namespaceObject = __rspack_createRequire_require("nod var external_node_module_default = /*#__PURE__*/__webpack_require__.n(external_node_module_namespaceObject);// ./default-require.js -const defaultRequire = external_node_module_default().createRequire(import.meta.url); +const defaultRequire = external_node_module_default().createRequire('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/default-require.js'); const defaultUnknown = defaultRequire.unknown; // ./exported-require.js -const exportedRequire = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); +const exportedRequire = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/exported-require.js'); // ./namespace-require.js -const namespaceRequire = external_node_module_namespaceObject.createRequire(import.meta.url); +const namespaceRequire = external_node_module_namespaceObject.createRequire('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/namespace-require.js'); const namespaceUnknown = namespaceRequire.unknown; @@ -48,14 +48,14 @@ function getRequire() { return req; } -const req = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); +const req = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/pre-declared-require.js'); function getNestedRequire() { function readRequire() { return nestedReq; } - const nestedReq = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); + const nestedReq = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/pre-declared-require.js'); return readRequire(); } @@ -77,8 +77,8 @@ const pre_declared_require_call_req = /* createRequire() */ undefined; const index_req = /* createRequire() */ undefined; -const requireWithUnknownMember = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); -const requireAsValue = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); +const requireWithUnknownMember = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); +const requireAsValue = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); const identity = value => value; @@ -92,14 +92,11 @@ it("should consume createRequire(import.meta.url) like webpack", async () => { const path = await import(/* webpackIgnore: true */ "node:path"); const source = fs.readFileSync(path.join(__rspack_import_meta_dirname__, "main.mjs"), "utf-8"); const fileUrlScheme = "file:" + "//"; - const normalizedRoot = "<" + "ROOT>"; const runtimeCreateRequire = "(0,external_node_module_namespaceObject." + "createRequire)(import.meta.url)"; - expect(source).not.toContain(fileUrlScheme); - expect(source).not.toContain("createRequire('" + normalizedRoot); - expect(source).not.toContain('createRequire("' + normalizedRoot); - expect(source.split(runtimeCreateRequire)).toHaveLength(6); + expect(source).toContain(fileUrlScheme); + expect(source).not.toContain(runtimeCreateRequire); expect(source).toContain("/* createRequire() */ undefined"); expect(source).toContain("__webpack_require__("); expect(source).toContain("/*require.resolve*/"); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt index 250bb550aabc..2323847cc32a 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt @@ -23,21 +23,21 @@ const external_node_module_namespaceObject = __rspack_createRequire_require("nod var external_node_module_default = /*#__PURE__*/__rspack_context.n(external_node_module_namespaceObject);// ./default-require.js -const defaultRequire = external_node_module_default().createRequire(import.meta.url); +const defaultRequire = external_node_module_default().createRequire('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/default-require.js'); const defaultUnknown = defaultRequire.unknown; // ./exported-require.js -const exportedRequire = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); +const exportedRequire = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/exported-require.js'); // ./namespace-require.js -const namespaceRequire = external_node_module_namespaceObject.createRequire(import.meta.url); +const namespaceRequire = external_node_module_namespaceObject.createRequire('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/namespace-require.js'); const namespaceUnknown = namespaceRequire.unknown; @@ -48,14 +48,14 @@ function getRequire() { return req; } -const req = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); +const req = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/pre-declared-require.js'); function getNestedRequire() { function readRequire() { return nestedReq; } - const nestedReq = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); + const nestedReq = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/pre-declared-require.js'); return readRequire(); } @@ -77,8 +77,8 @@ const pre_declared_require_call_req = /* createRequire() */ undefined; const index_req = /* createRequire() */ undefined; -const requireWithUnknownMember = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); -const requireAsValue = (0,external_node_module_namespaceObject.createRequire)(import.meta.url); +const requireWithUnknownMember = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); +const requireAsValue = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); const identity = value => value; @@ -92,14 +92,11 @@ it("should consume createRequire(import.meta.url) like webpack", async () => { const path = await import(/* webpackIgnore: true */ "node:path"); const source = fs.readFileSync(path.join(__rspack_import_meta_dirname__, "main.mjs"), "utf-8"); const fileUrlScheme = "file:" + "//"; - const normalizedRoot = "<" + "ROOT>"; const runtimeCreateRequire = "(0,external_node_module_namespaceObject." + "createRequire)(import.meta.url)"; - expect(source).not.toContain(fileUrlScheme); - expect(source).not.toContain("createRequire('" + normalizedRoot); - expect(source).not.toContain('createRequire("' + normalizedRoot); - expect(source.split(runtimeCreateRequire)).toHaveLength(6); + expect(source).toContain(fileUrlScheme); + expect(source).not.toContain(runtimeCreateRequire); expect(source).toContain("/* createRequire() */ undefined"); expect(source).toContain("__webpack_require__("); expect(source).toContain("/*require.resolve*/"); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js index eeffc9d84f58..a6e04a0521d4 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js @@ -24,14 +24,11 @@ it("should consume createRequire(import.meta.url) like webpack", async () => { const path = await import(/* webpackIgnore: true */ "node:path"); const source = fs.readFileSync(path.join(__dirname, "main.mjs"), "utf-8"); const fileUrlScheme = "file:" + "//"; - const normalizedRoot = "<" + "ROOT>"; const runtimeCreateRequire = "(0,external_node_module_namespaceObject." + "createRequire)(import.meta.url)"; - expect(source).not.toContain(fileUrlScheme); - expect(source).not.toContain("createRequire('" + normalizedRoot); - expect(source).not.toContain('createRequire("' + normalizedRoot); - expect(source.split(runtimeCreateRequire)).toHaveLength(6); + expect(source).toContain(fileUrlScheme); + expect(source).not.toContain(runtimeCreateRequire); expect(source).toContain("/* createRequire() */ undefined"); expect(source).toContain("__webpack_require__("); expect(source).toContain("/*require.resolve*/"); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/test.config.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/test.config.js index 948e81ed8077..10643f13f5e5 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/test.config.js +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/test.config.js @@ -8,15 +8,8 @@ module.exports = { "utf-8" ); - expect(source).not.toContain("file://"); - expect(source).not.toContain("createRequire('"); - expect(source).not.toContain('createRequire("'); - expect(source).toContain( - "external_node_module_namespaceObject.createRequire(import.meta.url)" - ); - expect(source).toContain( - "external_node_module_default().createRequire(import.meta.url)" - ); + expect(source).toContain("file://"); + expect(source).toMatch(/createRequire\)?\(['"]file:\/\//); expect(source).toContain("/* createRequire() */ undefined"); expect(source).toContain("__webpack_require__("); expect(source).toContain("/*require.resolve*/"); From 62fd0e6f24e8d8f26586c40436cd8aa812994d81 Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Fri, 17 Jul 2026 03:03:56 +0800 Subject: [PATCH 2/5] fix: escape import.meta.url replacements --- .../src/parser_plugin/import_meta_plugin.rs | 10 ++++++++-- .../import-meta-url-resolve/__snapshots__/esm.snap.txt | 9 +++++++++ .../__snapshots__/runtimeModeSnapshot/esm.snap.txt | 9 +++++++++ .../create-require/import-meta-url-resolve/index.js | 2 ++ .../import-meta-url-resolve/quote'require.js | 5 +++++ 5 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/quote'require.js diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs index e63877b01fbd..222a179fa40e 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs @@ -6,7 +6,7 @@ use rspack_core::{ RscModuleType, RuntimeGlobals, RuntimeRequirementsDependency, property_access, }; use rspack_error::{Error, Severity}; -use rspack_util::SpanExt; +use rspack_util::{SpanExt, json_stringify_str}; use swc_atoms::Atom; use swc_experimental_ecma_ast::{ AssignExpr, CallExpr, Expr, GetSpan, MemberExpr, MemberProp, MetaPropKind, OptChainBase, @@ -39,6 +39,12 @@ use crate::{ }, }; +fn single_quoted_string(value: &str) -> String { + let json = json_stringify_str(value); + let escaped = json[1..json.len() - 1].replace('\'', "\\'"); + concat_string!("'", escaped, "'") +} + fn create_import_meta_resolve_context_dependency( parser: &mut JavascriptParser, param: &BasicEvaluatedExpression, @@ -252,7 +258,7 @@ impl ImportMetaBuiltinProperty { } let replacement = match self.property { - ImportMetaKnownProperties::URL => concat_string!("'", plugin.import_meta_url(parser), "'"), + ImportMetaKnownProperties::URL => single_quoted_string(&plugin.import_meta_url(parser)), ImportMetaKnownProperties::WEBPACK => plugin.import_meta_version(), ImportMetaKnownProperties::MAIN => plugin.import_meta_main(parser), ImportMetaKnownProperties::FILENAME | ImportMetaKnownProperties::DIRNAME => { diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt index f8f8c05aa34d..bf3b6ef758ba 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/esm.snap.txt @@ -68,6 +68,13 @@ function load() { const pre_declared_require_call_req = /* createRequire() */ undefined; +// ./quote'require.js + + +const quotedRequire = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/quote\'require.js'); + +const quotedUnknown = quotedRequire.unknown; + // ./index.js @@ -76,6 +83,7 @@ const pre_declared_require_call_req = /* createRequire() */ undefined; + const index_req = /* createRequire() */ undefined; const requireWithUnknownMember = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); const requireAsValue = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); @@ -110,6 +118,7 @@ it("should consume createRequire(import.meta.url) like webpack", async () => { expect(getRequire().resolve("path")).toBe("path"); expect(getNestedRequire().resolve("path")).toBe("path"); expect(load()).toBe("loader"); + expect(quotedUnknown).toBe(undefined); }); export { index_value as value, loader, requireAsValueType, unknownMember }; diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt index 2323847cc32a..6e71b33910e2 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/__snapshots__/runtimeModeSnapshot/esm.snap.txt @@ -68,6 +68,13 @@ function load() { const pre_declared_require_call_req = /* createRequire() */ undefined; +// ./quote'require.js + + +const quotedRequire = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/quote\'require.js'); + +const quotedUnknown = quotedRequire.unknown; + // ./index.js @@ -76,6 +83,7 @@ const pre_declared_require_call_req = /* createRequire() */ undefined; + const index_req = /* createRequire() */ undefined; const requireWithUnknownMember = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); const requireAsValue = (0,external_node_module_namespaceObject.createRequire)('/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js'); @@ -110,6 +118,7 @@ it("should consume createRequire(import.meta.url) like webpack", async () => { expect(getRequire().resolve("path")).toBe("path"); expect(getNestedRequire().resolve("path")).toBe("path"); expect(load()).toBe("loader"); + expect(quotedUnknown).toBe(undefined); }); export { index_value as value, loader, requireAsValueType, unknownMember }; diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js index a6e04a0521d4..77dfa42b6744 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/index.js @@ -7,6 +7,7 @@ import { getRequire } from "./pre-declared-require.js"; import { load } from "./pre-declared-require-call.js"; +import { quotedUnknown } from "./quote'require.js"; const req = createRequire(import.meta.url); const requireWithUnknownMember = createRequire(import.meta.url); @@ -42,4 +43,5 @@ it("should consume createRequire(import.meta.url) like webpack", async () => { expect(getRequire().resolve("path")).toBe("path"); expect(getNestedRequire().resolve("path")).toBe("path"); expect(load()).toBe("loader"); + expect(quotedUnknown).toBe(undefined); }); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/quote'require.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/quote'require.js new file mode 100644 index 000000000000..3a7efb2d5726 --- /dev/null +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-resolve/quote'require.js @@ -0,0 +1,5 @@ +import { createRequire } from "node:module"; + +const quotedRequire = createRequire(import.meta.url); + +export const quotedUnknown = quotedRequire.unknown; From 22c3d69109398bcbf8cdf057c1d7f12f69939dd3 Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Fri, 17 Jul 2026 03:22:50 +0800 Subject: [PATCH 3/5] fix: satisfy clippy for import meta url escaping --- .../src/parser_plugin/import_meta_plugin.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs index 222a179fa40e..d4ca08295fa1 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs @@ -1,4 +1,5 @@ use concat_string::concat_string; +use cow_utils::CowUtils; use itertools::Itertools; use rspack_core::{ ArcComputed, ConstDependency, ContextDependency, ContextMode, ContextOptions, DependencyCategory, @@ -41,7 +42,7 @@ use crate::{ fn single_quoted_string(value: &str) -> String { let json = json_stringify_str(value); - let escaped = json[1..json.len() - 1].replace('\'', "\\'"); + let escaped = json[1..json.len() - 1].cow_replace('\'', "\\'"); concat_string!("'", escaped, "'") } From f7e8363f7360bacd2296318e682020dbe27a833a Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Fri, 17 Jul 2026 15:31:02 +0800 Subject: [PATCH 4/5] fix: preserve parser context for deferred createRequire arguments --- .../common_js_imports_parse_plugin.rs | 22 +++++++++++++++++-- .../src/visitors/dependency/mod.rs | 1 + .../src/visitors/mod.rs | 1 + .../__snapshots__/esm.snap.txt | 17 ++++++++++++++ .../import-meta-url-define/index.js | 7 ++++++ .../import-meta-url-define/rspack.config.js | 17 ++++++++++++++ 6 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt create mode 100644 tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/index.js create mode 100644 tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs index 40dddeca3627..a89ce0dbdc4c 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs @@ -33,8 +33,8 @@ use crate::{ magic_comment::try_extract_magic_comment, utils::eval::{self, BasicEvaluatedExpression}, visitors::{ - CallHooksName, ExportedVariableInfo, JavascriptParser, TagInfoData, VariableDeclaration, - VariableDeclarationKind, VariableInfo, VariableInfoFlags, context_reg_exp, + CallHooksName, ExportedVariableInfo, JavascriptParser, StatementPath, TagInfoData, + VariableDeclaration, VariableDeclarationKind, VariableInfo, VariableInfoFlags, context_reg_exp, create_context_dependency, create_traceable_error, expr_name, get_non_optional_part, }, }; @@ -71,6 +71,8 @@ struct PendingCreatedRequire<'a> { callee: DeferredCreateRequireCallee, // Deferred calls skip this expression until their keep/strip state is known. argument: Expr<'a>, + statement_path: Vec, + prev_statement: Option, } struct DeferredCreateRequireCallee { @@ -89,6 +91,8 @@ impl<'a> CreatedRequireReferencesState<'a> { call_span: Span, callee: DeferredCreateRequireCallee, argument: Expr<'a>, + statement_path: Vec, + prev_statement: Option, ) { // Normal walk refreshes provisional pre-walk data after earlier references may mark it. let must_keep = self @@ -101,6 +105,8 @@ impl<'a> CreatedRequireReferencesState<'a> { must_keep, callee, argument, + statement_path, + prev_statement, }, ); } @@ -1076,7 +1082,11 @@ fn keep_deferred_create_require_call<'a>( ) { add_deferred_create_require_callee_dependency(parser, pending.callee); // Let the regular parser plugins own children of a call that survives. + let statement_path = std::mem::replace(&mut parser.statement_path, pending.statement_path); + let prev_statement = std::mem::replace(&mut parser.prev_statement, pending.prev_statement); parser.walk_expression(&pending.argument); + parser.statement_path = statement_path; + parser.prev_statement = prev_statement; } fn pre_tag_created_require_declarator<'a>( @@ -1127,10 +1137,14 @@ fn pre_tag_created_require_declarator<'a>( preserve_unhandled: true, }), ); + let statement_path = parser.statement_path.clone(); + let prev_statement = parser.prev_statement; parser.created_require_references.add_pending( call.span, deferred_callee, call.args[0].expr.clone_in(parser.ast.allocator), + statement_path, + prev_statement, ); } @@ -1164,10 +1178,14 @@ fn tag_created_require_declarator<'a>( }), ); if let Some(callee) = deferred_callee { + let statement_path = parser.statement_path.clone(); + let prev_statement = parser.prev_statement; parser.created_require_references.add_pending( call_span, callee, args[0].expr.clone_in(parser.ast.allocator), + statement_path, + prev_statement, ); } else if clear_call { clear_create_require_call(parser, call_span); diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs index 197c9a2af3d2..54319084012a 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs @@ -13,6 +13,7 @@ use swc_experimental_allocator::Allocator; use swc_experimental_ecma_ast::{Comments, Program}; use swc_experimental_ecma_semantic::resolver::Semantic; +pub(crate) use self::parser::StatementPath; pub use self::{ context_dependency_helper::{ContextModuleScanResult, create_context_dependency}, parser::{ diff --git a/crates/rspack_plugin_javascript/src/visitors/mod.rs b/crates/rspack_plugin_javascript/src/visitors/mod.rs index 050496099fcb..e779d06a2b15 100644 --- a/crates/rspack_plugin_javascript/src/visitors/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/mod.rs @@ -2,4 +2,5 @@ mod dependency; pub mod scope_info; pub mod semicolon; +pub(crate) use self::dependency::StatementPath; pub use self::{dependency::*, scope_info::*}; diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt new file mode 100644 index 000000000000..ba735efebc71 --- /dev/null +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt @@ -0,0 +1,17 @@ +```mjs title=main.mjs +import { createRequire as __rspack_createRequire } from "node:module"; +const __rspack_createRequire_require = __rspack_createRequire(import.meta.url); +// node:module +const external_node_module_namespaceObject = __rspack_createRequire_require("node:module"); +// ./index.js + + +const req = (0,external_node_module_namespaceObject.createRequire)("file:///virtual/index.js"); + +it("should delegate import.meta.url to DefinePlugin when createRequire is preserved", () => { + expect(req.resolve("path")).toBe("path"); +}); + +export {}; + +``` \ No newline at end of file diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/index.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/index.js new file mode 100644 index 000000000000..7fb3048b3480 --- /dev/null +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/index.js @@ -0,0 +1,7 @@ +import { createRequire } from "node:module"; + +const req = createRequire(import.meta.url); + +it("should delegate import.meta.url to DefinePlugin when createRequire is preserved", () => { + expect(req.resolve("path")).toBe("path"); +}); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js new file mode 100644 index 000000000000..6cac9b70f502 --- /dev/null +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js @@ -0,0 +1,17 @@ +const { DefinePlugin } = require('@rspack/core'); + +module.exports = { + module: { + parser: { + javascript: { + createRequire: true, + requireResolve: false, + }, + }, + }, + plugins: [ + new DefinePlugin({ + 'import.meta.url': JSON.stringify('file:///virtual/index.js'), + }), + ], +}; From f6879c97d31d1630c7f312025cedc96c0b36880c Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Fri, 17 Jul 2026 16:06:27 +0800 Subject: [PATCH 5/5] test: make createRequire define case cross-platform --- .../import-meta-url-define/__snapshots__/esm.snap.txt | 2 +- .../create-require/import-meta-url-define/rspack.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt index ba735efebc71..b80f487ec9ef 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/__snapshots__/esm.snap.txt @@ -6,7 +6,7 @@ const external_node_module_namespaceObject = __rspack_createRequire_require("nod // ./index.js -const req = (0,external_node_module_namespaceObject.createRequire)("file:///virtual/index.js"); +const req = (0,external_node_module_namespaceObject.createRequire)("file:////c/virtual/index.js"); it("should delegate import.meta.url to DefinePlugin when createRequire is preserved", () => { expect(req.resolve("path")).toBe("path"); diff --git a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js index 6cac9b70f502..95654d9af3d1 100644 --- a/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js +++ b/tests/rspack-test/esmOutputCases/create-require/import-meta-url-define/rspack.config.js @@ -11,7 +11,7 @@ module.exports = { }, plugins: [ new DefinePlugin({ - 'import.meta.url': JSON.stringify('file:///virtual/index.js'), + 'import.meta.url': JSON.stringify('file:///C:/virtual/index.js'), }), ], };