Skip to content

feat!: Angular 22 support (release/v22)#2284

Open
just-jeb wants to merge 8 commits into
masterfrom
release/v22
Open

feat!: Angular 22 support (release/v22)#2284
just-jeb wants to merge 8 commits into
masterfrom
release/v22

Conversation

@just-jeb
Copy link
Copy Markdown
Owner

@just-jeb just-jeb commented Jun 7, 2026

PR Checklist

  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

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

What is the current behavior?

Builders track Angular 21 (@angular/build ^21). There is no published support for Angular 22, and installation/upgrade for the Jest builder is still a manual process.

This is the long-lived Angular 22 release branch. It branched off master at bc2e3819 and carries the v22 upgrade work.

What is the new behavior?

Upgrades all builders + example apps to Angular 22 (currently 22.0.0-rc.2) and lands the supporting tooling/migrations.

Commits on this branch (vs merge-base bc2e3819):

Closes #1899, #1009, #22 once released under the v22 line.

Does this PR introduce a breaking change?

[x] Yes
[ ] No

Major version bump aligned with Angular 22. Per project invariant, a v22 builder requires Angular CLI 22. Additional breaking changes within the line:

  • isolatedModules now defaults to true (Jest) — faster compilation; some projects relying on full-program type-checking during transform may need to adjust.
  • Jest coverage output is now scoped per-project in multi-project workspaces.

Migration path: see the v21 → v22 migration section added in #2273 and the new upgrade runbook.

Other information

⚠️ Draft — not ready to merge.

Opened now for visibility and to run CI against the v22 work.

just-jeb and others added 7 commits June 1, 2026 13:10
* chore(deps): bump Angular builder ranges to 22.0.0-rc.2

Extend update-package.js/update-example.js to accept explicit version
strings and dist-tags (required for RC prework), and bump all
@angular-builders/* Angular dependency ranges to 22.0.0-rc.2.

* chore(deps): upgrade example apps to Angular 22.0.0-rc.2 and fix TS6 build breakage

- Bump example apps to Angular 22.0.0-rc.2 and TypeScript 6.0.3.
- Add explicit rootDir to package tsconfigs (TS6 TS5011 now requires it
  when emitting with outDir).
- Add @types/node + types:[node] to standalone packages bazel/timestamp
  (TS6 no longer auto-includes node types without a transitive trigger).
- jest: move types into compilerOptions and pin to @types/jest so the
  ambient jest global resolves under TS6 (the bare 'jest' entry now
  resolves to the runtime jest package, which lacks the global); exclude
  spec files from the lib build so they stop leaking into dist.

* fix(examples): resolve TS6 breakage in example apps for Angular 22

- Add ignoreDeprecations: "6.0" to app tsconfigs using deprecated
  options (baseUrl, downlevelIteration) that TS6 now errors on.
- Add rootDir to Cypress e2e tsconfigs so ts-loader stops failing with
  TS5011 under TS6.
- full-cycle-app webpack configs: switch html-webpack-plugin to a default
  import (TS6 rejects constructing a namespace import) and guard the
  optional cfg.plugins array.
…webpack (#2267)

* docs(schematics): add v22 builder ng-add + ng-update design spec

* docs(schematics): cover v22-held breaking PRs (#2191/#2212) and MIGRATION.MD pairing

* docs(schematics): add Plan 0 — common/schematics core + packaging

* docs(schematics): add Plans 01-03 — jest, custom-esbuild, custom-webpack

Builder implementation plans referencing Plan 0's locked common/schematics
API contract. Reviewed for consistency: all import the shared helpers, none
redefine them, no raw fs (tree-based edits only).

* docs(schematics): add v17→v22 migration coverage caveat + 2c/2d execution checklist

- jest plan: document the stepwise-through-v21 skip + single-step old→22 flow
  and RC-time multi-major ng update validation
- new 2c/2d checklist: holds rebase, MIGRATION.MD pairing, RC validations,
  and the two e2e testing gaps (ng add integration, jest @21 post-migration smoke)

* docs(schematics): amend design for Karma roadmap (not removed in v22)

- spec §12: Karma deprecated-not-removed in v22; drop custom-webpack @22
  migration + hold #2260; add jest Vitest→Jest path; add custom-esbuild
  webpack-build guard; fix e2e fixtures
- spec §4.3/§5: custom-webpack ng-add-only; v22 breaking set = #2191+#2212
- 2c/2d checklist: #2260 off the v22 holds, Karma fixture via ng new, e2e cases

* docs(schematics): apply §12 amendments to builder plans

- jest (Plan 01): add Vitest→Jest ng-add path (Task 4b), keep Karma→Jest
- custom-esbuild (Plan 02): add webpack-build guard + --from-webpack (Task 3b)
- custom-webpack (Plan 03): drop @22 Karma-removal migration, ng-add only

Reviewed: no shared-helper redefinition, no raw fs in schematic code,
webpack @22 migration fully removed (remaining refs assert its absence).

* docs(schematics): add Plan 04 — schematics integration/e2e tests

ng add e2e per builder (jest Karma→Jest + Vitest→Jest, esbuild build +
webpack guard, webpack scaffold) via npm pack→ng add tarball into the existing
integration matrix; jest @21 post-migration build smoke; RC multi-major
ng update validation.

* build(common): add schematics subpath packaging (tsconfig + exports + copy)

* feat(common): add schematics version helpers

* feat(common): add SchematicTestHarness for schematics unit tests

Also stubs ESM-only `ora` (via @angular-devkit/schematics task executor
chain) with a moduleNameMapper in jest-ut.config.js, and resolves
@schematics/angular/collection.json via package.json to bypass the
exports map (required in Node 22+).

* feat(common): add workspace detection helpers for schematics

* feat(common): add composable schematics rule factories

* feat(common): export schematics core via ./schematics subpath

* build(jest): add schematics packaging (tsconfig + fields + copy)

* feat(jest): add ng-add collection + schema (project flag only)

* feat(jest): ng-add adds jest stack and rewrites test target

* feat(jest): declare ng-update migrations manifest (v21, v22)

* feat(jest): v21 migration bumps deps and applies Node16 tsconfig

* feat(jest): v22 advisory migration (isolatedModules, coverage path)

* build(custom-esbuild): add schematics packaging (tsconfig + ng-add fields + copy)

* feat(custom-esbuild): add ng-add collection + schema manifests

* feat(custom-esbuild): ng-add rewrites build/serve preserving options

* feat(custom-esbuild): ng-add guards webpack builds, adds --from-webpack (spec §12.3)

* feat(custom-esbuild): ng-add auto-rewrites Vitest test target with buildTarget

* feat(custom-esbuild): ng-add leaves Karma/Jest tests, logs unit-test advisory

* feat(custom-esbuild): ng-add --unit-test force-creates Vitest target

* test(custom-esbuild): assert ng-add idempotency

* build(custom-esbuild): verify schematics build

* build(custom-webpack): add schematics packaging (tsconfig + ng-add field)

* feat(custom-webpack): add ng-add schema (project flag, no prompts)

* feat(custom-webpack): add starter webpack.config scaffold template

* feat(custom-webpack): add ng-add (build/serve rewrite + config scaffold)

* test(custom-webpack): verify schematics build + no-migrations invariant

* fix(jest): add WorkspaceDefinition casts and JsonValue fixes for schematics tsc

* test(schematics): add local-only ng add e2e harness helpers

* build(schematics): use Node16 module resolution for schematics build

The schematics tsconfig used module:commonjs + moduleResolution:node (node10).
node10 is deprecated in TypeScript 6 (Angular 22) AND ignores package exports maps —
the latter is why each package's schematics tsconfig carried a 'paths' override just
to resolve @angular-builders/common/schematics.

Align with the main builder tsconfig (module/moduleResolution Node16): all packages are
CJS (no type:module), so Node16 still emits CommonJS as schematics require, while honoring
exports maps. This drops the paths workaround in all three packages and needs no
deprecation suppression. Add types:[node] since TS6 no longer auto-includes node ambients.

* test(schematics): generate ng-add e2e fixtures inline, default to no-install collection run

ng add e2e now generates the target app inline with the workspace CLI (ng new) instead of
committing a fresh-scaffold fixture — self-describing and immune to fixture drift across
Angular majors. Default ng add path resolves the collection from the workspace-linked
package with --skip-install (the schematic under test runs fully; no package-manager step,
so it can't mutate the symlinked node_modules). npm-pack tarball path kept behind
useTarball:true for a future isolated-install CI.

* fix(schematics): handle Angular 22 unified :unit-test runner model

Angular 22 expresses Karma and Vitest as the same @angular/build:unit-test builder,
distinguished by an options.runner ('karma' | 'vitest') field, rather than a dedicated
:karma builder (which still exists for webpack projects). The schematics, written for the
v21 model, broke on v22:

- detectTestBuilder classified every :unit-test target as Vitest, so Karma on a default
  (esbuild) v22 app was invisible. Now it reads options.runner for :unit-test builders and
  keeps the :karma suffix branch for webpack projects.
- jest ng-add merged {zoneless} onto the previous target's options, leaving the foreign
  builder's runner/buildTarget behind; the Jest builder then forwarded --runner to the Jest
  CLI ('Runner is not a constructor'). setBuilderForTarget gains an opt-in replaceOptions
  (default still merges, preserving other callers); jest ng-add uses it to start from a clean
  Jest option set.

Surfaced by the Plan 04 ng-add e2e (ng new --test-runner karma -> ng add -> ng test).

* test(jest): add Karma->Jest ng add e2e (inline fixture, real ng test)

* test(jest): add Vitest->Jest ng add e2e (ng build + ng test green)

* test(custom-esbuild): add build/serve rewrite ng add e2e

* test(custom-esbuild): add webpack-build guard ng add e2e

* test(custom-webpack): add build/serve rewrite + scaffold ng add e2e

* test(jest): add @21 migration post-build smoke; fix testPathPatterns string->array

The @21 migration renamed testPathPattern -> testPathPatterns but carried the value over
verbatim. Jest 30's testPathPatterns is a string array, so a migrated config failed builder
schema validation ('testPathPatterns must be array') at ng test. Wrap a carried-over string
in an array. Surfaced by the new ng update --migrate-only post-build smoke (Plan 04 Task 6),
which seeds a pre-21 config on an inline-generated app, runs the migration, then ng build + ng test.

* docs(runbook): record RC-validated multi-major ng update window

* test(schematics): neutralise package manager during ng add via PATH shim

The ng add collection run can't use --skip-install (ng add forwards it to the schematic, whose
schema rejects unknown options). Instead prepend a no-op npm/yarn/pnpm shim to PATH for the ng add
spawn so the schematic's NodePackageInstallTask runs harmlessly — it cannot write through the
workdir's node_modules symlink into the workspace. Every tree transform still happens; ng build/test
afterwards resolve from the workspace-linked modules.

* fix(custom-webpack): declare @angular-devkit/schematics and @schematics/angular deps

custom-webpack's ng-add imports from both packages but didn't declare them, relying on them
being present transitively via the user's @angular/cli install. Declare them explicitly
(matching jest and custom-esbuild) so ng add resolves regardless of hoisting.
…ixes #1899) (#2191)

* fix(jest): default isolatedModules to true for faster compilation (fixes #1899)

Angular 19 introduced signals, new control flow syntax, and standalone-by-default
components. These produce significantly more complex TypeScript that causes the
ts-jest language service (used when isolatedModules: false) to build a full
cross-file Program per test file, resulting in severe slowdowns (reports of
2 min → 15 min test runs).

Root cause: when isolatedModules is false (previously the implicit default),
ts-jest instantiates a TypeScript LanguageService and rebuilds a full Program
for each file. Angular 19+'s richer type surface makes this path prohibitively
slow.

Fix: set isolatedModules: true in the builder's default transformer options.
This switches ts-jest to its fast per-file transpile path, matching the
recommendation from jest-preset-angular's own example apps since v14.4.0.

Cross-file type checking is better served by tsc --noEmit or ng build.
Users who need the previous behaviour can opt out in their jest.config.ts:

  transform: {
    '^.+\.(ts|js|mjs|html|svg)$': ['jest-preset-angular', { isolatedModules: false }]
  }

BREAKING CHANGE: isolatedModules now defaults to true. This disables
cross-file TypeScript type checking during jest runs. Targeted for the
next major version.

* test(jest): replace config-shape assertions with behavioral integration test

Remove isolatedModules:true assertions from the unit spec — they tested
implementation details (object shape), not user-visible behavior. Replace
with targeted assertions on tsconfig path resolution, which is the actual
behavioral contract of the resolver.

Add isolated-modules-default integration test entry that proves Angular
component tests still pass end-to-end with isolatedModules:true active,
providing the behavioral regression guard for #1899.

---------

Co-authored-by: Houston (Jeb's AI) <houston@base44.com>
…aces (fixes #1009) (#2212)

* fix(jest): scope coverage output per-project in multi-project workspaces (fixes #1009)

* fix(jest): scope coverageDirectory per project to prevent output collisions (#1009)

When running ng test in a multi-project Angular workspace, each project's coverage
output was written to the same directory (e.g. root ./coverage/), causing each
successive run to overwrite the previous project's coverage report.

Fix: set coverageDirectory in DefaultConfigResolver.resolveForProject() to
<projectRoot>/coverage so each Angular project's coverage lands in its own
isolated output directory.

This is a project-level default that can still be overridden per-project via
the jest.config.js customization mechanism.

Also removes erroneous debug console.log statements from jest-configuration-builder.ts
and fixes the scopeOutputDirectoriesForProjects approach, which incorrectly mutated
the Jest 'projects' array (breaking --find-related-tests and project path resolution).

Adds: unit test for coverageDirectory scoping, integration test validate-coverage.js

* test(jest): remove impl-detail coverageDirectory spec, keep behavioral integration test

The unit test asserting config.coverageDirectory field value is an
implementation detail check. The behavioral proof — that coverage
files actually land under projects/<name>/coverage/ on disk — is
covered by the integration test in validate-coverage.js. Remove the
unit test to avoid false confidence from a config-shape assertion.

---------

Co-authored-by: Houston <houston@superagent.local>
Co-authored-by: Houston <houston@base44.com>
Sync the v21 maintenance line (renovate bumps, the #1859 --findRelatedTests
positional-args fix (#2237), and other v21 fixes) into the Angular 22 branch.

Conflicts:
- examples/*/package.json: kept v22 Angular versions (22.0.0-rc.2); master's
  incidental devDep bumps auto-merged outside the conflict hunks.
- yarn.lock: kept v22 lockfile, reconciled to merged manifests.
- package.json: bumped root @angular/build ^21 -> ^22.0.0-rc.2 (added on master
  via #2239) so merge-schemes.ts resolves Angular 22's base schema.
@just-jeb just-jeb marked this pull request as ready for review June 7, 2026 13:51
myabc added a commit to opf/openproject that referenced this pull request Jun 7, 2026
Necessary until a compatible version of @angular-builders/custom-esbuild
is released. See just-jeb/angular-builders#2284
myabc added a commit to opf/openproject that referenced this pull request Jun 7, 2026
Necessary until a compatible version of @angular-builders/custom-esbuild
is released. See just-jeb/angular-builders#2284
myabc added a commit to opf/openproject that referenced this pull request Jun 7, 2026
Necessary until a compatible version of @angular-builders/custom-esbuild
is released. See just-jeb/angular-builders#2284
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

1 participant