Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,6 @@ Lerna-Lite manages versioning and publishing. Packages use **independent version

## Angular Major Version Upgrade Process

**Runbook:** [`docs/runbooks/angular-major-upgrade.md`](docs/runbooks/angular-major-upgrade.md) — step-by-step procedure (branch, bump, `ng update` examples, fix breakage, migration coverage, RC→GA).

The upgrade process is not fully automated. It partially originates from Renovate PRs, but each Angular major update also requires running `ng update` for apps in `examples/`. The goal is full automation but it currently requires manual steps. Work sometimes begins against Angular RCs to have PRs ready, so only a final version update is needed when the major release lands. The packages layer is where Angular CLI internal API changes are felt most -- internal API moves (renamed/moved packages or exports) and schema changes to builder options are the most common breakages. (Source: SME interview, Jeb, 2026-02-16)
50 changes: 50 additions & 0 deletions MIGRATION.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,53 @@
# Migration from version 21 to version 22

## What's new: automated `ng add` and `ng update`

Version 22 introduces Angular schematics for the builder packages, so you no longer have to wire builders into `angular.json` by hand:

- `ng add @angular-builders/jest` — sets up the Jest builder as your `ng test` runner. Detects an existing Karma, Vitest (Angular 22's new default), or Jest setup and migrates the test target accordingly.
- `ng add @angular-builders/custom-esbuild` / `ng add @angular-builders/custom-webpack` — scaffold the custom build setup.
- `ng update @angular-builders/jest` — runs the version migrations below automatically.

The `ng update` migration window is `(from, to]`, so a project on **any version from 17 through 21 can jump straight to 22** in a single `ng update @angular-builders/jest`. The v21 migration (dep bumps, `Node16` tsconfig, option renames/removals — see the v20→v21 section below) and the v22 advisory both run in order.

## Breaking Changes

### Jest builder: `isolatedModules` now defaults to `true`

`ts-jest` `isolatedModules` now defaults to **`true`** (previously implicitly `false`) for significantly faster compilation. This is a behavior change in the builder itself — it is **not** rewritten into your config.

Impact: a `const enum` shared across files, and type-only re-exports without the `type` modifier, will now error under `isolatedModules`.

- To keep the old behavior, set `isolatedModules: false` in your Jest config.
- Otherwise, fix the call sites: convert `const enum` to a regular `enum` (or `as const`), and use `export type` for type-only re-exports.

`ng update @angular-builders/jest` does **not** change this for you — it runs an advisory migration that warns and lists any `const enum` it finds. The new default is intentional.

### Jest builder: coverage output is now scoped per-project

`coverageDirectory` now defaults to `<projectRoot>/coverage` instead of `./coverage`. In multi-project workspaces this prevents projects from overwriting each other's coverage reports.

Impact: update any CI/tooling that reads a hardcoded `./coverage/` path. The `ng update` advisory migration warns and lists affected projects.

## Custom ESBuild builder

- No breaking changes (except for updating to Angular 22).
- `ng add @angular-builders/custom-esbuild` is now available.

## Custom Webpack builder

- No breaking changes (except for updating to Angular 22).
- `ng add @angular-builders/custom-webpack` is now available (scaffolds a `webpack.config.js`).

## Angular 22 test runner note (Karma vs Vitest)

Angular 22 unifies test runners under the `@angular/build:unit-test` builder with a `runner` option (`"vitest"` is the default for new apps; `"karma"` is still supported). `@angular-builders/jest` replaces this with its own `:run` target:

- `ng add @angular-builders/jest` detects a Karma, Vitest, or existing Jest setup and rewrites the test target to the Jest builder. For a Vitest project it also fixes `tsconfig.spec.json` types (`vitest` → `jest`) and advises on any `vi.*` usages in your specs.
- Karma is **not removed** in Angular 22 — it's deprecated. Running `ng update @angular/core` to v22 keeps existing Karma users on Karma (Angular rewrites the target to `@angular/build:unit-test` with `runner: "karma"`). Migrating to Jest stays opt-in via `ng add @angular-builders/jest`.

---

# Migration from version 20 to version 21

## Breaking Changes
Expand Down
15 changes: 15 additions & 0 deletions __mocks__/ora.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Stub for the ESM-only `ora` package. Tests never exercise the
// package-manager task executor that uses ora, so a no-op spinner is enough.
'use strict';

function ora() {
return {
start: () => ({ stop: () => {}, succeed: () => {}, fail: () => {} }),
stop: () => {},
succeed: () => {},
fail: () => {},
};
}

module.exports = ora;
module.exports.default = ora;
105 changes: 105 additions & 0 deletions docs/runbooks/angular-major-upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Runbook: Angular Major Version Upgrade

> How to move all `@angular-builders/*` packages and example apps to a new Angular major. Runs ~twice a year. Designed to be executed by an agent following these steps — the scripts handle the mechanical bumps; the agent handles the judgment (resolving internal-API breakage, fixing example apps, migration coverage).

## When to run

A new Angular major reaches **RC** (start prework so PRs are ready when GA lands) or **GA**. Builder major versions track the Angular major 1:1.

## Roles

- **Mechanical (scripts):** `scripts/update-package.js` (bumps builder Angular dep ranges) and `scripts/update-example.js` (runs `ng update` per example). Invoked via `yarn update:packages <N>` / `yarn update:examples <N>`, or the `update.yml` workflow dispatch.
- **Judgment (agent):** resolve internal-API moves and schema changes that break builders, fix example apps, ensure migration coverage, handle RC→GA.

## Preconditions

- Clean root install: `yarn install` (a stale `node_modules` breaks Husky hooks — e.g. `yarn commitlint` not resolving — and builds).
- Node satisfies the target Angular's requirement (Angular 20+: `^20.19 || ^22.12 || >=24`).

## Procedure

### 1. Branch

Create a long-lived integration branch off master:

```bash
git checkout master && git checkout -b release/v<N>
```

All v`<N>`-bound feature branches and held breaking PRs (`breaking-change` label) **base on / rebase onto** this branch. When GA lands and the branch is green, it merges to master.

### 2. Bump builder Angular ranges

```bash
yarn update:packages <N> # e.g. 22
```

`update-package.js` sets stable deps (`@angular/build`, `@angular-devkit/build-angular`, `@angular-devkit/core`, `@angular/compiler[-cli]`) to `^<N>.0.0` and `@angular-devkit/architect` to `>=0.<N>00.0 < 0.<N+1>00.0`.

> **RC caveat (current tooling gap):** the script takes an integer major and writes `^<N>.0.0`, which by semver **excludes** prereleases like `<N>.0.0-rc.2`. For RC prework you must either (a) temporarily pin the explicit RC versions, or (b) extend `update-package.js`/`update-example.js` to accept an explicit version/tag (recommended — makes RC prework first-class and agent-runnable). `@angular/cli@next` resolves to the current RC; `@angular/cli@<N>` resolves to the latest **stable** `<N>` (nonexistent during RC).

### 3. Update example apps

```bash
yarn update:examples <N>
```

Runs `npx @angular/cli@<N> update @angular/core@<N> @angular/cli@<N> --create-commits` in each `examples/*` app (~7: `custom-esbuild` ×2, `custom-webpack` ×3, `jest` ×2). For RC, target `@next`/explicit RC per the caveat above. Review each app's generated commit.

### 4. Install + build + validate

```bash
yarn install
yarn build:packages:all
yarn test:local # integration matrix against examples/*
```

**This is the judgment core.** Expect breakage in the **packages layer** — internal Angular API moves (renamed/moved packages or exports) and builder-option schema changes are the most common (see `packages/AGENTS.md`). Fix per-package, rebuild, re-run until green.

### 5. Migration coverage (the breaking-change loop)

For the major, **enumerate every `breaking-change`-labeled PR** targeting it (`gh pr list --label breaking-change`). For **each** breaking change, confirm BOTH exist:

1. a migration step (auto-transform or logged advisory) in the builder's `migrations.json` (see the builder-schematics design + `@angular-builders/common/schematics`), and
2. a `MIGRATION.MD` entry for the major, annotated ✅ automated by `ng update` vs ⚠️ manual.

This is an **invariant**: a breaking change must not land in a major without both. CHANGELOGs are auto-generated from commits (orthogonal).

#### RC-validated: multi-major `ng update` window (v22)

Validated against `@angular/cli@22.0.0-rc.2` on `2026-06-03` via the `ng-update-jest-v21-smoke` e2e
(`scripts/e2e-jest-migration.js`):

- `ng update @angular-builders/jest --migrate-only --from=20.0.0 --to=22.0.0` runs **all** migrations
whose version falls in the `(from, to]` window in one step — observed `migration-v21` (the heavy
config transform) **and** the `migration-v22` advisory both firing. So a user on an old major who
jumps straight to 22 gets the spanned migrations; they are not skipped.
- Supported flow for older users: upgrade the Angular framework to 22, then run
`ng update @angular-builders/jest` once (or `--migrate-only --from=<old>` to run only the builder's
migrations). The post-migration config builds and tests green under v22 — proven by the e2e, which
runs `ng build` + `ng test` on the migrated app.
- E2E coverage of the migration output itself lives in `packages/jest/tests/integration.js`
(`ng-update-jest-v21-smoke`); the ng-add paths are the `ng-add-*` entries there and in the
`custom-esbuild`/`custom-webpack` integration files.

### 6. Stack feature work

Develop/rebase v`<N>`-bound features (e.g. schematics) and held breaking PRs on `release/v<N>`.

### 7. RC → GA

When the major reaches GA: bump ranges from RC to final (`update:packages <N>` now resolves stable), re-run install + matrix, finalize `MIGRATION.MD`, merge `release/v<N>` → master, then **graduate-publish** (CI dispatch `release_type: graduate`).

## Known breakage hotspots

- **Internal API moves** in `@angular/build` / `@angular-devkit/*` — imports the builders rely on get renamed/relocated (~every major). Felt in `packages/*/src`.
- **Schema/option changes** in Angular's base builder schemas — affects `custom-esbuild`/`custom-webpack` schema merging (`merge-schemes.ts`) and option pass-through.
- **Jest/test toolchain majors** (jest-preset-angular, Jest) — historically large (see jest `@21` migration).

## References

- Scripts: `scripts/update-package.js`, `scripts/update-example.js`, `scripts/AGENTS.md`
- Workflow: `.github/workflows/update.yml` (manual dispatch, input = version)
- Migration design: `docs/superpowers/specs/2026-06-01-builder-schematics-design.md`
- Per-major user guide: `MIGRATION.MD`
- Breakage context: `packages/AGENTS.md`, root `AGENTS.md` → "Angular Major Version Upgrade Process"
Loading
Loading