#14089·rspack

[Bug]: SwcJsMinimizerRspackPlugin produces non-deterministic variable names across builds

Author: eladoCreated May 19, 2026Updated Sep 13, 2026

System Info

  System:
    OS: Linux 5.4 Ubuntu 20.04.6 LTS (Focal Fossa)
    CPU: (32) x64 Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
    Memory: 102.32 GB / 123.81 GB
    Container: Yes
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 24.14.0
    Yarn: 4.12.0
    npm: 11.9.0
    Watchman: 4.9.0
  Browsers:
    Chrome: 147.0.7727.55
  • @rspack/core: 2.0.3
  • @rspack/binding: 2.0.3
  • @rsbuild/core: 2.0.5

Details

Building the same project twice with mode: 'production' produces different output files. The built-in SwcJsMinimizerRspackPlugin assigns different short variable names to the same code across separate build invocations (separate processes), even though the pre-minification input is byte-for-byte identical.

This breaks reproducible/hermetic builds, which is critical for build systems like Bazel that rely on content-addressable caching.

Important: We verified that @swc/core's standalone minify() API is deterministic across separate process invocations (tested with v1.15.21, 10 runs, all identical). The non-determinism only occurs through rspack's built-in SwcJsMinimizerRspackPlugin.

How the bug was found

We build a large production monolith (>3,700 JS output files) in Bazel. Building twice with no cache produces 240 entry point files (6.5%) with different content hashes. The content is identical except for minified variable names being swapped.

Evidence

Example 1 — runtime variable swap (bo)

Building the same entry point twice produces identical content for the first 9,687 bytes. Then:

// Run 1:
...getElementsByTagName("script"),s=0;s<d.length;s++){var b=d[s];if(b.getAttribute("src")==e||b.getAttribute...

// Run 2 (same source, same machine, same config):
...getElementsByTagName("script"),s=0;s<d.length;s++){var o=d[s];if(o.getAttribute("src")==e||o.getAttribute...

b and o are swapped throughout the runtime bootstrap code (35 occurrences).

Example 2 — top-level IIFE variable swap (ar, di)

// Run 1:
(()=>{"use strict";var e,t,a,r,c,n,d={379961(e,t,a){Promise.all([a.e(35249)...

// Run 2:
(()=>{"use strict";var e,t,r,a,c,n,i={379961(e,t,r){Promise.all([r.e(35249)...

a and r are swapped, and d becomes i. These are rspack's runtime variables (__webpack_modules__, __webpack_require__, etc.) after minification.

Scope: 240 out of 3,718 JS files affected. All are entry points containing the rspack runtime bootstrap. Async chunks (no runtime) are not affected.

Likely root cause

The rspack runtime code is generated deterministically (verified: first 9,687 bytes of the entry point are identical between runs). The non-determinism is introduced during the minification step by SwcJsMinimizerRspackPlugin.

Since standalone @swc/core minify() is deterministic with identical input, the issue is likely in how rspack invokes the minifier — possibly parallel chunk minification with shared mutable state, or the bundled swc_ecma_minifier in @rspack/binding being compiled with different settings than the npm @swc/core package.

Reproduce link

No response

Reproduce Steps

Build any rspack project with multiple entry points twice and compare byte-for-byte:

bash
npx rspack build && cp -r dist dist-run1
npx rspack build && cp -r dist dist-run2
diff -rq dist-run1 dist-run2

With a sufficiently large runtime (multiple chunks, script loading code), variable names in the entry point runtime bootstrap will differ.

Minimal config:

javascript
// rspack.config.js
module.exports = {
  mode: 'production',
  entry: {
    main: './src/index.js',
    other: './src/other.js',
  },
  optimization: {
    minimize: true, // uses SwcJsMinimizerRspackPlugin by default
  },
};

Note: The non-determinism may not reproduce with very small projects. It appears when the runtime has enough variables for the mangler's name pool to exhibit ordering differences. Our production build has ~3,700 output files and the runtime includes chunk loading, CSS loading, asset retry, and other plugins.

Also reproduced on @rspack/core 2.0.1. The 2.0.2 fix ("sort ConcatenatedModule export keys for deterministic builds") fixed async chunk determinism but did not fix entry point runtime variable naming.

Additional context

  • @swc/core 1.15.21 standalone minify() with { compress: true, mangle: true } is deterministic across 10 separate process invocations with a 1,200-line input — the issue is specific to rspack's usage of the minifier, not SWC itself.
  • The affected code is always in the rspack runtime bootstrap (chunk loading, script injection, CSS loading), never in user application code within async chunks.
  • Setting optimization.minimize = false eliminates the non-determinism (confirming the minifier as the source).