fix(angular-compiler): make hoisting dependency-aware to prevent TDZ#2286
Conversation
…ent TDZ Step 2a hoists non-exported variable/function statements before the first class so Ivy static definitions can reference file-level constants. However, it indiscriminately hoisted all such statements, including user-authored code that references classes, enums, or exported names defined after the first class — moving the reference before its definition and creating a TDZ error. Replace the single-pass hoist with a two-pass dependency-aware algorithm that adds negligible overhead (two lightweight iterations over the same top-level statements, with O(1) Set lookups): - Pass 1: collect names from statements that won't be hoisted (classes, enums, exported vars/functions after the first class) - Pass 2: for each candidate, check if it references any non-hoistable name; if so, skip it and mark its own names as non-hoistable (transitivity) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for analog-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-blog ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-app ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR augments the compiler hoisting pass with TDZ-safety analysis. New helpers ( Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/angular-compiler/src/lib/compile.ts`:
- Around line 126-137: collectIdentifiers currently descends into nested
function/arrow/class bodies and thus collects identifiers from lazily-evaluated
code; update the walk() inside collectIdentifiers to stop recursing into nested
function-like and class-like nodes (e.g. where ts.isFunctionLike(n) ||
ts.isClassLike(n)) so that when such a node is encountered you still handle the
node itself (identifiers on the node) but do not call ts.forEachChild on its
body; this ensures only eagerly-executed parts of initializers are scanned and
prevents lazy references (like in ArrowFunction/FunctionExpression/Class bodies)
from being treated as dependencies.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4162fe34-f2ea-4e7e-94d2-24db3f0d4329
📒 Files selected for processing (2)
packages/angular-compiler/src/lib/compile.tspackages/angular-compiler/src/lib/component.spec.ts
…ent false TDZ blocks collectIdentifiers walked into arrow/function/class bodies, treating lazy references as eager dependencies. This caused constants like `const TOKEN = () => LaterClass` to be incorrectly blocked from hoisting. Stop recursing into function-like and class-like nodes so only eagerly-evaluated identifiers are collected. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Currently the compile.ts file hoists non-exported variable/function statements before the first class so Ivy static definitions can reference file-level constants. However, it indiscriminately hoisted all such statements, including user-authored code that references classes, enums, or exported names defined after the first class - moving the reference before its definition and creating a TDZ error.
Replace the single-pass hoist with a two-pass dependency-aware algorithm (two lightweight iterations over the same top-level statements):
non-hoistable (transitivity)
PR Checklist
Closes #
Affected scope
Recommended merge strategy for maintainer [optional]
Commit preservation note [optional]
What is the new behavior?
Test plan
nx format:checkpnpm buildpnpm testDoes this PR introduce a breaking change?
Other information
[optional] What gif best describes this PR or how it makes you feel?