Skip to content

fix(transform-shaker, transform): shaker control flow fixes and deferred CJS exports#817

Merged
layershifter merged 9 commits intomainfrom
fix/shaker-control-flow-structural-children
Mar 13, 2026
Merged

fix(transform-shaker, transform): shaker control flow fixes and deferred CJS exports#817
layershifter merged 9 commits intomainfrom
fix/shaker-control-flow-structural-children

Conversation

@layershifter
Copy link
Member

@layershifter layershifter commented Mar 13, 2026

Summary

transform-shaker

  • Guard CatchClause.param — shaker was turning catch(e) {} into catch() {} (invalid syntax)
  • Guard LabeledStatement.label — shaker was turning outer: while(…) into : while(…)
  • Guard loop/conditional test expressions (DoWhileStatement.test, WhileStatement.test, IfStatement.test, SwitchStatement.discriminant, ForInStatement.left/right, ForOfStatement.left/right)
  • Fix export default someIdentifier treated as label instead of reference — shaker was removing the referenced function declaration
  • Fix TypeScript compiled enum IIFEs being removed — isLazyInit failed to recognize (Enum || (Enum = {})) because oxc-parser wraps in ParenthesizedExpression

transform

  • Defer exports.X = X assignments to end-of-file in convertESMtoCJS — fixes TS enum/namespace IIFE patterns where inline assignment captured undefined before the IIFE populated the variable (e.g. Depths.depth4)

Test plan

  • npx nx test transform-shaker — all tests pass (new tests for catch param, label, export default, IIFE enum preservation)
  • npx nx test transform — all tests pass (new test for deferred export assignment)
  • npx nx test webpack-plugin — all tests pass

🤖 Generated with Claude Code

@layershifter layershifter requested a review from a team as a code owner March 13, 2026 13:27
@github-actions
Copy link

github-actions bot commented Mar 13, 2026

📊 Bundle size report

✅ No changes found

layershifter and others added 6 commits March 13, 2026 17:46
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l, and loop test expressions

The shaker was removing scalar child nodes from control flow constructs,
producing invalid syntax:
- `catch(e) {}` → `catch() {}` (invalid, must be `catch(e)` or `catch`)
- `outer: while(…)` → `: while(…)`
- `do {} while(n)` → `do {} while()`

Add these and other control flow children (IfStatement.test,
SwitchStatement.discriminant, ForInStatement, ForOfStatement) to
STRUCTURAL_CHILDREN to prevent removal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, not label

`export default isPlainObject` was treated as a label (keep), so the
identifier wasn't connected to the function declaration in the dependency
graph. The shaker removed the function body but kept the export,
producing `ReferenceError: isPlainObject is not defined`.

Move ExportDefaultDeclaration.declaration from keep to refer so the
identifier resolves to its declaration. For inline function/class
declarations the handler doesn't fire (they're not Identifier nodes).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…or TS enum IIFEs

oxc-parser wraps `(ThemeName = {})` in a ParenthesizedExpression node, unlike
Babel which stores parentheses as metadata. This caused isLazyInit to fail to
recognize the TypeScript-compiled enum IIFE pattern:

  (function(Enum) { ... })(Enum || (Enum = {}));

Without the isLazyInit edge, the IIFE's assignment expression was never
connected to the ExpressionStatement, so the shaker removed the entire IIFE —
leaving the enum variable undefined at runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…toCJS

ESM uses live bindings — `export var X` reflects mutations made after the
declaration. CJS `exports.X = X` captures the value at assignment time.
When a TS-compiled enum IIFE follows `export var Depths;`, the inline
`exports.Depths = Depths` captured `undefined` before the IIFE populated it.

Deferring all `exports.X = X` from export declarations to the end of the
converted module approximates ESM live binding behavior and fixes the
`Depths.depth4` undefined error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@layershifter layershifter force-pushed the fix/shaker-control-flow-structural-children branch from 822e58c to c7af071 Compare March 13, 2026 16:47
@layershifter layershifter changed the title fix(transform-shaker): guard control flow structural children from removal fix(transform-shaker, transform): shaker control flow fixes and deferred CJS exports Mar 13, 2026
layershifter and others added 2 commits March 13, 2026 17:48
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@layershifter layershifter enabled auto-merge (squash) March 13, 2026 16:49
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@layershifter layershifter merged commit e7b6200 into main Mar 13, 2026
4 checks passed
@layershifter layershifter deleted the fix/shaker-control-flow-structural-children branch March 13, 2026 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants