Skip to content

fix: preserve createRequire().resolve with literal import.meta.url when requireResolve is disabled#14783

Merged
JSerFeng merged 3 commits into
mainfrom
codex/create-require-follow-up
Jul 15, 2026
Merged

fix: preserve createRequire().resolve with literal import.meta.url when requireResolve is disabled#14783
JSerFeng merged 3 commits into
mainfrom
codex/create-require-follow-up

Conversation

@JSerFeng

Copy link
Copy Markdown
Contributor

Summary

When module.parser.javascript.requireResolve is false, 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-arg require.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_createRequire helper, and those accesses hit the real require. (webpack instead rewrites value-use to a context-require + "Critical dependency" warning, and non-cache/resolve members to undefined + "unsupported" warning — this is the improvement.)
  • A plain invoke r("./x") is still bundled (__webpack_require__), and r.cache still maps to the bundler module cache.
  • So a created require used only as bundled invokes (or unused) is cleared to /* createRequire() */ undefined — no dead import.meta.url leaks 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 literal import.meta.url honors a customized output.importMetaName.

Since literal import.meta.url is only valid in ESM output, an Unsupported feature warning is emitted when a kept createRequire(import.meta.url) would land in a CommonJS bundle.

const r = createRequire(import.meta.url);
r("./x");               // bundled (__webpack_require__)
r.resolve("./x");       // preserved, runtime-resolved
r.resolve.paths("./x"); // real Node API (was unsupported/undefined)
export const x = r;     // real created require (was a context-require + warning)

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 to undefined, 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 honors output.importMetaName.
  • configCases/require/module-require-disable-resolve — dynamic-argument dependency preservation.

Related links

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

@JSerFeng JSerFeng marked this pull request as ready for review July 14, 2026 08:22
Copilot AI review requested due to automatic review settings July 14, 2026 08:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 RequireHeaderDependency modes 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.

Timeless0911
Timeless0911 previously approved these changes Jul 14, 2026
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

📦 Binary Size-limit

Comparing e6152c8 to fix(rsc): preserve multiple referenced client exports (#14794) by Cong-Cong Pan

❌ Size increased by 24.00KB from 66.55MB to 66.58MB (⬆️0.04%)

@codspeed-hq

codspeed-hq Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 2.32%

⚡ 1 improved benchmark
✅ 42 untouched benchmarks
⏩ 47 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation rust@create_module_hashes 8.5 ms 8.3 ms +2.32%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing codex/create-require-follow-up (e6152c8) with main (786dec5)

Open in CodSpeed

Footnotes

  1. 47 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@JSerFeng JSerFeng marked this pull request as draft July 14, 2026 08:50
@JSerFeng

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@JSerFeng

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@JSerFeng JSerFeng marked this pull request as ready for review July 15, 2026 09:34
@JSerFeng JSerFeng merged commit b8e3594 into main Jul 15, 2026
38 checks passed
@JSerFeng JSerFeng deleted the codex/create-require-follow-up branch July 15, 2026 09:34

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +1952 to +1954
if parser.javascript_options.is_create_require_enabled() {
pre_tag_created_require_declarator(parser, declarator, declaration);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants