Skip to content

fix(vite-plugin-angular): emit fastCompile JIT metadata in nested class scope#2361

Merged
brandonroberts merged 1 commit into
betafrom
claude/issue-2360-IOe3Y
Jun 2, 2026
Merged

fix(vite-plugin-angular): emit fastCompile JIT metadata in nested class scope#2361
brandonroberts merged 1 commit into
betafrom
claude/issue-2360-IOe3Y

Conversation

@brandonroberts

Copy link
Copy Markdown
Member

PR Checklist

Components defined directly inside a test spec file — e.g. a host component declared within a describe()/it()/beforeEach() callback to test a directive — threw ReferenceError: <Class> is not defined under fastCompile.

Closes #2360

Affected scope

  • Primary scope: vite-plugin-angular
  • Secondary scopes: none

Recommended merge strategy for maintainer [optional]

  • Squash merge
  • Rebase merge
  • Other

What is the new behavior?

In test mode fastCompile uses the JIT path (angular-vite-plugin.ts sets jit to isTest when not explicitly provided), so jitTransform() runs rather than the AOT registry path.

jitTransform calls findAllClasses(), which walks the AST recursively and therefore finds classes nested inside function/callback scopes — including a host @Component declared inside a describe(...)/it(...) block, the common "host component to test a directive" pattern. For every such class it collected the generated metadata statements (<Class>.decorators = [...], _jitCompileComponent(<Class>, ...), <Class>.ctorParameters = ..., <Class>.propDecorators = ...) into a single array and appended them all at the end of the file (module scope). A class declared inside a callback is not in scope there, so the appended statement referenced an undefined name and threw ReferenceError: <Class> is not defined when the spec module was evaluated.

This was missed because the existing nested-class tests only asserted the emitted string contained <Class>.decorators — they never evaluated the code.

The fix emits each class's metadata statements directly after its class body via ms.appendLeft(node.end, ...), so they run in the class's own lexical scope:

  • top-level classes → module scope (unchanged behavior)
  • nested classes → the enclosing function/callback scope (now correct)

node.end is the position just past the class's closing brace (decorators were already stripped before node.start), so the statements land as the next statement(s) in the same block. A separate accumulator string preserves the existing post-loop detection of missing field-decorator imports. Angular's ɵcompileComponent installs a lazy ɵcmp, so emitting a top-level component's _jitCompileComponent(...) right after its own declaration (instead of after every class) is safe.

Before / after for a component declared inside a function:

// before — appended at end of file, TestComponent out of scope → ReferenceError
export function makeHost() {
  class TestComponent {}
  return TestComponent;
}
TestComponent.decorators = [...];          // ReferenceError
_jitCompileComponent(TestComponent, {...});

// after — emitted in scope
export function makeHost() {
  class TestComponent {}
  TestComponent.decorators = [...];
  _jitCompileComponent(TestComponent, {...});
  return TestComponent;
}

Test plan

  • nx format:check (touched files)
  • nx build vite-plugin-angular
  • nx test vite-plugin-angular — 620 passed, 3 skipped
  • Manual verification — confirmed against built output that the metadata statements are emitted inside the enclosing function scope

Adds an evaluating regression test (jit-transform.spec.ts) that runs the emitted JS through new Function with stubbed @angular/core exports — it would throw ReferenceError under the old file-end emit and passes with the fix.

Does this PR introduce a breaking change?

  • Yes
  • No

fastCompile is opt-in, and top-level class emit is unchanged — only the out-of-scope placement for nested classes is corrected.

Other information

Scoped to the JIT path, which is the path test mode uses, directly resolving the reported error. The AOT registry's .spec.ts handling in fast-compile-plugin.ts is not on the test path and is out of scope for this issue.

https://claude.ai/code/session_01DEPPXW9VMWVXNYmYDM2Y6A


Generated by Claude Code

Components defined directly inside a test spec file - e.g. a host
component declared within a describe()/it() callback to test a directive
- threw 'ReferenceError: <Class> is not defined' under fastCompile.

In test mode fastCompile uses the JIT path. jitTransform recursively
finds nested classes but collected every class's generated metadata
statements (<Class>.decorators = ..., _jitCompileComponent(...), etc.)
and appended them all at the end of the file (module scope). A class
declared inside a function/callback scope is not in scope there, so the
appended statement referenced an undefined name.

Emit each class's metadata statements directly after its class body via
ms.appendLeft(node.end, ...) so they run in the class's own lexical
scope - module scope for top-level classes (unchanged behavior), the
enclosing callback scope for nested ones.

Adds an evaluating regression test (the prior nested-class tests only
asserted the output string, never executing it).

Closes #2360

https://claude.ai/code/session_01DEPPXW9VMWVXNYmYDM2Y6A
@netlify

netlify Bot commented Jun 2, 2026

Copy link
Copy Markdown

Deploy Preview for analog-docs ready!

Name Link
🔨 Latest commit 96e5116
🔍 Latest deploy log https://app.netlify.com/projects/analog-docs/deploys/6a1f0cd3814ba60008fb312b
😎 Deploy Preview https://deploy-preview-2361--analog-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^beta$
  • ^alpha$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6321959a-a1e1-4cbe-8526-c89735a51f4b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added scope:astro-angular Changes in @analogjs/astro-angular scope:ci GitHub workflow changes scope:content Changes in @analogjs/content scope:create-analog Changes in create-analog scope:docs Documentation changes scope:nx-plugin Changes in @analogjs/nx-plugin scope:platform Changes in @analogjs/platform scope:repo Repository metadata and tooling scope:router Changes in @analogjs/router scope:storybook-angular Changes in @analogjs/storybook-angular scope:vite-plugin-angular Changes in @analogjs/vite-plugin-angular scope:vite-plugin-nitro Changes in @analogjs/vite-plugin-nitro scope:vitest-angular Changes in @analogjs/vitest-angular labels Jun 2, 2026
@brandonroberts brandonroberts changed the base branch from main to beta June 2, 2026 17:11
@github-actions github-actions Bot removed scope:astro-angular Changes in @analogjs/astro-angular scope:content Changes in @analogjs/content scope:create-analog Changes in create-analog scope:nx-plugin Changes in @analogjs/nx-plugin scope:platform Changes in @analogjs/platform scope:router Changes in @analogjs/router scope:storybook-angular Changes in @analogjs/storybook-angular scope:vite-plugin-nitro Changes in @analogjs/vite-plugin-nitro scope:vitest-angular Changes in @analogjs/vitest-angular scope:docs Documentation changes scope:ci GitHub workflow changes scope:repo Repository metadata and tooling labels Jun 2, 2026
@brandonroberts brandonroberts merged commit a73ee49 into beta Jun 2, 2026
27 checks passed
@brandonroberts brandonroberts deleted the claude/issue-2360-IOe3Y branch June 2, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope:vite-plugin-angular Changes in @analogjs/vite-plugin-angular

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FastCompile is skipping components defined in tests

2 participants