Conversation
✅ Deploy Preview for rolldown-rs canceled.
|
Co-authored-by: shulaoda <[email protected]>
d6ed301 to
bd0956e
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes a critical bug where chunk filenames differing only by case (e.g., Edit.js vs edit.js) would silently overwrite each other on case-insensitive filesystems (macOS APFS, Windows NTFS), resulting in lost modules.
Changes:
- Modified
make_unique_nameto use lowercase keys for conflict detection while preserving original case in output filenames - Added unit test
case_insensitiveto verify the fix handles case variations correctly - Added integration test fixture
case_insensitive_chunk_filenameswith dynamic imports to validate end-to-end behavior
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| crates/rolldown_utils/src/make_unique_name.rs | Core fix: uses to_lowercase() for map keys to detect case-insensitive conflicts while returning original-case candidate |
| crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap | Updated snapshot showing Edit.js and edit2.js are correctly deduplicated |
| crates/rolldown/tests/rolldown/function/case_insensitive_chunk_filenames/main.js | Test entry with dynamic imports of modules differing only by case |
| crates/rolldown/tests/rolldown/function/case_insensitive_chunk_filenames/lowercase/edit.js | Lowercase variant test module |
| crates/rolldown/tests/rolldown/function/case_insensitive_chunk_filenames/_config.json | Test configuration using [name].js pattern to trigger conflict |
| crates/rolldown/tests/rolldown/function/case_insensitive_chunk_filenames/Edit.js | Capitalized test module |
Benchmarks Rust |
| match used_name_counts.entry(candidate.clone()) { | ||
| // Lowercase key for case-insensitive filesystems (macOS APFS, Windows NTFS). | ||
| // When already lowercase, reuse the `candidate` Arc directly to avoid allocation. | ||
| let lowercase_candidate = match candidate.as_str().cow_to_ascii_lowercase() { |
There was a problem hiding this comment.
cow_to_ascii_lowercase() only normalizes ASCII A-Z, so filenames that differ by non-ASCII case (e.g. "Ä.js" vs "ä.js") can still collide on case-insensitive filesystems. This also diverges from other filename-conflict logic in the repo (e.g. to_lowercase() in rolldown_common/src/file_emitter.rs). Consider using full Unicode lowercasing (e.g. to_lowercase() / a cow_to_lowercase() helper) for the map key to make conflict detection truly case-insensitive.
| let lowercase_candidate = match candidate.as_str().cow_to_ascii_lowercase() { | |
| let lowercase_candidate = match candidate.as_str().cow_to_lowercase() { |
| ], | ||
| "chunkFilenames": "[name].js" | ||
| }, | ||
| "snapshot": false, |
There was a problem hiding this comment.
This fixture disables snapshots and output execution, and there is no _test.mjs in the folder, so the regular fixture test run doesn't assert the expected behavior (e.g. that the output contains both Edit.js and edit2.js, and that main references ./edit2.js). Consider adding a small _test.mjs assertion or enabling snapshots for this fixture so the regression is directly validated.
| "snapshot": false, | |
| "snapshot": true, |
## [1.0.0-rc.6] - 2026-02-26 ### 💥 BREAKING CHANGES - css: remove `css_entry_filenames` , `css_chunk_filenames` and related code (#8402) by @hyf0 - css: drop builtin CSS bundling to explore alternative solutions (#8399) by @hyf0 ### 🚀 Features - rust/data-url: use hash as id for data url modules to prevent long string overhead (#8420) by @hyf0 - validate bundle stays within output dir (#8441) by @sapphi-red - rust: support `PluginOrder::PinPost` (#8417) by @hyf0 - support `ModuleType:Copy` (#8407) by @hyf0 - expose `ESTree` types from `rolldown/utils` (#8400) by @sapphi-red ### 🐛 Bug Fixes - incorrect sourcemap when postBanner/postFooter is used with shebang (#8459) by @Copilot - resolver: disable node_path option to align ESM resolver behavior (#8472) by @sapphi-red - parse `.js` within `"type": "commonjs"` as ESM for now (#8470) by @sapphi-red - case-insensitive filename conflict detection for chunk deduplication (#8458) by @Copilot - prevent inlining CJS exports that are mutated by importers (#8456) by @IWANABETHATGUY - parse `.cjs` / `.cts` / `.js` within `"type": "commonjs"` as CommonJS (#8455) by @sapphi-red - plugin/copy-module: correct hooks' priority (#8423) by @hyf0 - plugin/chunk-import-map: ensure `render_chunk_meta` run after users plugin (#8422) by @hyf0 - rust: correct hooks order of `DataUriPlugin` (#8418) by @hyf0 - `jsx.preserve` should also considering tsconfig json preserve (#8324) by @IWANABETHATGUY - `deferred_scan_data.rs "Should have resolved id: NotFound"` error (#8379) by @sapphi-red - cli: require value for `--dir`/`-d` and `--file`/`-o` (#8378) by @Copilot - dev: avoid mutex deadlock caused by inconsistent lock order (#8370) by @sapphi-red ### 🚜 Refactor - watch: rename TaskStart/TaskEnd to BundleStart/BundleEnd (#8463) by @hyf0 - rust: rename `rolldown_plugin_data_uri` to `rolldown_plugin_data_url` (#8421) by @hyf0 - bindingify-build-hook: extract helper for PluginContextImpl (#8438) by @ShroXd - give source loading a proper name (#8436) by @IWANABETHATGUY - ban holding DashMap refs across awaits (#8362) by @sapphi-red ### 📚 Documentation - add glob pattern usage example to input option (#8469) by @IWANABETHATGUY - remove `https://rolldown.rs` from links in reference docs (#8454) by @sapphi-red - mention execution order issue in `output.codeSplitting` docs (#8452) by @sapphi-red - clarify `output.comments` behavior a bit (#8451) by @sapphi-red - replace npmjs package links with npmx.dev (#8439) by @Boshen - reference: add `Exported from` for values / types exported from subpath exports (#8394) by @sapphi-red - add JSDocs for APIs exposed from subpath exports (#8393) by @sapphi-red - reference: generate reference pages for APIs exposed from subpath exports (#8392) by @sapphi-red - avoid pipe character in codeSplitting example to fix broken rendering (#8391) by @IWANABETHATGUY ### ⚡ Performance - avoid redundant PathBuf allocations in resolve paths (#8435) by @Brooooooklyn - bump to `sugar_path@2` (#8432) by @hyf0 - use flag-based convergence detection in include_statements (#8412) by @Brooooooklyn ### 🧪 Testing - execute `_test.mjs` even if `executeOutput` is false (#8398) by @sapphi-red - add retry to tree-shake/module-side-effects-proxy4 as it is flaky (#8397) by @sapphi-red - avoid `expect.assertions()` as it is not concurrent test friendly (#8383) by @sapphi-red - disable `mockReset` option (#8382) by @sapphi-red - fix flaky failure caused by concurrent resolveId calls (#8381) by @sapphi-red ### ⚙️ Miscellaneous Tasks - deps: update dependency rollup to v4.59.0 [security] (#8471) by @renovate[bot] - ai/design: add design doc about watch mode (#8453) by @hyf0 - deps: update oxc resolver to v11.19.0 (#8461) by @renovate[bot] - ai: introduce progressive spec-driven development pattern (#8446) by @hyf0 - deprecate output.legalComments (#8450) by @sapphi-red - deps: update dependency oxlint-tsgolint to v0.15.0 (#8448) by @renovate[bot] - ai: make CLAUDE.md a symlink of AGENTS.md (#8445) by @hyf0 - deps: update rollup submodule for tests to v4.59.0 (#8433) by @sapphi-red - deps: update test262 submodule for tests (#8434) by @sapphi-red - deps: update oxc to v0.115.0 (#8430) by @renovate[bot] - deps: update oxc apps (#8429) by @renovate[bot] - deps: update npm packages (#8426) by @renovate[bot] - deps: update rust crate owo-colors to v4.3.0 (#8428) by @renovate[bot] - deps: update github-actions (#8424) by @renovate[bot] - deps: update rust crates (#8425) by @renovate[bot] - deps: update oxc resolver to v11.18.0 (#8406) by @renovate[bot] - deps: update dependency oxlint-tsgolint to v0.14.2 (#8405) by @renovate[bot] - ban `expect.assertions` in all fixture tests (#8395) by @sapphi-red - deps: update oxc apps (#8389) by @renovate[bot] - ban `expect.assertions` in fixture tests (#8387) by @sapphi-red - enable lint for `_config.ts` files (#8386) by @sapphi-red - deps: update dependency oxlint-tsgolint to v0.14.1 (#8385) by @renovate[bot] Co-authored-by: shulaoda <[email protected]>
On case-insensitive filesystems (macOS APFS, Windows NTFS), chunk filenames differing only by case (e.g.,
Edit.jsvsedit.js) resolve to the same inode, causing silent file overwrites and lost modules.Root Cause
make_unique_nametracked used names with case-sensitive keys, soEdit.jsandedit.jswere treated as distinct — no suffix was generated, and the second write would overwrite the first on case-insensitive filesystems.Fix
crates/rolldown_utils/src/make_unique_name.rs— Store lowercase keys inused_name_countsfor conflict detection while preserving the original case in the returned filename:candidate.to_lowercase(), but the returned value retains the original-casecandidate.Tests
case_insensitiveunit test inmake_unique_name.rscase_insensitive_chunk_filenamesintegration fixture: dynamic imports ofEdit.jsandlowercase/edit.jsproduce distinct outputsEdit.jsandedit2.js, withmain.jscorrectly referencing./edit2.jsOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.