Skip to content

fix(@angular/build): resolve unimported NgModule scopes when filtering unit-test files - #34329

Merged
dgp1130 merged 1 commit into
angular:mainfrom
clydin:fix/unit-test-ngmodule-scope-fallback
Oct 14, 2026
Merged

dgp1130 merged 1 commit into
angular:mainfrom
clydin:fix/unit-test-ngmodule-scope-fallback

Conversation

@clydin

@clydin clydin commented Oct 13, 2026

Copy link
Copy Markdown
Member

When rootFiles overrides are provided (such as during unit-test runs), the initial TypeScript program is restricted to those entry points and their transitive imports. If a test imports a non-standalone @Component directly without importing its declaring @NgModule, the NgModule may be omitted from the program and leave the component without its compilation scope.

Preserve the full tsconfig root file set as fallbackRootNames for application builds when rootFiles is a subset of the tsconfig files. If AOT analysis detects a non-standalone component lacking an owning NgModule scope (getOwningNgModule(component) === null) and additional uncompiled tsconfig files exist, recreate and re-analyze the Angular program with the full root file set so all NgModule declarations can be discovered.

…g unit-test files

When `rootFiles` overrides are provided (such as during `unit-test` runs),
the initial TypeScript program is restricted to those entry points and their
transitive imports. If a test imports a non-standalone `@Component` directly
without importing its declaring `@NgModule`, the `NgModule` may be omitted
from the program and leave the component without its compilation scope.

Preserve the full `tsconfig` root file set as `fallbackRootNames` for
application builds when `rootFiles` is a subset of the `tsconfig` files. If
AOT analysis detects a non-standalone component lacking an owning `NgModule`
scope (`getOwningNgModule(component) === null`) and additional uncompiled
`tsconfig` files exist, recreate and re-analyze the Angular program with the
full root file set so all `NgModule` declarations can be discovered.
@clydin
clydin force-pushed the fix/unit-test-ngmodule-scope-fallback branch from 6be51ea to 957de2a Compare October 13, 2026 16:56
@clydin
clydin marked this pull request as ready for review October 13, 2026 18:47

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a fallback mechanism to the full tsconfig root file set (fallbackRootNames) during AOT compilation when explicit rootFiles filtering is active. This fallback is triggered if a non-standalone component is encountered that lacks a resolved NgModule scope, allowing the Angular compiler to discover all NgModules and resolve component compilation scopes. Unit tests have been added to verify this behavior. I have no additional feedback to provide as there are no review comments.

@clydin clydin added action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release target: minor This PR is targeted for the next minor release and removed target: patch This PR is targeted for the next patch release labels Oct 13, 2026
@alan-agius4 alan-agius4 linked an issue Oct 13, 2026 that may be closed by this pull request
1 task done
@alan-agius4 alan-agius4 added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Oct 14, 2026
@dgp1130
dgp1130 merged commit 7714a85 into angular:main Oct 14, 2026
43 of 44 checks passed
@dgp1130

dgp1130 commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

This PR was merged into the repository. The changes were merged into the following branches:

@clydin
clydin deleted the fix/unit-test-ngmodule-scope-fallback branch October 14, 2026 16:39
leosvelperez added a commit to nrwl/nx that referenced this pull request Oct 19, 2026
The @angular/build 22.2 Vitest runner skips NgModule files that no spec
imports, so both NgRx e2e tests fail with NG8001
(angular/angular-cli#34231). The fix, angular/angular-cli#34329, is on
main but not in a 22.2.x release yet.
FrozenPandaz pushed a commit to nrwl/nx that referenced this pull request Oct 19, 2026
## Current Behavior

Nx creates Angular workspaces with Angular 22.1 and has no migration to
22.2. With Angular 22.2:

- `@nx/angular-rspack` doesn't work with `@angular/build` 22.2, because
the private Angular APIs it calls changed.
- Vitest runs that use Analog before 2.7.3 fail with `cache.has is not a
function`.
- Buildable libraries built with ng-packagr 22.2.0 to 22.2.3 are missing
the output of their secondary entry points.
- A secondary entry point with its own browserslist file gets different
CSS targets than ng-packagr 22.2 gives it.

Other `@nx/angular-rspack` bugs:

- When `/` is prerendered, server responses have two `ng-state` scripts,
because the server renders from the prerendered `/` page.
- With `@angular/build` 22.1, `isolatedModules` and script source maps,
some bundled files get empty source maps.
- With `@angular/build` 22.1.3 or later, `3rdpartylicenses.txt` differs
from the Angular CLI output.
- With `@angular/build` before 22.2, `removeComments: true` in the
tsconfig strips `/* @__PURE__ */` annotations, so the minifier keeps
unused calls.

The `add-trust-proxy-headers` migration breaks `new AngularAppEngine`
written without parentheses. It adds a second `trustProxyHeaders` when
the options set it with a shorthand, method or accessor member.

## Expected Behavior

- New workspaces and generated projects install Angular `~22.2.0` and
angular-eslint `^22.5.0`. New Vitest setups with Analog install Analog
`~2.7.3`.
- `nx migrate` moves Angular 22.1 workspaces to 22.2. Workspaces that
use Angular Cypress component testing, or `@module-federation/enhanced`
in an Angular webpack build, stay on 22.1, and the migration says why.
- `nx migrate` moves Angular 22 workspaces to angular-eslint 22.5, and
workspaces with Analog to Analog 2.7.3.
- Setting up Cypress component testing fails on Angular 22.2 and later,
with an error that links cypress-io/cypress#34259.
- The `@nx/angular:unit-test` executor accepts Angular's new `splitting`
option on 22.2 and rejects it on earlier versions.
- `@nx/angular-rspack` works with `@angular/build` 22.2, where
server-rendered and prerendered pages inline critical CSS like the
Angular CLI.
- The bugs listed above are fixed.

## Related Issue(s)

Fixes #37375

NXC-5056

## Implementation Notes

> [!IMPORTANT]
> Merge after #37477, which adds the `optionalUpdate` flag this PR sets
on the Angular 22.2 update. Without it, `--include=required` runs the
update and `--include=optional` skips it.

- The Angular 22.2 update runs as a migration so it can keep a workspace
on 22.1 when the first two breaks below affect it.
- `add-angular-build` adds `@angular/build` at the installed
`@angular-devkit/build-angular` version, so it doesn't run ahead of a
workspace kept on 22.1.
- `nx migrate` doesn't install after a migration that changes only
catalog entries. #37478, released with this PR, fixes it.
- For SSR, the browser build generates `index.server.html` and
`index.csr.html` after output hashing, like the Angular CLI. On 22.2 the
server index is written to `browser/index.original.html` for the Nx
CommonEngine server template, so generated servers need no edits.
- The repo catalog and the `@nx/webpack` option types move to webpack
5.111.0, with no runtime change.

> [!IMPORTANT]
> `@angular-devkit/build-angular` 22.2 installs webpack 5.111.0 and
webpack-dev-server 6. No stable release fixes these breaks yet:
>
> - `@module-federation/enhanced` production builds fail
(module-federation/core#5077, open fix in module-federation/core#5165).
> - Cypress component testing rejects webpack-dev-server 6
(cypress-io/cypress#34259, open fix in cypress-io/cypress#35040). The
Angular component testing e2e tests are skipped.
> - Vitest tests of NgModule components can fail with NG8001 on
`@angular/build` 22.2 (angular/angular-cli#34231, fixed by
angular/angular-cli#34329 in 22.3.0-next.2). The `ngrx` e2e suite is
skipped.

> [!NOTE]
> `useTsProjectReferences` has no effect on 22.2, and the plugin warns
when it is set. A follow-up deprecates it for removal in Nx 25 and
rewrites its `kb/create-config.mdoc` entry, which still says it enables
project references.

<!-- polygraph-session-start -->
---
<p><a
href="https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/nxc-5056-2dcb8260">View
Polygraph session ↗</a></p>
<!-- polygraph-session-end -->
eiweiss added a commit to cngxjs/cngx that referenced this pull request Oct 21, 2026
…apps (#637)

**feat(core/schematics): place the feedback outlets in ng new ngmodule
apps**

### PR Type

What kind of change does this PR introduce?

- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [x] CI related changes
- [x] Documentation content changes
- [ ] Other...

### What is the current behavior?

In an `ng new --standalone=false` app, `ng add @cngx/core` with feedback
on places no outlet. The spec the CLI writes for an NgModule app
declares `App` in its own TestBed, so `ng add` reads it as a custom
spec, fails closed and only prints a note.

### What is the new behavior?

On Angular CLI 21, `ng add` with feedback on places both outlets in an
NgModule app and keeps `ng test` green:

- `<cngx-banner-outlet />` and `<cngx-toast-outlet />` go into the root
template, and `CngxBannerOutlet` and `CngxToastOutlet` into the root
NgModule `imports`, as before for NgModule apps without a spec.
- The untouched NgModule `app.spec.ts` gets both outlet classes in the
TestBed `imports` (the property is created when it is missing),
`provideFeedback(withToasts(), withBanners())` in its `providers`, and
the two import statements. These are AST edits, re-checked at apply
time.
- Detection is exact: the scaffold catalog now holds the NgModule spec
templates of CLI 21 and 22 (routing on or off, zone or zoneless, raw and
Prettier-formatted), and the provided form is the same edit applied to
those bodies, so a spec the CLI's Prettier pass reformatted still reads
as provided. One changed character makes the spec custom.
- A second `ng add` plans nothing.

Angular 22 stays fail-closed. From `@angular/build` 22.2, the Vitest
runner compiles only the specs and the files they import, so `App`
compiles without `AppModule`. An untouched CLI 22 NgModule app then
fails `ng test` with NG8001 on `<router-outlet>`, and every outlet fails
the same way whatever the TestBed holds
([angular/angular-cli#34231](angular/angular-cli#34231),
fixed by angular/angular-cli#34329 for 22.3, which is not out as a
stable release yet).

The NgModule spec therefore takes the outlets only where
`@angular/build` 21 is installed, or, before the install, where
`package.json` declares a range that admits major 21 alone. Every other
case keeps the outlets out with its own note: an installed 22, an
unreadable manifest, a range that spans majors, a tag, and a custom
NgModule spec on 22. The note names the outlets, what the TestBed needs,
and `import './app-module';` for the spec, which makes the test build
compile `App` with its NgModule.

|App|Outlets|Spec|
|-|-|-|
|CLI 21 NgModule, untouched spec|placed|outlet classes + providers|
|CLI 21 NgModule, custom spec|note|unchanged|
|NgModule on `@angular/build` 22 (any spec)|note, with the module
import|unchanged|
|Standalone|unchanged|unchanged|

CI: a new `consumer-compat` row `angular-21-ngmodule` runs the
onboarding path (`--preset full`) on a fresh `ng new --standalone=false`
app with routing. It runs `ng add` twice (the second must change
nothing), then `ng build` and `ng test`, checks both outlets in the DOM,
and runs a browser smoke. No starter is expected there.

The ng add bundle budget moves from 288 to 320 KB minified (gzip stays
at 96 KB). The NgModule spec bodies take the catalog from 21 to 39 KB;
ng add measures 308,306 B minified and 92,328 B gzip.

### Does this PR introduce a breaking change?

- [ ] Yes
- [x] No

Migration:

### Other information

**Branch protection:** the new job `angular-21-ngmodule` should be added
to the required checks.

Note text change: the custom-spec note now reads "is not an untouched ng
new spec" instead of "is not the untouched standalone ng new spec",
since the NgModule spec is supported now.

Verified locally:

- every `consumer-compat` row: 21.1.0 minimal; 21 full, zone, ssr; 22
full, minimal; 21 schematics `--stale-core previous --preset full`; 21
and 22 onboarding `--preset full`; the new 21 ngmodule `--preset full`;
- `build:libs`, `docs:json`, `lint`, `npm test`, `test:schematics`,
`test:scripts`, `build:examples`, `pack-libs --check`;
- by hand on fresh apps: CLI 21 NgModule with routing on and off, zone
and zoneless, green with the edited spec; CLI 22.2.3 red with any
outlet; green with `@angular/build@22.3.0-next.2`, or on 22.2.3 with
`import './app-module'` in the spec.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: @angular/build target: minor This PR is targeted for the next minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ng test fails with NG8001 in a new NgModule app on 22.2.0

3 participants