fix: preserve createRequire().resolve with literal import.meta.url when requireResolve is disabled#14783
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts Rspack’s JavaScript parser/plugin handling of module.createRequire(...) so that when module.parser.javascript.requireResolve === false, a created require’s .resolve(...) (and other “real require object” uses) are preserved for runtime execution instead of being rewritten to a bundled module id, while created requires that are only invoked as r("./x") can still be cleared to undefined to avoid leaking import.meta.url into CommonJS output.
Changes:
- Add deferred “keep vs clear” tracking for
const r = createRequire(import.meta.url)and mark declarations as “must keep” when value/member usages require a real Node require at runtime. - Extend parser walking/scope tracking (deferred function depth + immediate execution) and add new
RequireHeaderDependencymodes to preserve argument side effects / TDZ-like behavior in tricky inline forms. - Add/adjust extensive esmOutputCases + configCases fixtures and snapshots to validate preserved
.resolve(...), unknown member access, value escape, helper name collisions, and CommonJS warning behavior.
Reviewed changes
Copilot reviewed 69 out of 69 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-value-escape.js | New ESM fixture covering created-require value escape when requireResolve is disabled. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-unknown-member.js | New ESM fixture covering unknown member access preservation path. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-sequence-callee.js | New ESM fixture covering detached/sequence callee form. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-resolve.js | New ESM fixture validating runtime .resolve(...) preservation. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-resolve-paths.js | New ESM fixture validating nested .resolve.paths(...) behavior. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-resolve-options.js | New ESM fixture validating 2-arg require.resolve(request, options) form. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-extra-create-require-args.js | New ESM fixture validating extra createRequire arg side effects are preserved. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-conditional-copy.js | New ESM fixture validating conditional value-copy/aliasing cases. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/with-cache.js | New ESM fixture validating r.cache maps to bundler module cache. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/rspack.config.js | New test config enabling createRequire and disabling requireResolve. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/only-require.js | New ESM fixture validating invoke-only case can be cleared. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/mixed-parsed-and-preserved.js | New ESM fixture mixing bundled invokes + preserved member access. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/inline-resolve.js | New ESM fixture validating inline createRequire(...).resolve(...) form. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/index.js | New ESM test entry asserting expected runtime behaviors. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/dep.js | Test dependency module for bundling assertions. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/snapshots/runtimeModeSnapshot/esm.snap.txt | Snapshot validating runtime-mode emission for disabled requireResolve case. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-require-resolve-disabled/snapshots/esm.snap.txt | Snapshot validating standard-mode emission for disabled requireResolve case. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-value-escape.js | New/updated ESM fixture validating value escape in default mode. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-use-before-declaration.js | New/updated ESM fixture exercising pre-initialization and deferred execution paths. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-unknown-member.js | New/updated ESM fixture covering unknown member + optional chaining behavior. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-resolve.js | New/updated ESM fixture covering default .resolve handling. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-non-dominating-mutation.js | New/updated ESM fixture covering mutation/alias uncertainty paths. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-non-deferred-value-escape.js | New/updated ESM fixture covering non-deferred argument forms (e.g. new URL(...)). |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-nested-extra-args.js | New/updated ESM fixture covering nested extra-arg evaluation. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-inline-value-escape.js | New/updated ESM fixture covering inline value escape. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-ignored-uses.js | New/updated ESM fixture covering webpackIgnore interactions. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/with-conditional-callee.js | New/updated ESM fixture covering conditional callee behavior. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/rspack.config.js | New test config enabling createRequire for default-mode suite. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/only-require.js | New/updated ESM fixture for invoke-only behavior in default mode. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/mixed-parsed-and-preserved.js | New/updated ESM fixture mixing handled and unhandled uses. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/index.js | New/updated ESM test entry asserting behaviors across modules. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/dep.js | Test dependency module for default suite. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/snapshots/runtimeModeSnapshot/esm.snap.txt | Snapshot updates for runtime-mode output in default suite. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-default/snapshots/esm.snap.txt | Snapshot updates for standard output in default suite. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/with-value-escape.js | New fixture covering exported created-require in CommonJS library output. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/with-unknown-member.js | New fixture covering unknown member handling in CommonJS library output. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/with-resolve.js | New fixture covering .resolve in CommonJS library output. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/rspack.config.js | Config for CommonJS library output case. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/only-require.js | Invoke-only fixture in CommonJS library output case. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/index.js | Assertions for CommonJS library output behavior. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/dep.js | Test dependency module for CommonJS library case. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/snapshots/runtimeModeSnapshot/esm.snap.txt | Snapshot validating runtime-mode CommonJS library output. |
| tests/rspack-test/esmOutputCases/create-require/multi-module-commonjs-library/snapshots/esm.snap.txt | Snapshot validating standard CommonJS library output. |
| tests/rspack-test/esmOutputCases/create-require/helper-name-collision/rspack.config.js | Config enabling disabled requireResolve to validate helper symbol deconfliction. |
| tests/rspack-test/esmOutputCases/create-require/helper-name-collision/index.js | Fixture ensuring preserved createRequire calls don’t collide with helper symbols. |
| tests/rspack-test/esmOutputCases/create-require/helper-name-collision/dep.js | Test dependency module for collision case. |
| tests/rspack-test/esmOutputCases/create-require/helper-name-collision/snapshots/runtimeModeSnapshot/esm.snap.txt | Snapshot validating runtime-mode helper-name collision output. |
| tests/rspack-test/esmOutputCases/create-require/helper-name-collision/snapshots/esm.snap.txt | Snapshot validating standard helper-name collision output. |
| tests/rspack-test/esmOutputCases/create-require/exported-require-default/rspack.config.js | Config for exported created-require default behavior suite. |
| tests/rspack-test/esmOutputCases/create-require/exported-require-default/index.js | Test asserting exported created require remains usable (including .resolve). |
| tests/rspack-test/esmOutputCases/create-require/exported-require-default/created-require.js | Fixture exporting created require through several alias/copy patterns. |
| tests/rspack-test/esmOutputCases/create-require/exported-require-default/snapshots/esm.snap.txt | Snapshot validating emitted output for exported created-require case. |
| tests/rspack-test/esmOutputCases/create-require/custom-source-preserved/shim.js | Custom createRequire source shim used for preservation behavior. |
| tests/rspack-test/esmOutputCases/create-require/custom-source-preserved/rspack.config.js | Config selecting a custom createRequire source symbol. |
| tests/rspack-test/esmOutputCases/create-require/custom-source-preserved/index.js | Fixture validating unhandled members on a custom-source created require are preserved. |
| tests/rspack-test/esmOutputCases/create-require/custom-source-preserved/snapshots/esm.snap.txt | Snapshot validating output for custom-source preservation case. |
| tests/rspack-test/esmOutputCases/create-require/commonjs-require-resolve-disabled/rspack.config.js | Config for CommonJS output with requireResolve disabled. |
| tests/rspack-test/esmOutputCases/create-require/commonjs-require-resolve-disabled/index.js | Fixture asserting runtime .resolve(...) and extra-arg effects in CommonJS output. |
| tests/rspack-test/esmOutputCases/create-require/commonjs-require-resolve-disabled/snapshots/runtimeModeSnapshot/esm.snap.txt | Snapshot validating runtime-mode output for CommonJS requireResolve-disabled case. |
| tests/rspack-test/esmOutputCases/create-require/commonjs-require-resolve-disabled/snapshots/esm.snap.txt | Snapshot validating standard output for CommonJS requireResolve-disabled case. |
| tests/rspack-test/configCases/require/module-require/warnings.js | Update expected warnings to reflect new preservation behavior (fewer warnings). |
| tests/rspack-test/configCases/require/module-require/index.js | Update assertions to reflect preserved/unhandled member behavior. |
| tests/rspack-test/configCases/require/module-require-escape/warnings.js | Remove warnings expectation (case should now compile without warnings). |
| crates/rspack_plugin_javascript/src/visitors/dependency/parser/walk.rs | Extend AST walking with deferred-scope and immediate-execution tracking for createRequire logic. |
| crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs | Add parser state for created-require reference tracking + execution-context flags. |
| crates/rspack_plugin_javascript/src/parser_plugin/mod.rs | Re-export new created-require tracking state/helpers from the parser plugin module. |
| crates/rspack_plugin_javascript/src/parser_plugin/esm_export_dependency_parser_plugin.rs | Record exported locals so deferred created requires are kept when exported. |
| crates/rspack_plugin_javascript/src/parser_plugin/common_js_imports_parse_plugin.rs | Implement deferred keep/clear decision, preserve .resolve when disabled, and new inline/TDZ handling. |
| crates/rspack_plugin_javascript/src/dependency/commonjs/require_header_dependency.rs | Add new dependency modes to preserve side effects / TDZ behavior when rewriting require headers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
📦 Binary Size-limit
❌ Size increased by 24.00KB from 66.55MB to 66.58MB (⬆️0.04%) |
Merging this PR will improve performance by 2.32%
Performance Changes
Tip Curious why this is faster? Comment Comparing Footnotes
|
7242eeb to
25a40b0
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 25a40b0125
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e6152c887a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e6152c887a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if parser.javascript_options.is_create_require_enabled() { | ||
| pre_tag_created_require_declarator(parser, declarator, declaration); | ||
| } |
There was a problem hiding this comment.
Avoid pre-tagging lexical createRequire bindings
When the declaration is const req = createRequire(import.meta.url) and an earlier statement in the same block reads or calls req, this pre-walk hook tags the lexical binding before execution order reaches the declaration. Because block pre-walk runs over declarations before normal statement walking, cases like req("./a"); const req = createRequire(import.meta.url); are now handled as a created require and bundled/cleared instead of preserving the required TDZ ReferenceError; the existing no-hoist coverage uses new URL(...), so it does not exercise this new literal-import.meta.url pre-tag path.
Useful? React with 👍 / 👎.
Summary
When
module.parser.javascript.requireResolveisfalse, a created require's.resolve(...)is preserved to run at runtime instead of being rewritten to a module id.The created require is treated as a real require object whenever it is used as one — decided per declaration:
.resolve(...)(incl. the 2-argrequire.resolve(request, options)form), any other member (r.main,r.resolve.paths(...), ...), or used as a bare value (helper(r),export { r },return r, ...) → the declaration is kept and rendered via rspack's injected__rspack_createRequirehelper, and those accesses hit the real require. (webpack instead rewrites value-use to a context-require + "Critical dependency" warning, and non-cache/resolvemembers toundefined+ "unsupported" warning — this is the improvement.)r("./x")is still bundled (__webpack_require__), andr.cachestill maps to the bundler module cache./* createRequire() */ undefined— no deadimport.meta.urlleaks and CommonJS output stays valid.The keep/clear decision is deferred to the parser's
finish()(the callee is left un-walked so a clear doesn't collide with an import-specifier rewrite; the kept case re-renders the callee as__rspack_createRequire). The kept literalimport.meta.urlhonors a customizedoutput.importMetaName.Since literal
import.meta.urlis only valid in ESM output, anUnsupported featurewarning is emitted when a keptcreateRequire(import.meta.url)would land in a CommonJS bundle.Tests
esmOutputCases/create-require/import-meta-url-resolve-disabled— ESM keep via helper, variable + inline forms, 2-arg resolve, extra-arg side effect; snapshot.configCases/require/create-require-kept-for-access— value-position use + non-resolve member access keep the real created require (no context-require/unsupported rewrite, no warning); invoke still bundled.configCases/require/create-require-no-resolve-no-leak— used only as invokes → cleared toundefined, valid CJS, no warning.configCases/require/create-require-resolve-disabled-cjs— CommonJS warning + artifact.configCases/require/create-require-resolve-custom-import-meta-name— kept argument honorsoutput.importMetaName.configCases/require/module-require-disable-resolve— dynamic-argument dependency preservation.Related links
Checklist