Skip to content

fix(es/minifier): respect ecma for iife temp vars#11873

Merged
kdy1 merged 3 commits into
swc-project:mainfrom
hardfist:fix/minifier-ecma5-iife-temp-var
May 21, 2026
Merged

fix(es/minifier): respect ecma for iife temp vars#11873
kdy1 merged 3 commits into
swc-project:mainfrom
hardfist:fix/minifier-ecma5-iife-temp-var

Conversation

@hardfist
Copy link
Copy Markdown
Collaborator

Description:

When compressing arrow IIFEs, the minifier can synthesize temporary variable declarations for extracted parameters. That path always emitted let, even when compress.ecma was ES5.

This updates the synthesized declaration kind to use var below ES2015 and keep let for ES2015+, then adds an ES5 fixture covering the arrow IIFE temp-var path. Related snapshots were updated for the same behavior change.

BREAKING CHANGE:

None.

Related issue (if exists):

N/A

Testing:

  • git submodule update --init --recursive
  • cargo fmt --all
  • cargo test -p swc_ecma_minifier
  • cargo clippy --all --all-targets -- -D warnings

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 20, 2026

🦋 Changeset detected

Latest commit: 9830d18

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 20, 2026

Merging this PR will not alter performance

✅ 219 untouched benchmarks
⏩ 31 skipped benchmarks1


Comparing hardfist:fix/minifier-ecma5-iife-temp-var (9830d18) with main (4662db8)

Open in CodSpeed

Footnotes

  1. 31 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@hardfist hardfist marked this pull request as ready for review May 20, 2026 07:22
@hardfist hardfist requested a review from a team as a code owner May 20, 2026 07:22
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 364bf3f4cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +689 to +693
kind: if self.options.ecma >= EsVersion::Es2015 {
VarDeclKind::Let
} else {
VarDeclKind::Var
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve per-iteration captures when lowering IIFE temp vars

Switching synthesized temp declarations from let to var in invoke_iife breaks closure semantics for arrow-IIFE inlining inside loops when compress.ecma < 2015. In patterns like for (const k in obj) { out[k] = ((k)=>()=>k)(k) }, the extracted temp now becomes a function-scoped var, so all generated closures share one binding and observe the last iteration’s value instead of each iteration’s value. This is a runtime behavior regression (not just formatting) and is visible in the updated 8246 fixture shape where the captured temp is now var inside the loop body.

Useful? React with 👍 / 👎.

Comment on lines +689 to +693
kind: if self.options.ecma >= EsVersion::Es2015 {
VarDeclKind::Let
} else {
VarDeclKind::Var
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid leaking inlined arrow params into outer function scope

Using var here also removes block scoping for synthesized parameter temporaries, so an inlined arrow-IIFE inside a nested block can unexpectedly alias an existing outer binding with the same name. For example, var value = 1; { ((value)=>value)(2); } now lowers through inline_fn_param into assignments against a function-scoped var value, mutating the outer value (returning 2) even though the original arrow parameter should have remained block-local shadowing.

Useful? React with 👍 / 👎.

Comment on lines +689 to +693
kind: if self.options.ecma >= EsVersion::Es2015 {
VarDeclKind::Let
} else {
VarDeclKind::Var
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent var/lexical redeclaration syntax errors after inlining

Lowering these synthesized temporaries to function-scoped var can make previously valid code fail to parse when the enclosing function already has a lexical binding with the same name. A valid case like function f(){ let x=0; { ((x)=>x)(1) } } can be rewritten with an injected var x, which conflicts with the existing let x in the same function scope and triggers Identifier 'x' has already been declared.

Useful? React with 👍 / 👎.

@Austaras
Copy link
Copy Markdown
Member

I believe the current behavior is fine as there're very few browsers that supports arrow but not let/const.

@hardfist
Copy link
Copy Markdown
Collaborator Author

hardfist commented May 20, 2026

I believe the current behavior is fine as there're very few browsers that supports arrow but not let/const.

its actually about performance let and const is much slower in some js engine(like quickjs and safari
Related to https://esbuild.github.io/faq/#:~:text=bundling%20is%20active.-,For%20performance

let | const is about 10x+ slower than var during parsing in primjs engine(which is a modified version of quickjs), when I replace let | const to var in our code, the whole parse time reduced from 453ms to 264ms for our code

kdy1
kdy1 previously approved these changes May 21, 2026
Copy link
Copy Markdown
Member

@kdy1 kdy1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, thanks!

I'll merge after adding changesets

@kdy1 kdy1 requested a review from a team as a code owner May 21, 2026 02:02
@kdy1 kdy1 merged commit e481934 into swc-project:main May 21, 2026
42 of 62 checks passed
@github-actions github-actions Bot added this to the Planned milestone May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants