Reproduction link or steps
Turns out rolldown's build is non-deterministic when you have identical content under different names in your bundle tree. The minimal reproduction is just 2 identical files, each imported by its own module and then one final entry file:
src/assets/alpha.png # identical content with beta.png
src/assets/beta.png # identical content with alpha.png
src/mod_a.js # import u from './assets/alpha.png'; export default u
src/mod_b.js # import u from './assets/beta.png'; export default u
src/entry.js # import './mod_a.js'; import './mod_b.js'; console.log(...)
What is expected?
The build process should produce the same output every time when ran with the same input content.
When deduplicating assets, the file name chosen to be used should be deterministically-determined.
What is actually happening?
Here's how I believe it happens:
When two emitted assets have identical content but different names (e.g. the same logo under two filenames, imported as asset modules), rolldown deduplicates them by content hash and the surviving asset's output filename is taken from whichever copy emits first. Assets emit concurrently from parallel module processing and which one is emitted first is thus non-deterministic, especially on systems with multiple processors. The one name of the deduplicated asset chosen by this process then lands in [name] of assetFileNames, so it changes the bytes — and therefore the [hash] — of every chunk that references that duplicate asset. That's how byte-identical input produces different asset and chunk hashes per build.
System Info
rolldown 1.1.2
This happens on macOS (Apple Silicon) and Ubuntu Linux (arm64, multi-core) as well. Don't think this would be relevant but let me know if it is and I'll add more info.
The system info below is from StackBlitz:
System:
OS: Linux 5.0 undefined
CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 0 Bytes / 0 Bytes
Shell: 1.0 - /bin/jsh
Binaries:
Node: 22.22.3 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 10.8.2 - /usr/local/bin/npm
pnpm: 8.15.6 - /usr/local/bin/pnpm
npmPackages:
rolldown: 1.1.2 => 1.1.2
Any additional comments?
We were hit quite bad by this at Dext (other factors were at play as well, of course, but this non-determinism was one of the required conditions...) It would be great if we manage to resolve it.
Full disclosure: the reproduction was created with the help of Claude Code. I did try to simplify it as much as possible and massaged its output. Looks absolutely legit to me but I might have missed something.
Reproduction link or steps
node build.mjsin the terminal there will run a build 10 times and will report in case the output differs): https://stackblitz.com/github/mitio/rolldown-asset-dedup-nondeterminism-reproTurns out rolldown's build is non-deterministic when you have identical content under different names in your bundle tree. The minimal reproduction is just 2 identical files, each imported by its own module and then one final entry file:
What is expected?
The build process should produce the same output every time when ran with the same input content.
When deduplicating assets, the file name chosen to be used should be deterministically-determined.
What is actually happening?
Here's how I believe it happens:
When two emitted assets have identical content but different names (e.g. the same logo under two filenames, imported as asset modules), rolldown deduplicates them by content hash and the surviving asset's output filename is taken from whichever copy emits first. Assets emit concurrently from parallel module processing and which one is emitted first is thus non-deterministic, especially on systems with multiple processors. The one name of the deduplicated asset chosen by this process then lands in
[name]ofassetFileNames, so it changes the bytes — and therefore the[hash]— of every chunk that references that duplicate asset. That's how byte-identical input produces different asset and chunk hashes per build.System Info
rolldown 1.1.2This happens on macOS (Apple Silicon) and Ubuntu Linux (arm64, multi-core) as well. Don't think this would be relevant but let me know if it is and I'll add more info.
The system info below is from StackBlitz:
System: OS: Linux 5.0 undefined CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz Memory: 0 Bytes / 0 Bytes Shell: 1.0 - /bin/jsh Binaries: Node: 22.22.3 - /usr/local/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 10.8.2 - /usr/local/bin/npm pnpm: 8.15.6 - /usr/local/bin/pnpm npmPackages: rolldown: 1.1.2 => 1.1.2Any additional comments?
We were hit quite bad by this at Dext (other factors were at play as well, of course, but this non-determinism was one of the required conditions...) It would be great if we manage to resolve it.
Full disclosure: the reproduction was created with the help of Claude Code. I did try to simplify it as much as possible and massaged its output. Looks absolutely legit to me but I might have missed something.