Latest Results
fix(minifier): keep reads of never-assigned uninitialized bindings (#24452)
`inline_identifier_reference` counts `undefined` among the constants that are always worth substituting into a read. But it prints as `void 0` — six characters — while a kept read mangles to one or two, and for a binding declared without an initializer (`let x;`) there is no initializer whose elimination pays the difference, so the substitution can only grow mangled output. rolldown hits this on bundled chunks (`minify: true` always mangles): the chunk in rolldown/rolldown#10174 minified to 58 bytes where keeping the binding gives 55.
`SymbolValue` now records that a tracked `undefined` is the implicit value of an initializer-less declaration, and the textual inline skips such reads. Only the byte substitution is affected: constant-driven folds (`if (x)` dead branches, `x === void 0`, `return x` elision) resolve the value through `SymbolValue` during evaluation and behave as before. An explicit `const x = undefined` initializer still inlines — there the substitution deletes the `= void 0` text along with the declaration, so it pays for itself.
The known tradeoff is compress-without-mangle: with `mangle: false` the kept binding prints its full source name, where the old behavior emitted the shorter `void 0`. The small-value thresholds in this heuristic already assume mangled read sites (numbers up to 3 digits, strings up to 3 chars), so the gate follows the same assumption; a CompressOptions hint for compress-only pipelines can be added later if anyone hits it. For comparison, esbuild never models implicit `undefined` (its const-value inlining requires a `const` with a literal initializer) and wins this shape by keeping the binding; terser substitutes like the old behavior and produces the same 58 bytes.
## Example
The rolldown chunk from the issue (`minify: false` output, then minified with mangling):
```js
let undefinedVar;
let value;
function reset() {
value = undefinedVar;
}
export { reset, value };
```
Before:
```js
let e;function t(){e=void 0}export{t as reset,e as value};
```
After:
```js
let e,t;function n(){t=e}export{n as reset,t as value};
```
Verified with the minifier unit suite (two expectation updates where the kept binding is now smaller or equal), `just minsize` and allocation snapshots unchanged, `--twice` idempotency on the repro, and old-vs-new byte-identical output on real rolldown chunks of vue, svelte, and rxjs.
Fixes rolldown/rolldown#10174.
Implemented with AI assistance (Claude Code); design, review, and verification driven by the author per the repo AI-usage policy. Latest Branches
0%
cellison-figma:feat/react-function-component-definition -3%
cellison-figma:feat/eslint-one-var 0%
armano2:fix/drop-statements © 2026 CodSpeed Technology