Skip to content

fix(esm-library): handle CSS modules in preserveModules#13670

Open
JSerFeng wants to merge 1 commit intomainfrom
fy/friendly-volhard
Open

fix(esm-library): handle CSS modules in preserveModules#13670
JSerFeng wants to merge 1 commit intomainfrom
fy/friendly-volhard

Conversation

@JSerFeng
Copy link
Copy Markdown
Contributor

@JSerFeng JSerFeng commented Apr 9, 2026

Summary

  • Fix output.library.preserveModules panic when CSS modules are involved — both native CSS (experiments.css: true) and CssExtractRspackPlugin. The build previously died with "chunk xxx should have at least one file" or "Multiple assets emit different content to the same filename".
  • Classify modules in preserve_modules by source_types, preserve the .css extension and source path for CSS modules, and set css_filename_template instead of the JS filename_template.
  • Skip CSS-only modules/chunks in the ESM render phase so it no longer emits __webpack_require__('style.css') calls or import \"__RSPACK_ESM_CHUNK_<id>\" placeholders that point at chunks with no JS file.
  • Make CssExtractRspackPlugin honor chunk.css_filename_template, so preserveModules' per-chunk CSS filename overrides actually take effect.

Test plan

  • New case `tests/rspack-test/esmOutputCases/preserve-modules/css` — native CSS with `experiments.css: true` and `exportsOnly: false` (forces a real `.css` asset). Emits `style.css` + `components/button/style.css` alongside `index.mjs`.
  • New case `tests/rspack-test/esmOutputCases/preserve-modules/css-extract` — same layout via `CssExtractRspackPlugin`; verifies the chunk-filename override path.
  • All 199 `EsmOutput` tests pass (`pnpm rstest run --include "**/EsmOutput.test.js" --project base`).
  • Ran `Config.part*.test.js` (1685 tests) and `Normal*.test.js` (1883 tests) — all green.

Copilot AI review requested due to automatic review settings April 9, 2026 10:16
@github-actions github-actions bot added team The issue/pr is created by the member of Rspack. release: bug fix release: bug related release(mr only) labels Apr 9, 2026
Copy link
Copy Markdown

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

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: 73feb3d522

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes output.library.preserveModules behavior when CSS-only modules/chunks are present, preventing ESM library rendering from emitting JS imports/requires for chunks that have no JavaScript asset and enabling per-chunk CSS filename overrides to take effect (including with CssExtractRspackPlugin).

Changes:

  • Classify preserveModules splitting by module source_types, preserving .css extension and applying chunk.css_filename_template for CSS output.
  • Skip CSS-only modules/chunks during ESM render so JS output no longer references CSS-only chunks as JS imports/requires.
  • Make CssExtractRspackPlugin respect chunk.css_filename_template and add new ESM output test fixtures for native CSS + extract-css.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
crates/rspack_plugin_extract_css/src/plugin.rs Honors per-chunk CSS filename overrides (chunk.css_filename_template) when emitting extracted CSS assets.
crates/rspack_plugin_esm_library/src/render.rs Skips emitting JS requires/imports for CSS-only modules and chunks in ESM rendering.
crates/rspack_plugin_esm_library/src/preserve_modules.rs Splits modules by kind (JS vs CSS), preserves .css names, and sets css_filename_template for CSS chunks.
crates/rspack_core/src/chunk.rs Adds set_css_filename_template setter to support per-chunk CSS naming updates.
tests/rspack-test/esmOutputCases/preserve-modules/css/test.config.js New esm-output case config for native CSS + preserveModules.
tests/rspack-test/esmOutputCases/preserve-modules/css/rspack.config.js Enables experiments.css and preserveModules for native CSS fixture.
tests/rspack-test/esmOutputCases/preserve-modules/css/src/index.js Imports CSS and a nested JS module for the native CSS fixture.
tests/rspack-test/esmOutputCases/preserve-modules/css/src/style.css Native CSS fixture stylesheet.
tests/rspack-test/esmOutputCases/preserve-modules/css/src/components/button/index.js Nested JS module used by the native CSS fixture.
tests/rspack-test/esmOutputCases/preserve-modules/css/src/components/button/style.css Nested native CSS fixture stylesheet.
tests/rspack-test/esmOutputCases/preserve-modules/css/snapshots/esm.snap.txt Snapshot for native CSS preserveModules fixture.
tests/rspack-test/esmOutputCases/preserve-modules/css-extract/test.config.js New esm-output case config for extract-css + preserveModules.
tests/rspack-test/esmOutputCases/preserve-modules/css-extract/rspack.config.js Configures CssExtractRspackPlugin and preserveModules for extract-css fixture.
tests/rspack-test/esmOutputCases/preserve-modules/css-extract/src/index.js Imports CSS and a nested JS module for the extract-css fixture.
tests/rspack-test/esmOutputCases/preserve-modules/css-extract/src/style.css Extract-css fixture stylesheet.
tests/rspack-test/esmOutputCases/preserve-modules/css-extract/src/components/button/index.js Nested JS module used by the extract-css fixture.
tests/rspack-test/esmOutputCases/preserve-modules/css-extract/src/components/button/style.css Nested extract-css fixture stylesheet.
tests/rspack-test/esmOutputCases/preserve-modules/css-extract/snapshots/esm.snap.txt Snapshot for extract-css preserveModules fixture.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 9, 2026

Merging this PR will degrade performance by 1.17%

❌ 1 regressed benchmark
✅ 27 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation rust@persistent_cache_restore_after_single_file_change@basic-react-development 27.6 ms 27.9 ms -1.17%

Comparing fy/friendly-volhard (b3d5c82) with main (c0afc25)

Open in CodSpeed

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Rsdoctor Bundle Diff Analysis

Found 6 projects in monorepo, 0 projects with changes.

📊 Quick Summary
Project Total Size Change
popular-libs 1.7 MB 0
react-10k 5.7 MB 0
react-1k 826.2 KB 0
rome 984.1 KB 0
react-5k 2.7 MB 0
ui-components 5.0 MB 0

Generated by Rsdoctor GitHub Action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

📦 Binary Size-limit

Comparing b3d5c82 to feat: implement compilation.hooks.shouldRecord for NoEmitOnErrorsPlugin (#13630) by pshu

🎉 Size decreased by 4.97KB from 49.39MB to 49.38MB (⬇️0.01%)

@JSerFeng JSerFeng force-pushed the fy/friendly-volhard branch 3 times, most recently from a2e272a to c085726 Compare April 10, 2026 07:53
@JSerFeng
Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ 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 force-pushed the fy/friendly-volhard branch 2 times, most recently from 49640dc to 5f69f8b Compare April 10, 2026 08:49
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying rspack with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2df2b07
Status: ✅  Deploy successful!
Preview URL: https://47724d76.rspack-v2.pages.dev
Branch Preview URL: https://fy-friendly-volhard.rspack-v2.pages.dev

View logs

@JSerFeng
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

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

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: 2df2b07c2e

ℹ️ 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 +151 to +155
let base_name: String = if let Some(extension) = file_path.extension() {
let suffix = format!(".{}", extension.to_string_lossy());
file_path_str
.strip_suffix(&suffix)
.unwrap_or(&file_path_str)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve unique chunk names across file extensions

Stripping the source extension unconditionally makes foo.js and foo.css both resolve to the same base_name (foo) under preserveModules. That introduces duplicate chunk names/ids for different chunks, and downstream code assumes ids are unique (NamedChunkIdsPlugin uses chunk.name() directly and after_code_generation stores id -> ukey with overwrite). In projects that colocate files like index.js + index.css, JS placeholders can resolve to the CSS-only chunk and later fail in process_assets with "chunk <id> should have at least one file".

Useful? React with 👍 / 👎.

Comment on lines +147 to +150
let file_path_str = file_path
.to_slash()
.expect("relative path should be valid UTF-8")
.into_owned();
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 panicking on non-UTF8 module paths

to_slash().expect("relative path should be valid UTF-8") introduces a hard panic when a preserved module path contains non-UTF8 bytes (valid on Unix filesystems). The previous implementation used lossy conversion and continued; with this change, compilation can abort unexpectedly for such repositories instead of returning a normal diagnostic.

Useful? React with 👍 / 👎.

@JSerFeng JSerFeng force-pushed the fy/friendly-volhard branch from 2df2b07 to 35fbb90 Compare April 13, 2026 03:40
@JSerFeng JSerFeng requested a review from LingyuCoder as a code owner April 13, 2026 03:40
@JSerFeng JSerFeng force-pushed the fy/friendly-volhard branch from 35fbb90 to 885ce36 Compare April 13, 2026 11:19
CSS modules (native `experiments.css` or mini-css-extract) under
`output.library.preserveModules` caused either "chunk xxx should have at
least one file" or "Multiple assets emit different content to the same
filename". preserveModules set the JS filename_template on CSS chunks,
left chunks nameless so multiple CSS files collapsed onto `.css`, and
the ESM render phase emitted `import "__RSPACK_ESM_CHUNK_<id>"`
placeholders pointing at CSS-only chunks with no JS file.

Classify modules in preserve_modules by source_types and set
css_filename_template for CSS modules (preserving the `.css` extension
and source path), skip CSS-only modules/chunks in the ESM render paths
that emit JS-side requires / cross-chunk imports, and make
CssExtractRspackPlugin honor chunk.css_filename_template so preserveModules
can override its per-chunk filename.
@JSerFeng JSerFeng force-pushed the fy/friendly-volhard branch from 885ce36 to b3d5c82 Compare April 14, 2026 03:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release: bug fix release: bug related release(mr only) team The issue/pr is created by the member of Rspack.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants