Skip to content

fix: render CommonJS externals directly in modern-module output - #15238

Merged
JSerFeng merged 13 commits into
mainfrom
codex/optimize-esm-commonjs-external-require
Sep 18, 2026
Merged

JSerFeng merged 13 commits into
mainfrom
codex/optimize-esm-commonjs-external-require

Conversation

@JSerFeng

Copy link
Copy Markdown
Contributor

Summary

  • Render eligible static CommonJS externals directly at their CommonJS dependency sites for library.type: "modern-module", removing the intermediate external module factory.
  • Preserve wrapper semantics for optional, conditional/context, request-rest, relative, and base-sensitive node-commonjs requests, including reused module graphs and mixed/multi-entry consumers.
  • Handle Node builtin export-requires and object-form external maps safely, while sharing the collision-safe createRequire init fragment.

For an eligible external, emitted code now keeps require("external") (or the createRequire equivalent for Node builtins) in the rendered module instead of routing through a generated external "external" factory.

Related links

Supersedes #14948.

Validation

  • corepack pnpm run build:binding:dev
  • corepack pnpm run build:js
  • corepack pnpm test EsmOutput.test.js (522 tests)
  • focused modern-module concatenation and external edge-case suites
  • cargo test -p rspack_plugin_esm_library commonjs_external::tests
  • cargo test -p rspack_core module_graph::connection::tests
  • cargo clippy -p rspack_core -p rspack_plugin_esm_library -p rspack_plugin_externals --all-targets -- -D warnings
  • cargo fmt --all --check
  • corepack pnpm run lint:js

Checklist

  • Tests updated.
  • Documentation not required (internal rendering optimization; no public API change).

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

📦 Binary Size-limit

Comparing accb090 to chore: release version 2.2.6 (#15743) by AsyncIter

❌ Size increased by 32.13KB from 68.80MB to 68.83MB (⬆️0.05%)

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Rsdoctor Bundle Diff Analysis

Found 5 projects in monorepo, 0 projects with changes.

📊 Quick Summary
Project Total Size Gzip Size Change Gzip Change
popular-libs 1.7 MB 529.8 KB 0 0
react-10k 5.4 MB 1.3 MB 0 0
react-1k 827.7 KB 226.6 KB 0 0
react-5k 2.6 MB 670.9 KB 0 0
ui-components 5.0 MB 1.4 MB 0 0

Generated by Rsdoctor GitHub Action

@codspeed

codspeed Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 50 untouched benchmarks
⏩ 47 skipped benchmarks1


Comparing codex/optimize-esm-commonjs-external-require (accb090) with main (1032815)

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.

@alfaproject

Copy link
Copy Markdown

Oh man, this has been a major pet peeve of mine, I'm glad it's being worked on. Also duplicate imports from node:path and path is another one

@JSerFeng
JSerFeng force-pushed the codex/optimize-esm-commonjs-external-require branch from 805eab3 to 85c23c5 Compare August 25, 2026 07:35
@JSerFeng
JSerFeng force-pushed the codex/optimize-esm-commonjs-external-require branch from f19acca to 8d157d5 Compare September 15, 2026 07:38
@JSerFeng

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-17T11:30:41.704660Z accb090 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 8d157d54f0

ℹ️ 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 September 15, 2026 09:32

@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: 751e35276f

ℹ️ 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 thread crates/rspack_plugin_esm_library/src/plugin.rs Outdated
@JSerFeng
JSerFeng force-pushed the codex/optimize-esm-commonjs-external-require branch from 751e352 to 83a28c0 Compare September 17, 2026 03:15
@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

let used = dup_id_used.exports().iter().filter_map(|(name, export)| {
if matches!(export.get_used(None), rspack_core::UsageState::Used) {
Some(name.clone())
} else {
None

P1 Badge Preserve partially used exports when merging externals

When two issuer-scoped module externals are merged into one chunk and a non-canonical copy uses only a nested property (for example, one issuer consumes external.a.x), its top-level export is marked OnlyPropertiesUsed, not Used. This filter therefore omits that export from used_exports; after all connections are moved to the canonical module, usage analysis is not rerun, so collect_module_external_remapping can treat the export as unused and generate a namespace without it. Merge OnlyPropertiesUsed and its nested usage state, or conservatively promote every non-Unused export to Used on the canonical module.

ℹ️ 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

let used = dup_id_used.exports().iter().filter_map(|(name, export)| {
if matches!(export.get_used(None), rspack_core::UsageState::Used) {
Some(name.clone())
} else {
None

P1 Badge Preserve partially used exports when merging externals
When two issuer-scoped module externals are merged into one chunk and a non-canonical copy uses only a nested property (for example, one issuer consumes external.a.x), its top-level export is marked OnlyPropertiesUsed, not Used. This filter therefore omits that export from used_exports; after all connections are moved to the canonical module, usage analysis is not rerun, so collect_module_external_remapping can treat the export as unused and generate a namespace without it. Merge OnlyPropertiesUsed and its nested usage state, or conservatively promote every non-Unused export to Used on the canonical module.

ℹ️ About Codex in GitHub

It's the same with current main, as external module cannot produce nested exports info

@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: b58bcf20db

ℹ️ 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 thread crates/rspack_plugin_esm_library/src/dependency/commonjs_external.rs Outdated
@JSerFeng
JSerFeng enabled auto-merge (squash) September 18, 2026 03:32
@JSerFeng
JSerFeng merged commit 475c7ad into main Sep 18, 2026
46 checks passed
@JSerFeng
JSerFeng deleted the codex/optimize-esm-commonjs-external-require branch September 18, 2026 03:37

Copy link
Copy Markdown
Member

<agent: daily-job rspack eco-ci>

Eco-CI: rspack/rslib expects the old external wrapper

Conclusion / impact: This PR exposes three downstream output-shape assertions; the focused runtime checks pass after adjusting them. Rslib repair: #1931.

Why this PR

The modern-module dependency templates now emit CommonJS external calls directly. Rslib still expects module.exports = __rspack_createRequire_require(...). The same Rslib SHA passes before this merge and fails at the merge commit; generated configs enable modern-module. This is a downstream test-contract mismatch, not evidence of a runtime regression.

Suggested fix

Rslib should assert the external callee/request and retain its runtime checks without requiring the removed wrapper. The repair passes all 10 externals tests with both the locked dependencies and this PR's official WASM artifact.

Evidence and attribution checks
  • Current run: https://github.com/rstackjs/rstack-ecosystem-ci/actions/runs/35323623709
  • Current job: https://github.com/rstackjs/rstack-ecosystem-ci/actions/runs/35323623709/job/105531676228
  • Tested Rspack: 3861cb025c648c93760fd09f0d32587dc55872d8; downstream Rslib: 57087f50a80fecfc706e1d296444432cce9eab3a.
  • Previous main green: run 35215608792 / job 105183339567; first bad: run 35304243186 / job 105473133524 at 475c7ad20f5dded5c1139e3bc820673c9a59f015. Both use that same Rslib SHA.
  • Local comparison: official WASM artifact from Rspack CI run 35303892819, core JS 2.2.6 (unchanged by this PR): original assertions 3 failed / 7 passed; repaired assertions 10 passed. Stable locked Rspack 2.2.4: 10 passed.
  • DEBUG=rsbuild fixture configs confirm output.library.type and externalsType are modern-module.
  • Latest surface test: migrate stateful and diagnostic loaders to ESM #15758 changes only Rspack tests. Interleaved migration run 35322823195 does not contain this PR and is not a flaky counterexample. The signature is absent from prior green main runs and reproduced deterministically with this PR's artifact.
  • Full latest Linux-native suite was not rerun locally. No dependency upgrades or runtime changes are proposed.

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.

4 participants