#18191·babel

transform-block-scoping: closure created in a for-loop initializer captures the wrong binding

Author: ko-successCreated Aug 19, 2026Updated Aug 19, 2026
Labelsi: needs triage

  • Would you like to work on a fix?

How are you using Babel?

babel-loader (webpack)

Input code

REPL reproduction

javascript
for (let i = 0, f = () => i; i < 1;) {
  i = 42;
  console.log(f());
}

Configuration file name

No response

Configuration

json
{
  "plugins": ["@babel/plugin-transform-block-scoping"]
}

Current and expected behavior

Babel changes the observable runtime behavior of this program. The original JavaScript prints 0. However, @babel/plugin-transform-block-scoping transforms it into:

javascript
for (var i = 0, f = () => i; i < 1;) {
  i = 42;
  console.log(f());
}

The transformed program prints 42.

The closure f is created while evaluating the lexical for initializer and captures the initial i binding. Before executing the first iteration, the ECMAScript for semantics create a new per-iteration binding for i. Therefore, the assignment i = 42 in the loop body updates the per-iteration binding, while f must continue to observe the initial binding whose value is 0. Lowering both bindings to the same var i loses this distinction. As a result, the transform silently changes valid JavaScript semantics.

Environment

  • Babel version(s): 8.0.4; also reproduces with 7.29.1
  • Plugin: @babel/plugin-transform-block-scoping
  • Node: 24.14.0
  • npm: 11.11.0
  • OS: macOS
  • Monorepo: no

Possible solution

The loop-binding capture analysis should also account for closures created in a ForStatement initializer.

Currently, references outside the loop body appear to be discarded even when they occur inside a closure. Consequently, the reference to i in f = () => i is not treated as requiring preservation of the lexical environment.

The transform needs to preserve the initializer environment separately from the per-iteration environment when lowering the lexical declaration to var.

Additional context

No response