fix(angular-compiler): hostDirectives, emitExpr safety, TDZ hoisting, misc compilation fixes#2255
Conversation
✅ Deploy Preview for analog-blog ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-docs 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 (6)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughUpdates extend compiler behavior and tests: documentation adds Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Key observations
🚥 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: 3
🧹 Nitpick comments (1)
packages/angular-compiler/src/lib/compile.ts (1)
791-818: Consider narrowing the hoisting scope to only declarations actually referenced by Ivy initializers.The pass unconditionally hoists all non-exported post-class declarations before the first class. While this prevents TDZ errors, it also changes module evaluation order for unrelated side effects. Function declarations are already hoisted by JavaScript and don't need moving. If blanket reordering is necessary, add an explicit note in the commit explaining why selective hoisting isn't feasible, and verify no real-world code depends on post-class declaration order.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-compiler/src/lib/compile.ts` around lines 791 - 818, The current hoisting loop in compile.ts moves all non-exported post-class variable and function declarations, which can reorder unrelated side effects; narrow this to only move variable declarations actually referenced by Ivy initializers: use classResults to collect identifier names used by the static ɵcmp/initializer code (or other classResults fields that contain initializer AST/text), then when iterating origSourceFile.statements only hoist ts.isVariableStatement stmts that declare one or more of those referenced names (skip function declarations entirely since JS already hoists them), using hasExportModifier to avoid exported symbols and ms.appendLeft/ms.remove to perform the move; if selective hoisting cannot cover all cases add a clear comment/commit note explaining why blanket hoisting is required and document the risk to evaluation order.
🤖 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/integration.spec.ts`:
- Around line 1675-1716: Add a focused spec that reproduces the regression where
an argument can be emitted as undefined so compilation doesn't crash: inside the
existing "emitExpr routing for function/method args" suite add a test that calls
a component method with an undefined argument (for example via a literal
`undefined` or a property that is `undefined`) in the template, compile it
(using the existing compile(...) helper) and assert the compilation succeeds
(expectCompiles(result)) and the generated output contains the expected call
site (e.g., 'ctx.format' or the method name), so this directly exercises the
emitExpr path changed in js-emitter.ts and guards against the undefined-arg
crash.
- Around line 1623-1650: Update the test that uses compile/expectCompiles for
HostComponent/ColorDirective to assert the aliased input/output names are
exposed, not just that "hostDirectives" exists: modify the hostDirectives object
in the test to use inputs: ['color: appColor'] and outputs: ['colorChange:
appColorChange'] and then assert the compiled result contains the aliased
identifiers (e.g. "appColor" and "appColorChange" or the specific generated
mapping for hostDirectives), so the normalization logic in compile for
inputs/outputs is validated.
In `@packages/angular-compiler/src/lib/metadata.ts`:
- Around line 181-199: The inputs/outputs parsing currently stores the raw
string; update the loop in metadata.ts (the blocks handling
prop.initializer.elements where inputs and outputs are populated) to parse
ts.isStringLiteral(e). For each e.text, if it contains a colon split on ':' and
trim both sides so you assign the directive property as the key and the public
alias as the value (e.g., let [dirProp, alias] = text.split(':').map(s =>
s.trim()); inputs![dirProp] = alias ?? dirProp), otherwise preserve the original
name for key and value; apply the same fix for outputs and add a unit test that
exercises an aliased binding like inputs: ['color: appColor'] to prevent
regressions.
---
Nitpick comments:
In `@packages/angular-compiler/src/lib/compile.ts`:
- Around line 791-818: The current hoisting loop in compile.ts moves all
non-exported post-class variable and function declarations, which can reorder
unrelated side effects; narrow this to only move variable declarations actually
referenced by Ivy initializers: use classResults to collect identifier names
used by the static ɵcmp/initializer code (or other classResults fields that
contain initializer AST/text), then when iterating origSourceFile.statements
only hoist ts.isVariableStatement stmts that declare one or more of those
referenced names (skip function declarations entirely since JS already hoists
them), using hasExportModifier to avoid exported symbols and
ms.appendLeft/ms.remove to perform the move; if selective hoisting cannot cover
all cases add a clear comment/commit note explaining why blanket hoisting is
required and document the risk to evaluation order.
🪄 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: c6d585b8-8430-4df2-b5a8-a13289694bbd
📒 Files selected for processing (6)
packages/angular-compiler/COMPILER.mdpackages/angular-compiler/src/lib/compile.tspackages/angular-compiler/src/lib/integration.spec.tspackages/angular-compiler/src/lib/js-emitter.tspackages/angular-compiler/src/lib/metadata.tspackages/angular-compiler/src/lib/type-elision.ts
… type-elision and version detection - Route function/method/instantiate args through emitExpr() to handle undefined args gracefully (fixes visitExpression crash on production build) - Parse hostDirectives decorator metadata into R3HostDirectiveMetadata (bare identifiers, object form with inputs/outputs, forwardRef) - Hoist non-exported post-class const/function declarations before the first class to avoid TDZ errors in static initializers - Fix ANGULAR_MAJOR fallback: treat version 0.0.0 (local builds) as 22 - Set hasDirectiveDependencies when meta.imports is non-empty even if registry declarations are empty (fixes empty router-outlet) - Fix type-elision: TSAsExpression/TSSatisfiesExpression expression child is a value position, not a type position (fixes erroneous elision of imports used in as-casts) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c9b06c9 to
8645225
Compare
PR Checklist
Affected scope
Recommended merge strategy for maintainer [optional]
What is the new behavior?
Fix multiple angular-compiler bugs:
Validated analog-compiler works with angular.dev application
Test plan
npx vitest run packages/angular-compiler/src/lib/integration.spec.ts— 70 tests passing (8 new)ANGULAR_SOURCE_DIR=.angular-conformance npx vitest run packages/angular-compiler/src/lib/conformance.spec.ts— 160 passed, 0 failures (Angular v21.2.7)pnpm buildpnpm testDoes this PR introduce a breaking change?
Other information