Latest Results
fix(es/minifier): Preserve top-level declarations referenced only by direct eval (#12029)
## Description
The DCE tree-shaker protects declarations reachable by a direct `eval`
only within function scopes (added in #4693). `found_direct_eval` is
propagated to the root scope, but `visit_mut_module` /
`visit_mut_script` never consumed it, so top-level declarations
referenced only through a direct `eval` were dropped — the `eval` then
observes a missing binding at runtime.
This consumes `found_direct_eval` at the root: when a direct `eval`
reaches the root scope, the root-scope declarations are pinned so DCE
cannot remove them. "Root-scope" means the module/script's own bindings
— immediate top-level declarations (var/fn/class + imports) plus `var`s
hoisted out of nested top-level statements — collected **without**
descending into nested function scopes, so a top-level `eval` doesn't
pin bindings it can't resolve. Each is pinned both as a graph entry (so
`subtract_cycles` never subtracts it across Repeat passes — this covers
reachable class cycles) and marked used (this covers lone bindings that
have no in-graph references).
Localized to the two root entry points; the added work only runs when a
direct `eval` reaches the root scope, so it should be
performance-neutral.
Regression tests added to `simplify_dce` (each fails without the fix): a
top-level `var`, a top-level class cycle, a top-level binding referenced
by a nested `eval`, and a `var` hoisted out of a top-level block.
Scope note: an `eval` inside an unreachable (dead) nested function still
pins the enclosing top-level declarations, matching the existing
function-scope behavior since #4693, which likewise doesn't gate on
reachability. This only ever over-preserves, which is safe.
## Related issue
Fixes #12028 Latest Branches
0%
hamidrezahanafi:perf/minifier-batch-substitutions +1%
refactor/ast-object-get-set 0%
© 2026 CodSpeed Technology