Skip to content

feat: ng add / ng update schematics for jest, custom-esbuild, custom-webpack#2267

Open
just-jeb wants to merge 48 commits into
release/v22from
feat/builder-schematics
Open

feat: ng add / ng update schematics for jest, custom-esbuild, custom-webpack#2267
just-jeb wants to merge 48 commits into
release/v22from
feat/builder-schematics

Conversation

@just-jeb
Copy link
Copy Markdown
Owner

@just-jeb just-jeb commented Jun 3, 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

(Also carries a few build/test fixes required to make the schematics correct on Angular 22 — detailed below.)

What is the current behavior?

Issue Number: N/A

The builder packages ship no Angular CLI schematics. Users wire @angular-builders/* builders into angular.json by hand, and there is no ng update path for the jest builder's breaking changes.

What is the new behavior?

Adds ng add (and, for jest, ng update) schematics across the builders, plus an end-to-end test layer that drives the real Angular CLI against inline-generated apps.

Schematics (built on a shared @angular-builders/common/schematics kernel):

  • jestng add detects the current test runner (Karma / Vitest) and switches the test target to @angular-builders/jest:run, cleans up Karma config/deps, fixes tsconfig.spec.json, and sets zoneless by detection. ng update ships a heavy @21 config migration + a @22 advisory.
  • custom-esbuildng add rewrites esbuild build/serve targets, guards webpack builds (advisory + --from-webpack escape hatch), and handles the Vitest test target (--unit-test).
  • custom-webpackng add rewrites build/serve and scaffolds a starter webpack.config.js.

E2E layer (scripts/e2e-ng-add.js, scripts/e2e-jest-migration.js) — six cases wired into the existing integration matrix (packages/*/tests/integration.js). Each generates a fixture inline with ng new, runs the real ng add/ng update, then asserts and runs real ng build/ng test. Fixtures are generated, not committed (no drift across majors); the package manager is neutralised during ng add so install tasks can't mutate the workspace.

Angular 22 fixes surfaced by the e2e:

  • detectTestBuilder now reads the runner option — Angular 22 unifies Karma/Vitest under @angular/build:unit-test rather than a dedicated :karma builder.
  • jest ng add no longer leaks the previous builder's runner/buildTarget options into the Jest builder (setBuilderForTarget gains replaceOptions).
  • jest @21 migration converts testPathPattern (string) → testPathPatterns (string array) per the Jest 30 schema.
  • Schematics build uses Node16 module resolution (TypeScript 6 / Angular 22 clean; drops a paths workaround).
  • custom-webpack now declares its @angular-devkit/schematics / @schematics/angular deps.

Does this PR introduce a breaking change?

[x] No

Schematics are additive. The jest @21/@22 migrations are the handlers for Angular 22's breaking changes, surfaced via ng update; documented in MIGRATION.MD / the upgrade runbook.

Other information

  • Stacked on feat/v22-upgrade (this PR targets that branch) — it needs the Angular 22.0.0-rc.2 toolchain to validate.
  • All packages build + unit-test green under Angular 22.0.0-rc.2; all 6 e2e cases pass through the local harness (~14s).

just-jeb added 29 commits June 3, 2026 14:38
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).
…tion 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)
- 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
- 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).
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.
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+).
just-jeb added 19 commits June 3, 2026 14:39
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.
…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.
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).
…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.
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.
…cs/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.
@just-jeb just-jeb force-pushed the feat/builder-schematics branch from 20c5ceb to 61f4649 Compare June 3, 2026 12:41
@just-jeb just-jeb changed the base branch from feat/v22-upgrade to release/v22 June 3, 2026 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant