angular-compat-matrix 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nizamudeen S
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,407 @@
1
- # Temporary Holding Version
1
+ # angular-compat-matrix
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Which Angular versions does your published library really work with?
4
+
5
+ Run it with `npx angular-compat-matrix`, or install the package and run `ng-matrix`: both names start the same command.
6
+
7
+ ng-matrix builds and packs your Angular library, installs the tarball into a fresh app for each Angular major (created with that major's own CLI), builds it, and reports a compatibility matrix. It tests the exact package your users get, not your local source.
8
+
9
+ ```
10
+ $ npx angular-compat-matrix
11
+
12
+ my-dropdown@3.0.0 (compiled with Angular 21.2.0; highest minVersion 20.0.0)
13
+
14
+ peer range install partial AOT build strict types
15
+ Angular 19 ✓ ✓ ✗ — —
16
+ Angular 20 ✓ ✓ ✓ ✓ ✗
17
+ Angular 21 ✓ ✓ ✓ ✓ ✓
18
+ Angular 22 ✗ ✓ ✓ ✓ ✓
19
+
20
+ ✗ 19: partial: Needs Angular 20.0.0 or later: highest minVersion 20.0.0, in
21
+ MyDropdownComponent. Build the library with the oldest Angular you support, or take
22
+ this version out of the peer range.
23
+ ✗ 20: strict types: Fails with strictTemplates: true and skipLibCheck: false.
24
+ ! 22: works, but peerDependencies doesn't allow it (@angular/core "^19.0.0 || ^20.0.0 ||
25
+ ^21.0.0"). Widen the range to include it.
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Quick Start
31
+
32
+ Run it in your library's workspace: an Angular CLI workspace with a library project, or any folder with `ng-package.json`.
33
+
34
+ ```sh
35
+ # Check supported majors plus every major your peer range claims
36
+ npx angular-compat-matrix
37
+
38
+ # Check a specific range or list of majors
39
+ npx angular-compat-matrix --versions 18-22
40
+ npx angular-compat-matrix --versions 20,22
41
+
42
+ # Check one library in a workspace with several
43
+ npx angular-compat-matrix --project my-lib
44
+
45
+ # Check a published package from npm (no local build needed)
46
+ npx angular-compat-matrix --package primeng@19.0.0
47
+
48
+ # Generate Markdown for your README or GitHub Actions summary
49
+ npx angular-compat-matrix --format markdown
50
+
51
+ # Machine-readable JSON output for CI pipelines
52
+ npx angular-compat-matrix --json
53
+
54
+ # Verbose output (shows exact commands executed and full failure logs)
55
+ npx angular-compat-matrix --verbose
56
+ ```
57
+
58
+ ---
59
+
60
+ ## How to Read the Matrix
61
+
62
+ The matrix tests each Angular major in order. A failure on any step marks subsequent checks `—`, ensuring the first true incompatibility is reported clearly.
63
+
64
+ ### Symbols
65
+
66
+ | Terminal | Markdown | Meaning |
67
+ | :---: | :---: | :--- |
68
+ | `✓` | `✅` | **Passed:** The check succeeded. |
69
+ | `✗` | `❌` | **Failed:** The check failed. |
70
+ | `✓?` | `✅*` | **Advisory pass:** Passed on `minVersion` alone, but because the library was compiled with a newer Angular major, `minVersion` has been historically unreliable. The build check decides compatibility. |
71
+ | `—` | `—` | **Not run:** Skipped because an earlier check failed. |
72
+ | `·` | `n/a` | **None:** Nothing to check (e.g. no `@angular/*` peer declared). |
73
+ | `!` | `⚠️` | **Setup error:** ng-matrix itself could not set up the app on this machine (e.g. disk full, network timeout). This is not a library failure. |
74
+
75
+ ---
76
+
77
+ ## What It Checks
78
+
79
+ | Check | What it asks | How it verifies |
80
+ | --- | --- | --- |
81
+ | **Peer range** | Does your package claim this version? | Compares every declared `@angular/*` peer (e.g. `@angular/core`, `@angular/cdk`, `@angular/material`). Never stops other checks. |
82
+ | **Install** | Does it install cleanly without peer conflicts? | Installs the packed tarball into a fresh Angular app using your package manager (`npm` with `--legacy-peer-deps` + `npm ls`, or `pnpm` with `strictPeerDependencies: false`). For versions outside your peer range, expected peer conflicts are safely ignored. |
83
+ | **Partial** | Can this version's Angular linker load the compiled code? | Scans every `ɵɵngDeclare…` call in the published code for `minVersion`. A failure is absolute proof of incompatibility. |
84
+ | **AOT build** | Does a production app build with your library? | Runs `ng build --configuration production` (`strictTemplates: false`, `skipLibCheck: true`) on a smoke app using every public component, directive, pipe, service, and entry point. |
85
+ | **Strict types** | Do consumer templates type-check under strict settings? | Re-runs the build with `strictTemplates: true` and `skipLibCheck: false`. Catches template type errors and invalid typings. (Errors strictly inside third-party `node_modules/**/*.d.ts` are reported as warnings and do not fail the check). |
86
+ | **Tests** | Do your own unit tests pass on this version? | (Opt-in with `--tests`): Copies your workspace, swaps Angular dependencies to the target major, and executes your test command (`npm test` or `--test-command`). |
87
+
88
+ ### The Generated Smoke App
89
+
90
+ When not using `--consumer`, ng-matrix inspects your package typings and generates a standalone smoke app:
91
+ - Imports all entry points exposed in your `package.json` `exports` map.
92
+ - Instantiates each component, directive, and pipe in its own isolated component.
93
+ - Supplies correctly-typed mock values for required `@Input()` fields.
94
+ - Uses attribute selectors bare (`<div myFlag>`) when they accept booleans or strings, matching real user patterns.
95
+ - Handles undeclared dependencies in secondary entry points: if a secondary entry point (e.g. `my-lib/chart`) fails because an undeclared extra (like `chart.js`) is missing, ng-matrix omits that entry point and warns you rather than failing the whole library. If the package's *main* entry point (`.`) imports an undeclared package, AOT fails immediately with a clear explanation.
96
+
97
+ If your library requires specialized providers, wrappers, or mock services, supply your own example app with `--consumer ./path-to-app`.
98
+
99
+ ---
100
+
101
+ ## Install-Script Safety Default & `--allow-scripts`
102
+
103
+ By default, ng-matrix **blocks arbitrary install scripts** (`preinstall`, `install`, `postinstall`) during `npm` and `pnpm` installations.
104
+
105
+ Only native build tools essential to the Angular toolchain are permitted to run:
106
+ - `esbuild`
107
+ - `@parcel/watcher`
108
+ - `lmdb`
109
+ - `@swc/core`
110
+
111
+ If your library or one of its dependencies requires an install script to compile native bindings, opt in explicitly:
112
+ ```sh
113
+ npx angular-compat-matrix --allow-scripts
114
+ ```
115
+
116
+ If an installation or build fails while scripts were blocked, ng-matrix explicitly lists which packages were skipped in the output findings so you know whether `--allow-scripts` is needed.
117
+
118
+ ---
119
+
120
+ ## Cache Management
121
+
122
+ ng-matrix caches npm registry metadata and downloaded tools (such as pinned pnpm binaries) to keep runs fast and enable `--offline` usage.
123
+
124
+ ```sh
125
+ # View cache location, disk usage, and file count
126
+ ng-matrix cache
127
+
128
+ # Clear the cache entirely
129
+ ng-matrix cache clean
130
+ ```
131
+
132
+ ### Cache Locations by OS
133
+
134
+ - **Linux:** `$XDG_CACHE_HOME/ng-matrix` or `~/.cache/ng-matrix`
135
+ - **macOS:** `~/.cache/ng-matrix` (or `$XDG_CACHE_HOME/ng-matrix`)
136
+ - **Windows:** `%LOCALAPPDATA%\ng-matrix\Cache`
137
+ - **Custom Location:** Set the `NG_MATRIX_CACHE` environment variable to override the default on any OS.
138
+
139
+ ---
140
+
141
+ ## Exit Codes
142
+
143
+ ng-matrix returns standard POSIX exit codes suitable for CI pipelines:
144
+
145
+ - **`0` (Success):** Every version claimed by the library's peer dependencies passed all checks. (If the library declares no `@angular/*` peer dependencies, 0 is returned if any checked version passes).
146
+ - **`1` (Broken Promise):** At least one Angular major claimed by your `peerDependencies` failed a check (install, partial, AOT, strict types, or tests).
147
+ - **`2` (Environment / Setup Error):** ng-matrix could not check a version due to machine or network failure (e.g. disk full ENOSPC, network down, step timeout, invalid CLI options).
148
+
149
+ ---
150
+
151
+ ## In CI
152
+
153
+ ### GitHub Actions
154
+
155
+ Add a step to your workflow to verify compatibility and post the matrix directly to your workflow run summary:
156
+
157
+ ```yaml
158
+ name: Compatibility Matrix
159
+
160
+ on:
161
+ push:
162
+ branches: [main]
163
+ pull_request:
164
+
165
+ jobs:
166
+ compat:
167
+ runs-on: ubuntu-latest
168
+ steps:
169
+ - uses: actions/checkout@v4
170
+ - uses: actions/setup-node@v4
171
+ with:
172
+ node-version: 24
173
+ - run: npm ci
174
+ - name: Run Angular Compatibility Matrix
175
+ run: npx angular-compat-matrix --format markdown >> "$GITHUB_STEP_SUMMARY"
176
+ ```
177
+
178
+ ---
179
+
180
+ ## JSON Output & Stable Schema Contract
181
+
182
+ When running with `--json` or `--format json`, ng-matrix outputs structured JSON with a fixed schema contract.
183
+
184
+ The top-level field `schemaVersion` is guaranteed. Any future additions or modifications to the JSON shape will increment `schemaVersion`.
185
+
186
+ ### Schema Version 1 Specification
187
+
188
+ ```json
189
+ {
190
+ "schemaVersion": 1,
191
+ "tool": {
192
+ "name": "angular-compat-matrix",
193
+ "version": "0.1.0"
194
+ },
195
+ "checkedAt": "2026-10-26T12:00:00.000Z",
196
+ "library": {
197
+ "name": "my-lib",
198
+ "version": "1.0.0",
199
+ "source": "package",
200
+ "compiledWith": "20.2.0",
201
+ "highestMinVersion": "20.0.0",
202
+ "highestMinVersionIn": "component (dist/my-lib.mjs)",
203
+ "declarations": 12
204
+ },
205
+ "packageManager": "npm",
206
+ "installScripts": "toolchain-only",
207
+ "node": "v24.19.0",
208
+ "versions": [
209
+ {
210
+ "major": 21,
211
+ "angularVersion": "21.2.0",
212
+ "supported": true,
213
+ "prerelease": false,
214
+ "claimed": true,
215
+ "peerMisses": [],
216
+ "checks": {
217
+ "peer": {
218
+ "status": "pass",
219
+ "message": "Claimed: @angular/core@^21.0.0.",
220
+ "errorLines": [],
221
+ "durationMs": 0
222
+ },
223
+ "install": {
224
+ "status": "pass",
225
+ "message": "Installed with npm.",
226
+ "errorLines": [],
227
+ "durationMs": 4200
228
+ },
229
+ "partial": {
230
+ "status": "pass",
231
+ "advisory": false,
232
+ "message": "minVersion is 20.0.0.",
233
+ "errorLines": [],
234
+ "durationMs": 12
235
+ },
236
+ "aot": {
237
+ "status": "pass",
238
+ "message": "Production build succeeded.",
239
+ "errorLines": [],
240
+ "durationMs": 6100
241
+ },
242
+ "strict": {
243
+ "status": "pass",
244
+ "message": "Builds with strictTemplates: true and skipLibCheck: false.",
245
+ "errorLines": [],
246
+ "durationMs": 5900
247
+ },
248
+ "tests": {
249
+ "status": "off",
250
+ "message": "Not requested (--tests).",
251
+ "errorLines": [],
252
+ "durationMs": 0
253
+ }
254
+ },
255
+ "skippedInstallScripts": [],
256
+ "omittedEntryPoints": [],
257
+ "dtsErrors": false,
258
+ "durationMs": 16212
259
+ }
260
+ ],
261
+ "findings": [
262
+ {
263
+ "level": "error",
264
+ "majors": [19],
265
+ "text": "AOT build: Production build failed."
266
+ }
267
+ ],
268
+ "exitCode": 0
269
+ }
270
+ ```
271
+
272
+ ### JSON Fields Reference
273
+
274
+ - **`schemaVersion`** (`number`): The JSON schema version (currently `1`).
275
+ - **`tool`** (`{ name: string, version: string }`): CLI name and version.
276
+ - **`checkedAt`** (`string`): ISO-8601 timestamp when the check was performed.
277
+ - **`library`** (`object`):
278
+ - `name`: Package name.
279
+ - `version`: Package version.
280
+ - `source`: How the package was acquired (`workspace`, `tarball`, or `package`).
281
+ - `compiledWith` (`string?`): The Angular version that built this library.
282
+ - `highestMinVersion` (`string?`): Highest `minVersion` found across declarations.
283
+ - `highestMinVersionIn` (`string?`): Component or declaration file with the highest `minVersion`.
284
+ - `declarations` (`number`): Count of Angular declarables detected.
285
+ - **`packageManager`** (`string`): `'npm'` or `'pnpm'`.
286
+ - **`installScripts`** (`'toolchain-only' | 'all'`): Whether `--allow-scripts` was active.
287
+ - **`node`** (`string`): Node.js runtime version.
288
+ - **`versions`** (`VersionReport[]`): Results per Angular major:
289
+ - `major`: Angular major version number.
290
+ - `angularVersion`: Resolved patch version checked.
291
+ - `supported`: Whether this major is currently within Angular's official 18-month support window.
292
+ - `prerelease`: Whether this major came from a pre-release tag (`next`).
293
+ - `claimed`: `true` if peer dependencies include this version, `false` if excluded, `null` if no Angular peers declared.
294
+ - `peerMisses`: Array of declared peers whose ranges excluded this major (`{ name, range, version }`).
295
+ - `checks`: Object mapping check names (`peer`, `install`, `partial`, `aot`, `strict`, `tests`) to check results:
296
+ - `status`: `'pass' | 'fail' | 'not-run' | 'off' | 'none'`.
297
+ - `message`: Summary message.
298
+ - `errorLines`: First relevant error lines extracted from build or install logs.
299
+ - `fullOutput` (`string?`): Full command stdout and stderr on failure (optional; present only when `--verbose` is used).
300
+ - `hint` (`string?`): Plain-language diagnostic hint.
301
+ - `durationMs`: Execution duration in milliseconds.
302
+ - `advisory` (`boolean?`): `true` when partial check passed only on `minVersion` despite being built with newer Angular.
303
+ - `setupError` (`string?`): Description if the run failed due to machine/environment reasons.
304
+ - `skippedInstallScripts` (`string[]?`): Packages whose install scripts were blocked.
305
+ - `omittedEntryPoints` (`{ entry: string, missingPackage: string }[]?`): Secondary entry points skipped due to undeclared dependencies.
306
+ - `dtsErrors` (`boolean?`): `true` if strict build had errors confined to `node_modules/**/*.d.ts`.
307
+ - `durationMs`: Total duration for checking this major.
308
+ - **`findings`** (`Finding[]`): Merged diagnostics across all checked versions:
309
+ - `level`: `'error' | 'warning' | 'note'`.
310
+ - `majors`: Array of major version numbers affected.
311
+ - `text`: Human-readable problem description and suggested resolution.
312
+ - `suggestedPeerDependencies` (`Record<string, string>?`): Recommended peer dependencies when none were declared.
313
+ - **`exitCode`** (`number`): `0`, `1`, or `2`.
314
+
315
+ ---
316
+
317
+ ## Troubleshooting Real-World Issues
318
+
319
+ Here are the most common real-world incompatibilities identified across popular Angular packages and how to solve them:
320
+
321
+ ### 1. Linker Crash on Older Angular (`Unsupported enum value` / `ɵɵFactoryTarget.Service`)
322
+ - **Symptom:** The library claims support for Angular 20 and 21, but on Angular 20 the linker crashes with `Unsupported enum value` or `Cannot create property 'message' on string`.
323
+ - **Cause:** The library was compiled with Angular 21 or 22. Newer Angular compilers emit bytecode constructs that older linkers do not recognize, even though `minVersion` was claimed as `12.0.0`.
324
+ - **Fix:** Compile your library with the **oldest** Angular major you support, or narrow your `peerDependencies` range to exclude older majors.
325
+
326
+ ### 2. Secondary Entry Points with Undeclared Dependencies
327
+ - **Symptom:** A component in a secondary entry point (e.g. `primeng/chart` or `ng-zorro-antd/graph`) fails because `chart.js` or `dagre-d3-es` is not found.
328
+ - **Handling:** ng-matrix omits secondary entry points that fail exclusively because of undeclared packages and flags a warning finding. However, if your **main** entry point (`.`) imports an undeclared package, ng-matrix fails the AOT check because all consumers would experience build failures.
329
+ - **Fix:** Move undeclared packages into `peerDependencies` (or `dependencies` if required).
330
+
331
+ ### 3. Library Typings Errors under `skipLibCheck: false`
332
+ - **Symptom:** Strict build fails with errors inside declaration files under `node_modules/**/*.d.ts`.
333
+ - **Handling:** When errors are confined strictly to third-party declaration files, ng-matrix marks the strict check as passed with an informational warning finding (`"library typings have errors with skipLibCheck: false"`). It does not fail the version or trigger an exit code 1.
334
+ - **Fix:** Keep your third-party typing dependencies aligned with target TypeScript versions.
335
+
336
+ ### 4. Disk Full (ENOSPC)
337
+ - **Symptom:** Exit code 2 with `ENOSPC: no space left on device`.
338
+ - **Fix:** Clear temporary build folders and caches using `ng-matrix cache clean`, or point `NG_MATRIX_CACHE` and `TMPDIR`/`TEMP` to a drive with sufficient disk space.
339
+
340
+ > **Tip for library authors:** If your library accepts bare attributes like `<comp myFlag>`, give boolean inputs `@Input({ transform: booleanAttribute })` so consumers can write bare attributes under strict template checking (ng-matrix does not check this).
341
+
342
+ ---
343
+
344
+ ## Options Reference
345
+
346
+ ```
347
+ Usage
348
+ npx angular-compat-matrix [options]
349
+ ng-matrix [options] (once installed)
350
+ ng-matrix cache (show cache location and size)
351
+ ng-matrix cache clean (delete the cache)
352
+
353
+ Options
354
+ --versions <list> Angular majors to check: 22, 18-22, 20,22. Default: supported
355
+ majors, claimed majors (from 17), and the newest
356
+ --next Also check whatever the `next` tag points to
357
+ --project <name> The library project, in a workspace with several
358
+ --consumer <dir> Build your own example app instead of a generated smoke app
359
+ --tests Also run the library's tests on each version (slow)
360
+ --test-command <cmd> The test command for --tests (default: <pm> test)
361
+ --format <format> terminal (default), markdown or json
362
+ --json Shorthand for --format json
363
+ --pm <npm|pnpm> Package manager for the apps (default: pnpm if lockfile present, else npm)
364
+ --build <cmd> Build the library with this command instead of the detected one
365
+ --skip-build Pack the existing build output as is
366
+ --tarball <file> Check this packed .tgz instead of building
367
+ --package <spec> Check a published package, e.g. some-lib@3.1.0
368
+ --concurrency <n> Versions checked at once (default: based on available memory)
369
+ --step-timeout <min> Maximum minutes any single step may take (default: 20)
370
+ --keep Keep the temporary apps after completion for debugging
371
+ --allow-scripts Permit all package install scripts to run (default: toolchain only)
372
+ --offline Use only cached registry answers and packages
373
+ --cwd <dir> The library's workspace (default: current directory)
374
+ --verbose Show commands run and full build/install output for failures
375
+ -h, --help Show help
376
+ -v, --version Show version
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Known Limitations & Roadmap
382
+
383
+ - **Supported Workspaces:** Angular CLI workspaces and standalone `ng-packagr` libraries.
384
+ - **Node.js:** Node 24.15 or later is recommended to cover all Angular majors from 17 through 22.
385
+ - **Planned:**
386
+ - `ng-matrix ci github`: Generates dedicated GitHub Actions workflows with matrix parallelism.
387
+ - First-class Nx workspace discovery and integration.
388
+ - Dedicated SSR (Server-Side Rendering) and `with-zone.js` validation columns.
389
+ - **Out of Scope:** Yarn and Bun (npm and pnpm cover package testing; JSON output makes custom wrappers simple), app migration tooling, and runtime browser testing.
390
+
391
+ ---
392
+
393
+ ## Development
394
+
395
+ ```sh
396
+ npm install
397
+ npm test # run all unit tests
398
+ npm run build # compile TypeScript
399
+ npm ci --prefix fixtures # install Angular fixture toolchain
400
+ node test/fixtures/run.ts # run fixture integration tests
401
+ ```
402
+
403
+ ---
404
+
405
+ ## License
406
+
407
+ MIT © 2026 Nizamudeen S
package/dist/app.js ADDED
@@ -0,0 +1,91 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { environmentProblem, errorLines, exec, quietEnv, stepTimeout } from "./exec.js";
4
+ import { binOf, ensureTool } from "./tools.js";
5
+ /** One flag set that works, without prompts, for every CLI from 17 to 22. */
6
+ export function newAppArgs(pm) {
7
+ return [
8
+ 'new',
9
+ 'smoke',
10
+ '--minimal',
11
+ '--skip-git',
12
+ '--skip-install',
13
+ '--defaults',
14
+ '--interactive=false',
15
+ '--ssr=false',
16
+ `--package-manager=${pm}`,
17
+ ];
18
+ }
19
+ export async function generateApp(angular, pm, parentDir, cacheDir, offline) {
20
+ const cli = await ensureTool(cacheDir, '@angular/cli', angular.cliVersion, offline);
21
+ const result = await exec(process.execPath, [binOf(cli, 'ng'), ...newAppArgs(pm)], {
22
+ cwd: parentDir,
23
+ env: quietEnv({ CI: '1' }),
24
+ timeoutMs: stepTimeout(),
25
+ });
26
+ const appDir = join(parentDir, 'smoke');
27
+ if (result.code !== 0 || !existsSync(join(appDir, 'angular.json'))) {
28
+ const why = environmentProblem(result);
29
+ throw new Error(`ng new (Angular CLI ${angular.cliVersion}) failed${why ? `: ${why}` : ''}:\n${errorLines(result.output).join('\n')}`);
30
+ }
31
+ return appDir;
32
+ }
33
+ export function readJson(path) {
34
+ // Generated tsconfig files start with /* comments */; JSON.parse can't take them.
35
+ const text = readFileSync(path, 'utf8').replace(/^\s*\/\*[\s\S]*?\*\/\s*/gm, '').replace(/^\s*\/\/.*$/gm, '');
36
+ return JSON.parse(text);
37
+ }
38
+ export function writeJson(path, value) {
39
+ writeFileSync(path, JSON.stringify(value, null, 2) + '\n');
40
+ }
41
+ /** The app project in angular.json: the generated one, or the first application of a --consumer app. */
42
+ export function appProject(appDir) {
43
+ const workspace = readJson(join(appDir, 'angular.json'));
44
+ const entry = Object.entries(workspace.projects).find(([, p]) => (p.projectType ?? 'application') === 'application');
45
+ if (!entry)
46
+ throw new Error(`${appDir}/angular.json has no application project.`);
47
+ const [name, project] = entry;
48
+ const tsConfig = String(project.architect?.build?.options?.tsConfig ?? 'tsconfig.app.json');
49
+ return { name, tsConfig };
50
+ }
51
+ /**
52
+ * Points the build at ng-matrix's own tsconfig (and, for a generated app, its own main file),
53
+ * and removes the production budgets so a large component kit doesn't fail the build on size.
54
+ */
55
+ export function configureBuild(appDir, mainFile) {
56
+ const path = join(appDir, 'angular.json');
57
+ const workspace = readJson(path);
58
+ const project = appProject(appDir);
59
+ const build = workspace.projects[project.name].architect.build;
60
+ build.options ??= {};
61
+ if (mainFile) {
62
+ // `browser` for the application builder (17+), `main` for the older webpack one.
63
+ if ('main' in build.options && !('browser' in build.options))
64
+ build.options.main = mainFile;
65
+ else
66
+ build.options.browser = mainFile;
67
+ }
68
+ build.options.tsConfig = 'tsconfig.ngm.json';
69
+ for (const configuration of Object.values(build.configurations ?? {})) {
70
+ if ('budgets' in configuration)
71
+ configuration.budgets = [];
72
+ }
73
+ writeJson(path, workspace);
74
+ return project;
75
+ }
76
+ /**
77
+ * Writes tsconfig.ngm.json extending the app's own tsconfig, with the template strictness set
78
+ * explicitly: Angular 17 to 21 apps write strictTemplates: true, and 22 makes it the default.
79
+ */
80
+ export function writeTsconfig(appDir, baseTsConfig, mode, files) {
81
+ const config = {
82
+ extends: `./${baseTsConfig.replace(/^\.\//, '')}`,
83
+ compilerOptions: { skipLibCheck: mode.skipLibCheck },
84
+ angularCompilerOptions: { strictTemplates: mode.strictTemplates },
85
+ };
86
+ if (files) {
87
+ config.files = files;
88
+ config.include = [];
89
+ }
90
+ writeJson(join(appDir, 'tsconfig.ngm.json'), config);
91
+ }