Skip to content

fix(vite-plugin-angular): import NgModule/tuple exports from their defining file in fastCompile#2370

Merged
brandonroberts merged 1 commit into
betafrom
fix/fastcompile-ngmodule-export-import-path
Jun 10, 2026
Merged

fix(vite-plugin-angular): import NgModule/tuple exports from their defining file in fastCompile#2370
brandonroberts merged 1 commit into
betafrom
fix/fastcompile-ngmodule-export-import-path

Conversation

@brandonroberts

Copy link
Copy Markdown
Member

PR Checklist

Fixes a fastCompile bug where expanding an NgModule's exports (or a tuple barrel's members) generated a synthetic import that pointed at the module file rather than the file the class is actually defined in, causing build errors like:

"MyComponent" is not exported by "libs/.../my.module.ts"

Affected scope

  • Primary scope: vite-plugin-angular (fast compiler — compiler/compile.ts, compiler/utils.ts)
  • Secondary scopes: none

What is the new behavior?

When a standalone component imports a local NgModule, the fast compiler expands the module's exports into direct declarations and injects synthetic imports for each exported class. For local classes (no sourcePackage), it previously fell back to the module's import specifier — but the module file only re-exports the module, not the component, so the import failed.

The synthetic import specifier is now resolved from the declaration's own fileName in the registry, deriving a relative path from the consuming file (./my.component instead of ./my.module). Details:

  • External .d.ts entries continue to fall back to the module specifier (their file path is not a usable relative import target; package-name/sourcePackage resolution is preserved).
  • Same-file declarations emit no synthetic import (already in scope).
  • The identical bug in the tuple barrel expansion path (imports: [SomeImports] where the const is [A, B] as const) is fixed by the same shared helper.

New shared helpers live in compiler/utils.ts: resolveSyntheticImportSpecifier and deriveRelativeImportPath.

Test plan

  • nx format:check — ran prettier --write/--check on changed files instead
  • pnpm build
  • pnpm testvitest run packages/vite-plugin-angular/src/lib/compiler (1072 passed, 0 failures; conformance baseline unchanged)
  • Manual verification — added a regression test asserting the synthetic import targets ./button.component, not ./shared.module; confirmed it fails without the fix and passes with it

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Reported via a detailed write-up; no linked issue. The report cited line numbers from a built compile.js; the live source locations are the NgModule (~497) and tuple (~457) export-expansion branches in compiler/compile.ts.

🤖 Generated with Claude Code

…fining file in fastCompile

When fastCompile expands an NgModule's `exports` (or a tuple barrel's
members) into synthetic imports, local classes have no `sourcePackage`,
so the compiler fell back to the module/tuple's own import specifier.
This imported the declaration from the module file (e.g. `./x.module`),
which only exports the module — not the component — producing
`"MyComponent" is not exported by ".../x.module.ts"`.

Resolve the synthetic import specifier from the declaration's own
`fileName` in the registry instead, deriving a relative path from the
consuming file. External `.d.ts` entries keep falling back to the module
specifier since their file path is not a usable import target, and
same-file declarations emit no synthetic import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the scope:vite-plugin-angular Changes in @analogjs/vite-plugin-angular label Jun 10, 2026
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors synthetic import specifier resolution in the Angular vite plugin compiler. It introduces two utility functions in utils.tsresolveSyntheticImportSpecifier (which prioritizes sourcePackage, derived relative paths, or a fallback) and deriveRelativeImportPath (which normalizes relative file-based import paths)—then applies this logic consistently in both the tuple-barrel and NgModule export expansion paths within compile.ts, replacing prior ad-hoc fallback patterns. A new test case validates that NgModule re-exports correctly resolve to the component's defining file.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commit style with scope (vite-plugin-angular) and clearly summarizes the main fix: importing NgModule/tuple exports from their defining file.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the bug, the fix, affected files, test coverage, and validation performed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

❤️ Share

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

@netlify

netlify Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploy Preview for analog-blog ready!

Name Link
🔨 Latest commit a4cb51a
🔍 Latest deploy log https://app.netlify.com/projects/analog-blog/deploys/6a29a799003b5100084b5953
😎 Deploy Preview https://deploy-preview-2370--analog-blog.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.

@netlify

netlify Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploy Preview for analog-docs ready!

Name Link
🔨 Latest commit a4cb51a
🔍 Latest deploy log https://app.netlify.com/projects/analog-docs/deploys/6a29a799f482410008329098
😎 Deploy Preview https://deploy-preview-2370--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.

@netlify

netlify Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploy Preview for analog-app ready!

Name Link
🔨 Latest commit a4cb51a
🔍 Latest deploy log https://app.netlify.com/projects/analog-app/deploys/6a29a799f482410008329096
😎 Deploy Preview https://deploy-preview-2370--analog-app.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 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/vite-plugin-angular/src/lib/compiler/ngmodule.spec.ts (1)

133-172: ⚡ Quick win

Add a tuple-barrel import-path regression test next to this NgModule case.

This PR updates synthetic specifier resolution in both NgModule and tuple expansion paths, but this new assertion only guards the NgModule branch. A small tuple-focused case asserting import-from-defining-file (not tuple barrel file) would lock in the second changed path too.

As per coding guidelines, “Include tests which validate behavior for any new functionality added”; the PR objective also states both NgModule and tuple-barrel expansion were changed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vite-plugin-angular/src/lib/compiler/ngmodule.spec.ts` around lines
133 - 172, Add a new spec next to the existing NgModule test that reproduces the
tuple-barrel case: create a button component source (ButtonComponent) and a
tuple-barrel file (e.g., export const SHARED = [ButtonComponent]) registered via
buildRegistry, then compile an app that imports that tuple (use compile and
expectCompiles as in the existing test). Assert the generated output includes an
import from the component's defining file (expect(result).toContain 'import {
ButtonComponent } from "./button.component"') and does NOT import from the
tuple-barrel file (expect(result).not.toContain 'import { ButtonComponent } from
"./shared.barrel"'), ensuring tuple expansion resolves to the defining file just
like the NgModule path.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/vite-plugin-angular/src/lib/compiler/ngmodule.spec.ts`:
- Around line 133-172: Add a new spec next to the existing NgModule test that
reproduces the tuple-barrel case: create a button component source
(ButtonComponent) and a tuple-barrel file (e.g., export const SHARED =
[ButtonComponent]) registered via buildRegistry, then compile an app that
imports that tuple (use compile and expectCompiles as in the existing test).
Assert the generated output includes an import from the component's defining
file (expect(result).toContain 'import { ButtonComponent } from
"./button.component"') and does NOT import from the tuple-barrel file
(expect(result).not.toContain 'import { ButtonComponent } from
"./shared.barrel"'), ensuring tuple expansion resolves to the defining file just
like the NgModule path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cb113946-19b0-470b-9b84-428112373e6c

📥 Commits

Reviewing files that changed from the base of the PR and between 98fe9cb and a4cb51a.

📒 Files selected for processing (3)
  • packages/vite-plugin-angular/src/lib/compiler/compile.ts
  • packages/vite-plugin-angular/src/lib/compiler/ngmodule.spec.ts
  • packages/vite-plugin-angular/src/lib/compiler/utils.ts

@brandonroberts brandonroberts merged commit dafa025 into beta Jun 10, 2026
37 checks passed
@brandonroberts brandonroberts deleted the fix/fastcompile-ngmodule-export-import-path branch June 10, 2026 18:28
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.

1 participant