oxc-project
oxc
Blog
Docs
Changelog
Blog
Docs
Changelog
Overview
Branches
Benchmarks
Runs
Performance History
Latest Results
fix(transformer): correct scope of inferred named FE in async-to-generator When the async-to-generator transform wraps an async arrow / anonymous async function expression whose name is inferred from its parent (e.g. `const foo = async () => {}`, `{ foo: async () => {} }`), it emits a named function expression as the caller: ```js const foo = function () { var _ref = asyncToGenerator(function* () { /* body */ }); return function foo() { return _ref.apply(this, arguments); }; }(); ``` Per the JS spec, the inferred name `foo` binds only inside the named FE itself — not in the enclosing wrapper IIFE scope. But the previous code had two bugs that together caused oxc to emit an AST that a fresh semantic analysis would misread: 1. `create_function` set `FunctionType::FunctionDeclaration` whenever `id.is_some()`, even for callers using the result as a function expression. Any fresh semantic pass on the output trusts `r#type` and treats the node as a declaration — which binds the name in the enclosing wrapper scope. 2. The inferred id's binding was registered in `wrapper_scope_id` rather than the function's own scope. Combined, they cause downstream tools (e.g. Rolldown's minifier in Vite) to resolve a body reference like `typeNext()` inside `const typeNext = async () => { typeNext(); }` to the inner caller instead of the outer `const typeNext`. The outer const then looks unused, gets inlined away, and the body reference fails at runtime with a `ReferenceError`. Fix: pass `FunctionType` explicitly (via new `create_function_expression` and `create_function_declaration` helpers), and place the inferred id's binding in the caller function's own scope. Closes #21125 Closes #21445 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix/async-to-generator-inferred-name-scope
29 minutes ago
fix(ecmascript): treat update expressions as unconditionally side-effectful `obj.prop++` performs an implicit GetValue + ToNumeric + PutValue. The ToNumeric coercion alone can invoke `valueOf`/`Symbol.toPrimitive` on the old value, and the read/write may trigger getters, setters, or Proxy traps. Previously `UpdateExpression::may_have_side_effects` only checked `property_write_side_effects()` and treated the update as free when disabled — letting the minifier drop `counter.value++` even when `counter` is externally observable. Terser, esbuild, Rollup, and SWC all hardcode `++`/`--` as unconditionally side-effectful; match that (mirrors how compound assignments like `a.b += 1` are already handled one impl above). Reported via rolldown/rolldown#9094.
fix/update-expression-side-effects
37 minutes ago
fix(transformer): correct scope of inferred named FE in async-to-generator When the async-to-generator transform wraps an async arrow / anonymous async function expression whose name is inferred from its parent (e.g. `const foo = async () => {}`, `{ foo: async () => {} }`), it emits a named function expression as the caller: ```js const foo = function () { var _ref = asyncToGenerator(function* () { /* body */ }); return function foo() { return _ref.apply(this, arguments); }; }(); ``` Per the JS spec, the inferred name `foo` binds only inside the named FE itself — not in the enclosing wrapper IIFE scope. But the previous code had two bugs that together caused oxc to emit an AST that a fresh semantic analysis would misread: 1. `create_function` set `FunctionType::FunctionDeclaration` whenever `id.is_some()`, even for callers using the result as a function expression. Any fresh semantic pass on the output trusts `r#type` and treats the node as a declaration — which binds the name in the enclosing wrapper scope. 2. The inferred id's binding was registered in `wrapper_scope_id` rather than the function's own scope. Combined, they cause downstream tools (e.g. Rolldown's minifier in Vite) to resolve a body reference like `typeNext()` inside `const typeNext = async () => { typeNext(); }` to the inner caller instead of the outer `const typeNext`. The outer const then looks unused, gets inlined away, and the body reference fails at runtime with a `ReferenceError`. Fix: pass `FunctionType` explicitly (via new `create_function_expression` and `create_function_declaration` helpers), and place the inferred id's binding in the caller function's own scope. Closes #21125 Closes #21445 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix/async-to-generator-inferred-name-scope
41 minutes ago
fix(ecmascript): treat update expressions as unconditionally side-effectful `obj.prop++` performs an implicit GetValue + ToNumeric + PutValue. The ToNumeric coercion alone can invoke `valueOf`/`Symbol.toPrimitive` on the old value, and the read/write may trigger getters, setters, or Proxy traps. Previously `UpdateExpression::may_have_side_effects` only checked `property_write_side_effects()` and treated the update as free when disabled — letting the minifier drop `counter.value++` even when `counter` is externally observable. Terser, esbuild, Rollup, and SWC all hardcode `++`/`--` as unconditionally side-effectful; match that (mirrors how compound assignments like `a.b += 1` are already handled one impl above). Reported via rolldown/rolldown#9094.
fix/update-expression-side-effects
4 hours ago
feat(parser): support `turbopack` magic comments
kane50613:main
4 hours ago
fix(minifier): avoid illegal `var;` when folding unused arguments copy loop (#21421) ## Summary When peephole-folding the classic `arguments` copy loop, we could end up with an empty `VariableDeclaration` initializer (`for (var; …)`), which prints as invalid `var;` and can confuse downstream folding (e.g. `try_fold_for` hoisting). ## Changes - Only build the `[...arguments]` / `.slice(offset)` array initializer when there is a binding pattern for the copy result (`r_id_pat`). - If there is **no** binding pattern, clear `for_stmt.init` instead of emitting an empty `VariableDeclaration`, so the loop becomes `for (; 0;)` and dead-code elimination can remove it cleanly. ## Tests - Added a regression in `crates/oxc_minifier/tests/peephole/substitute_alternate_syntax.rs` covering unused copy result + consequent (must not become illegal `var;`). ## AI usage This change was developed with assistance from Cursor; I reviewed the diff and tests locally before opening this PR. Co-authored-by: 进新 <zhengxubei.zx@alibaba-inc.com>
main
5 hours ago
fix(minifier): preserve `var` inside `catch` with same-named parameter (#21366) ## Summary - Block inlining of `var` declarators whose symbol has the `CatchVariable` flag, preventing loss of function-scoped hoisting - Check `symbol_redeclarations` before removing catch parameters, preventing semantic changes when the catch body contains a `var` redeclaration - Uses existing semantic data (`CatchVariable` flag and `symbol_redeclarations`) instead of manual AST walks Closes #17307 ## Test plan - [x] `cargo test -p oxc_minifier` — 476 tests pass - [x] Verified minified output matches original JS semantics via `node` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
main
5 hours ago
Merge branch 'main' into fix/minifier-rewrite-arguments-copy-loop
fazba:fix/minifier-rewrite-arguments-copy-loop
6 hours ago
Latest Branches
CodSpeed Performance Gauge
0%
fix(transformer): correct scope of inferred named FE in async-to-generator
#21458
1 hour ago
a21b1f6
fix/async-to-generator-inferred-name-scope
CodSpeed Performance Gauge
0%
fix(ecmascript): treat update expressions as unconditionally side-effectful
#21456
4 hours ago
1860cc7
fix/update-expression-side-effects
CodSpeed Performance Gauge
0%
feat(parser): support `turbopack` magic comments
#20803
19 days ago
6a0e43f
kane50613:main
© 2026 CodSpeed Technology
Home
Terms
Privacy
Docs