ng-checkup 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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +273 -2
  3. package/dist/checks/change-detection.d.ts +22 -0
  4. package/dist/checks/change-detection.js +99 -0
  5. package/dist/checks/change-detection.js.map +1 -0
  6. package/dist/checks/circular-deps.d.ts +13 -0
  7. package/dist/checks/circular-deps.js +43 -0
  8. package/dist/checks/circular-deps.js.map +1 -0
  9. package/dist/checks/components.d.ts +19 -0
  10. package/dist/checks/components.js +97 -0
  11. package/dist/checks/components.js.map +1 -0
  12. package/dist/checks/context.d.ts +51 -0
  13. package/dist/checks/context.js +53 -0
  14. package/dist/checks/context.js.map +1 -0
  15. package/dist/checks/deprecated-apis.d.ts +14 -0
  16. package/dist/checks/deprecated-apis.js +141 -0
  17. package/dist/checks/deprecated-apis.js.map +1 -0
  18. package/dist/checks/state.d.ts +13 -0
  19. package/dist/checks/state.js +72 -0
  20. package/dist/checks/state.js.map +1 -0
  21. package/dist/checks/testing.d.ts +13 -0
  22. package/dist/checks/testing.js +98 -0
  23. package/dist/checks/testing.js.map +1 -0
  24. package/dist/checks/type-safety.d.ts +14 -0
  25. package/dist/checks/type-safety.js +117 -0
  26. package/dist/checks/type-safety.js.map +1 -0
  27. package/dist/checks/version.d.ts +12 -0
  28. package/dist/checks/version.js +107 -0
  29. package/dist/checks/version.js.map +1 -0
  30. package/dist/cli.d.ts +65 -0
  31. package/dist/cli.js +285 -0
  32. package/dist/cli.js.map +1 -0
  33. package/dist/config.d.ts +47 -0
  34. package/dist/config.js +93 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/deprecations.d.ts +30 -0
  37. package/dist/deprecations.js +279 -0
  38. package/dist/deprecations.js.map +1 -0
  39. package/dist/files.d.ts +33 -0
  40. package/dist/files.js +98 -0
  41. package/dist/files.js.map +1 -0
  42. package/dist/graph.d.ts +44 -0
  43. package/dist/graph.js +151 -0
  44. package/dist/graph.js.map +1 -0
  45. package/dist/index.d.ts +44 -0
  46. package/dist/index.js +135 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/init.d.ts +78 -0
  49. package/dist/init.js +220 -0
  50. package/dist/init.js.map +1 -0
  51. package/dist/report/html.d.ts +14 -0
  52. package/dist/report/html.js +210 -0
  53. package/dist/report/html.js.map +1 -0
  54. package/dist/report/markdown.d.ts +8 -0
  55. package/dist/report/markdown.js +55 -0
  56. package/dist/report/markdown.js.map +1 -0
  57. package/dist/report/summary.d.ts +45 -0
  58. package/dist/report/summary.js +65 -0
  59. package/dist/report/summary.js.map +1 -0
  60. package/dist/report/terminal.d.ts +17 -0
  61. package/dist/report/terminal.js +70 -0
  62. package/dist/report/terminal.js.map +1 -0
  63. package/dist/scan.d.ts +119 -0
  64. package/dist/scan.js +270 -0
  65. package/dist/scan.js.map +1 -0
  66. package/dist/types.d.ts +97 -0
  67. package/dist/types.js +9 -0
  68. package/dist/types.js.map +1 -0
  69. package/dist/workspace.d.ts +76 -0
  70. package/dist/workspace.js +156 -0
  71. package/dist/workspace.js.map +1 -0
  72. package/package.json +67 -4
  73. package/schematics/collection.json +10 -0
  74. package/schematics/ng-add/index.cjs +60 -0
  75. package/schematics/ng-add/schema.json +29 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The shapes every check produces and every report renderer consumes.
3
+ *
4
+ * Kept in one dependency-free file so the renderers never import a check and a
5
+ * check never imports a renderer: a check only describes what it found, and the
6
+ * terminal, Markdown, HTML and JSON outputs decide independently how to show it.
7
+ */
8
+ /**
9
+ * How a check judges the area it looked at.
10
+ *
11
+ * `info` means the check had nothing to grade, either because the codebase has
12
+ * none of what it measures (no components, no tests) or because a required
13
+ * input was missing (no `node_modules`). It never counts against the codebase.
14
+ */
15
+ export type Grade = 'good' | 'fair' | 'poor' | 'info';
16
+ /**
17
+ * How urgent a single finding is. `high` findings are lifted into the
18
+ * "top priorities" list at the head of every report.
19
+ */
20
+ export type Severity = 'high' | 'medium' | 'low' | 'info';
21
+ /** A source location, relative to the analysed root and always with `/` separators. */
22
+ export interface FileRef {
23
+ path: string;
24
+ /** 1-based; absent when the finding concerns the file as a whole. */
25
+ line?: number;
26
+ }
27
+ /** One headline number shown in a check's metric grid. */
28
+ export interface Metric {
29
+ label: string;
30
+ value: string | number;
31
+ /** A short qualifier rendered under the value, such as "of 214 components". */
32
+ hint?: string;
33
+ }
34
+ /** One thing a check wants a reader to act on, or at least to know. */
35
+ export interface Finding {
36
+ severity: Severity;
37
+ /** One line, readable without the detail. Backticks mark code, as in Markdown; nothing else is markup. */
38
+ message: string;
39
+ /** Why it matters and what fixing it involves. Same markup rule as `message`. */
40
+ detail?: string;
41
+ /**
42
+ * Example locations, already capped at `maxExamples`. `count` holds the real
43
+ * total, so a renderer can say "and 40 more".
44
+ */
45
+ files?: FileRef[];
46
+ count?: number;
47
+ }
48
+ /** What one check returns. */
49
+ export interface CheckResult {
50
+ /** Stable machine id, used by `--fail-on` and in the JSON output. */
51
+ id: string;
52
+ title: string;
53
+ grade: Grade;
54
+ /** One sentence shown next to the grade in every format. */
55
+ summary: string;
56
+ metrics: Metric[];
57
+ findings: Finding[];
58
+ }
59
+ /** The `ng-checkup` result as a whole, which is also the JSON output schema. */
60
+ export interface Report {
61
+ tool: {
62
+ name: string;
63
+ version: string;
64
+ };
65
+ /** ISO 8601. */
66
+ generatedAt: string;
67
+ root: string;
68
+ project: {
69
+ name: string;
70
+ kind: WorkspaceKind;
71
+ angularVersion?: string;
72
+ sourceFiles: number;
73
+ templateFiles: number;
74
+ linesOfCode: number;
75
+ };
76
+ checks: CheckResult[];
77
+ durationMs: number;
78
+ }
79
+ /**
80
+ * How the repository is organised. It decides where project configuration is
81
+ * read from: `angular.json` for the Angular CLI, the `project.json` files for
82
+ * Nx. A repository with neither is still analysed, its builders are just unknown.
83
+ */
84
+ export type WorkspaceKind = 'angular-cli' | 'nx' | 'unknown';
85
+ /** Options shared by the CLI and the programmatic API. */
86
+ export interface CheckupOptions {
87
+ /**
88
+ * Skip the npm registry lookup of the latest Angular version. The version
89
+ * check then compares against the newest major this release of the tool
90
+ * knows about and says so.
91
+ */
92
+ offline?: boolean;
93
+ /** How many example locations a finding lists. Defaults to 5. */
94
+ maxExamples?: number;
95
+ /** Paths to leave out, as in the config file's `ignore` (see `ignoreMatcher`). */
96
+ ignore?: string[];
97
+ }
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The shapes every check produces and every report renderer consumes.
3
+ *
4
+ * Kept in one dependency-free file so the renderers never import a check and a
5
+ * check never imports a renderer: a check only describes what it found, and the
6
+ * terminal, Markdown, HTML and JSON outputs decide independently how to show it.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,76 @@
1
+ import type { WorkspaceKind } from './types.js';
2
+ /** One build/test/e2e target of one project, as `angular.json` or a `project.json` declares it. */
3
+ export interface TargetInfo {
4
+ project: string;
5
+ target: string;
6
+ /** The `builder` (Angular CLI) or `executor` (Nx), such as `@angular/build:application`. */
7
+ builder: string;
8
+ /** The target's `options` block, untyped because every builder defines its own. */
9
+ options: Record<string, unknown>;
10
+ /** Absolute directory the project's relative option paths resolve against. */
11
+ baseDirectory: string;
12
+ }
13
+ /** What the analysis knows about the repository before it reads any source file. */
14
+ export interface WorkspaceInfo {
15
+ root: string;
16
+ name: string;
17
+ kind: WorkspaceKind;
18
+ /** `dependencies` and `devDependencies` of the root `package.json`, merged. Empty when there is none. */
19
+ dependencies: Record<string, string>;
20
+ /** Whether `node_modules/@angular/core` exists, which the deprecation index needs. */
21
+ hasNodeModules: boolean;
22
+ angular: {
23
+ /** The range `package.json` asks for, such as `^17.3.0`. */
24
+ declared?: string;
25
+ /** The exact version in `node_modules`, when dependencies are installed. */
26
+ installed?: string;
27
+ /** From `installed` when present, else from `declared`. Absent when Angular is not a dependency. */
28
+ major?: number;
29
+ };
30
+ /**
31
+ * The project's TypeScript major, installed version first, then the
32
+ * `package.json` range. It decides what an absent `strict` means: on from
33
+ * TypeScript 6, off before.
34
+ */
35
+ typescriptMajor?: number;
36
+ targets: TargetInfo[];
37
+ /** Every polyfill entry of every target, which is where `zone.js` is wired in for CLI 15+ projects. */
38
+ polyfills: string[];
39
+ /** The tsconfig the application is compiled with, used for the strictness flags. */
40
+ appTsconfig?: string;
41
+ /** The tsconfig holding repository-wide `paths`, used to resolve imports for the dependency graph. */
42
+ rootTsconfig?: string;
43
+ }
44
+ /**
45
+ * Reads the repository-level configuration: `package.json`, `angular.json` or
46
+ * the Nx `project.json` files, and the tsconfig files that matter.
47
+ *
48
+ * Every read is defensive. A repository is analysed as far as it can be, so a
49
+ * missing or malformed file leaves the corresponding field empty instead of
50
+ * aborting the run; the checks that depend on it then grade `info`.
51
+ *
52
+ * @param projectJsons The `project.json` paths the file walk already found, so
53
+ * an Nx workspace is not walked a second time.
54
+ */
55
+ export declare function readWorkspace(root: string, projectJsons: string[]): WorkspaceInfo;
56
+ /**
57
+ * The major version in a version or a range: `^17.3.0` gives 17, `~22.0.8`
58
+ * gives 22, `22.0.8` gives 22. Undefined for anything without a leading number,
59
+ * such as `latest`, a git URL or a `workspace:` protocol.
60
+ */
61
+ export declare function majorOf(versionOrRange: string | undefined): number | undefined;
62
+ /**
63
+ * The `version` field of an installed package, or undefined when the package
64
+ * is not in `node_modules`. Reads `package.json` directly instead of using
65
+ * `require.resolve`, which would honour the analysed package's `exports` map
66
+ * and refuse a path it does not export.
67
+ */
68
+ export declare function readInstalledVersion(root: string, packageName: string): string | undefined;
69
+ /**
70
+ * Parses a JSON file that may contain comments and trailing commas, which
71
+ * `angular.json`, `project.json` and every tsconfig are allowed to. Returns
72
+ * undefined for a missing file, a parse error, or anything that is not an object.
73
+ */
74
+ export declare function readJson(path: string): Record<string, unknown> | undefined;
75
+ /** Narrows an unknown JSON value to an object, treating anything else as empty. */
76
+ export declare function asRecord(value: unknown): Record<string, unknown>;
@@ -0,0 +1,156 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { basename, dirname, join, resolve } from 'node:path';
3
+ import ts from 'typescript';
4
+ /**
5
+ * Reads the repository-level configuration: `package.json`, `angular.json` or
6
+ * the Nx `project.json` files, and the tsconfig files that matter.
7
+ *
8
+ * Every read is defensive. A repository is analysed as far as it can be, so a
9
+ * missing or malformed file leaves the corresponding field empty instead of
10
+ * aborting the run; the checks that depend on it then grade `info`.
11
+ *
12
+ * @param projectJsons The `project.json` paths the file walk already found, so
13
+ * an Nx workspace is not walked a second time.
14
+ */
15
+ export function readWorkspace(root, projectJsons) {
16
+ const packageJson = readJson(join(root, 'package.json'));
17
+ const dependencies = {
18
+ ...asStringRecord(packageJson?.['devDependencies']),
19
+ ...asStringRecord(packageJson?.['dependencies']),
20
+ };
21
+ const angularJson = readJson(join(root, 'angular.json'));
22
+ const isNx = existsSync(join(root, 'nx.json'));
23
+ const kind = angularJson ? 'angular-cli' : isNx ? 'nx' : 'unknown';
24
+ const targets = angularJson ? readAngularJsonTargets(root, angularJson) : readProjectJsonTargets(projectJsons);
25
+ const installed = readInstalledVersion(root, '@angular/core');
26
+ const declared = dependencies['@angular/core'];
27
+ return {
28
+ root,
29
+ name: typeof packageJson?.['name'] === 'string' ? packageJson['name'] : basename(root),
30
+ kind,
31
+ dependencies,
32
+ hasNodeModules: installed !== undefined,
33
+ angular: { declared, installed, major: majorOf(installed ?? declared) },
34
+ typescriptMajor: majorOf(readInstalledVersion(root, 'typescript') ?? dependencies['typescript']),
35
+ targets,
36
+ polyfills: targets.flatMap((target) => polyfillsOf(target.options)),
37
+ appTsconfig: findAppTsconfig(root, targets),
38
+ rootTsconfig: ['tsconfig.base.json', 'tsconfig.json'].map((name) => join(root, name)).find((path) => existsSync(path)),
39
+ };
40
+ }
41
+ /**
42
+ * The major version in a version or a range: `^17.3.0` gives 17, `~22.0.8`
43
+ * gives 22, `22.0.8` gives 22. Undefined for anything without a leading number,
44
+ * such as `latest`, a git URL or a `workspace:` protocol.
45
+ */
46
+ export function majorOf(versionOrRange) {
47
+ const match = versionOrRange?.match(/(\d+)\.\d+/) ?? versionOrRange?.match(/^[\^~>=<\s]*(\d+)$/);
48
+ return match ? Number(match[1]) : undefined;
49
+ }
50
+ /**
51
+ * The `version` field of an installed package, or undefined when the package
52
+ * is not in `node_modules`. Reads `package.json` directly instead of using
53
+ * `require.resolve`, which would honour the analysed package's `exports` map
54
+ * and refuse a path it does not export.
55
+ */
56
+ export function readInstalledVersion(root, packageName) {
57
+ const version = readJson(join(root, 'node_modules', packageName, 'package.json'))?.['version'];
58
+ return typeof version === 'string' ? version : undefined;
59
+ }
60
+ /** Targets from `angular.json`, which calls them `architect` in older files and `targets` in newer ones. */
61
+ function readAngularJsonTargets(root, angularJson) {
62
+ const projects = asRecord(angularJson['projects']);
63
+ const targets = [];
64
+ for (const [projectName, value] of Object.entries(projects)) {
65
+ const project = asRecord(value);
66
+ const projectRoot = typeof project['root'] === 'string' ? project['root'] : '';
67
+ const declared = asRecord(project['architect'] ?? project['targets']);
68
+ for (const [targetName, target] of Object.entries(declared)) {
69
+ const builder = asRecord(target)['builder'];
70
+ if (typeof builder !== 'string')
71
+ continue;
72
+ // angular.json resolves option paths against the workspace root, not the project root.
73
+ targets.push({ project: projectName || projectRoot, target: targetName, builder, options: asRecord(asRecord(target)['options']), baseDirectory: root });
74
+ }
75
+ }
76
+ return targets;
77
+ }
78
+ /** Targets from Nx `project.json` files, which name the builder `executor`. */
79
+ function readProjectJsonTargets(projectJsons) {
80
+ const targets = [];
81
+ for (const path of projectJsons) {
82
+ const project = readJson(path);
83
+ if (!project)
84
+ continue;
85
+ const projectName = typeof project['name'] === 'string' ? project['name'] : basename(dirname(path));
86
+ for (const [targetName, target] of Object.entries(asRecord(project['targets']))) {
87
+ const record = asRecord(target);
88
+ const builder = record['executor'] ?? record['builder'];
89
+ if (typeof builder !== 'string')
90
+ continue;
91
+ // Nx resolves option paths against the workspace root as well, which is
92
+ // two or more levels above the project.json in a typical apps/ layout.
93
+ targets.push({ project: projectName, target: targetName, builder, options: asRecord(record['options']), baseDirectory: findWorkspaceRoot(path) });
94
+ }
95
+ }
96
+ return targets;
97
+ }
98
+ /** The nearest ancestor of `path` holding `nx.json`, falling back to the file's own directory. */
99
+ function findWorkspaceRoot(path) {
100
+ let directory = dirname(path);
101
+ while (true) {
102
+ if (existsSync(join(directory, 'nx.json')))
103
+ return directory;
104
+ const parent = dirname(directory);
105
+ if (parent === directory)
106
+ return dirname(path);
107
+ directory = parent;
108
+ }
109
+ }
110
+ /** `polyfills` is a string in CLI 15–16 projects and an array from CLI 17 on. */
111
+ function polyfillsOf(options) {
112
+ const polyfills = options['polyfills'];
113
+ if (typeof polyfills === 'string')
114
+ return [polyfills];
115
+ return Array.isArray(polyfills) ? polyfills.filter((entry) => typeof entry === 'string') : [];
116
+ }
117
+ /**
118
+ * The application's tsconfig: the `tsConfig` option of the first application
119
+ * build target, then a root `tsconfig.app.json`, then the root `tsconfig.json`.
120
+ *
121
+ * A library's build target is skipped on purpose. Libraries are commonly built
122
+ * with laxer settings than the app, and the app config is what decides how
123
+ * strictly the shipped code is checked.
124
+ */
125
+ function findAppTsconfig(root, targets) {
126
+ const applicationBuild = targets.find((target) => target.target === 'build' && /:(application|browser|browser-esbuild|webpack-browser)$/.test(target.builder));
127
+ const fromTarget = applicationBuild?.options['tsConfig'];
128
+ if (applicationBuild && typeof fromTarget === 'string') {
129
+ const path = resolve(applicationBuild.baseDirectory, fromTarget);
130
+ if (existsSync(path))
131
+ return path;
132
+ }
133
+ return ['tsconfig.app.json', 'tsconfig.json'].map((name) => join(root, name)).find((path) => existsSync(path));
134
+ }
135
+ /**
136
+ * Parses a JSON file that may contain comments and trailing commas, which
137
+ * `angular.json`, `project.json` and every tsconfig are allowed to. Returns
138
+ * undefined for a missing file, a parse error, or anything that is not an object.
139
+ */
140
+ export function readJson(path) {
141
+ if (!existsSync(path))
142
+ return undefined;
143
+ const parsed = ts.parseConfigFileTextToJson(path, readFileSync(path, 'utf8'));
144
+ if (parsed.error || typeof parsed.config !== 'object' || parsed.config === null)
145
+ return undefined;
146
+ return parsed.config;
147
+ }
148
+ /** Narrows an unknown JSON value to an object, treating anything else as empty. */
149
+ export function asRecord(value) {
150
+ return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {};
151
+ }
152
+ /** Narrows a dependency map, dropping any entry whose version is not a string. */
153
+ function asStringRecord(value) {
154
+ return Object.fromEntries(Object.entries(asRecord(value)).filter((entry) => typeof entry[1] === 'string'));
155
+ }
156
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "ng-checkup",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "A one-minute modernization report for any Angular codebase: version gap, deprecated APIs, zoneless readiness, signals adoption, test runner, type safety and circular dependencies.",
5
+ "keywords": [
6
+ "angular",
7
+ "audit",
8
+ "migration",
9
+ "modernization",
10
+ "zoneless",
11
+ "signals",
12
+ "health-check",
13
+ "cli"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "Stefan Kuburovic",
17
+ "homepage": "https://github.com/stefankuburovic/ng-checkup#readme",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/stefankuburovic/ng-checkup.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/stefankuburovic/ng-checkup/issues"
24
+ },
25
+ "type": "module",
26
+ "bin": {
27
+ "ng-checkup": "dist/cli.js"
28
+ },
29
+ "main": "dist/index.js",
30
+ "types": "dist/index.d.ts",
31
+ "schematics": "./schematics/collection.json",
32
+ "ng-add": {
33
+ "save": "devDependencies"
34
+ },
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "default": "./dist/index.js"
39
+ },
40
+ "./package.json": "./package.json",
41
+ "./schematics/*": "./schematics/*"
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "schematics"
46
+ ],
47
+ "engines": {
48
+ "node": ">=22"
49
+ },
50
+ "scripts": {
51
+ "build": "tsc -p tsconfig.build.json",
52
+ "prepare": "npm run build",
53
+ "typecheck": "tsc --noEmit",
54
+ "pretest": "npm run build",
55
+ "test": "vitest run",
56
+ "prepublishOnly": "npm run typecheck && npm test",
57
+ "checkup": "node dist/cli.js"
58
+ },
59
+ "dependencies": {
60
+ "typescript": "~6.0.3"
61
+ },
62
+ "devDependencies": {
63
+ "@types/node": "^24.19.2",
64
+ "vitest": "^5.0.5"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public"
68
+ }
69
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
3
+ "schematics": {
4
+ "ng-add": {
5
+ "description": "Sets up ng-checkup: a checkup script, a config file and a CI step.",
6
+ "factory": "./ng-add/index.cjs#ngAdd",
7
+ "schema": "./ng-add/schema.json"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+ /**
3
+ * `ng add ng-checkup`.
4
+ *
5
+ * The Angular CLI has already installed the package and saved it to
6
+ * devDependencies (see "ng-add" in package.json) by the time this runs, so the
7
+ * rule only applies the same plan `ng-checkup init` does, through the CLI's
8
+ * virtual tree: nothing is written unless the whole schematic succeeds, and
9
+ * `ng add --dry-run` shows the changes for free.
10
+ *
11
+ * Written as CommonJS on purpose. The Angular CLI loads schematic factories
12
+ * with `require`, while the rest of this package is ESM; the planner is
13
+ * therefore loaded with a dynamic `import()`, which a CommonJS module may do
14
+ * and which Angular's rule runner awaits.
15
+ *
16
+ * It deliberately depends on nothing from @angular-devkit: the Tree and
17
+ * context are passed in by the CLI, so the package needs no schematics
18
+ * dependency of its own.
19
+ */
20
+ const { join } = require('node:path');
21
+ const { pathToFileURL } = require('node:url');
22
+
23
+ /**
24
+ * @param {{ ci?: 'auto' | 'github' | 'gitlab' | 'none', force?: boolean }} options
25
+ */
26
+ function ngAdd(options) {
27
+ return async (tree, context) => {
28
+ const { planInit } = await import(pathToFileURL(join(__dirname, '..', '..', 'dist', 'init.js')).href);
29
+ const { version } = require(join(__dirname, '..', '..', 'package.json'));
30
+
31
+ const plan = planInit(
32
+ {
33
+ read: (path) => {
34
+ const buffer = tree.read(`/${path}`);
35
+ return buffer ? buffer.toString('utf8') : undefined;
36
+ },
37
+ exists: (path) => {
38
+ if (tree.exists(`/${path}`)) return true;
39
+ // Tree.exists only knows files; a directory exists when it holds something.
40
+ const directory = tree.getDir(`/${path}`);
41
+ return directory.subfiles.length > 0 || directory.subdirs.length > 0;
42
+ },
43
+ },
44
+ { ci: options.ci ?? 'auto', force: options.force ?? false, addDependency: false, version },
45
+ );
46
+
47
+ for (const change of plan.changes) {
48
+ const path = `/${change.path}`;
49
+ if (tree.exists(path)) tree.overwrite(path, change.content);
50
+ else tree.create(path, change.content);
51
+ context.logger.info(`ng-checkup: ${change.path}: ${change.summary}`);
52
+ }
53
+ if (plan.changes.length === 0) context.logger.info('ng-checkup is already configured here.');
54
+ for (const note of plan.notes) context.logger.warn(note);
55
+ context.logger.info(`Run the checkup with: ${plan.nextSteps[plan.nextSteps.length - 1]}`);
56
+ return tree;
57
+ };
58
+ }
59
+
60
+ module.exports = { ngAdd };
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "NgCheckupNgAdd",
4
+ "title": "ng-checkup ng-add",
5
+ "type": "object",
6
+ "properties": {
7
+ "ci": {
8
+ "type": "string",
9
+ "enum": ["auto", "github", "gitlab", "none"],
10
+ "default": "auto",
11
+ "description": "Where to add the CI step. auto uses GitHub when .github/ exists, GitLab when .gitlab-ci.yml does.",
12
+ "x-prompt": {
13
+ "message": "Add a CI step that runs the checkup on every pull request?",
14
+ "type": "list",
15
+ "items": [
16
+ { "value": "auto", "label": "Yes, for the CI this repository already uses" },
17
+ { "value": "github", "label": "Yes, GitHub Actions" },
18
+ { "value": "gitlab", "label": "Yes, GitLab CI" },
19
+ { "value": "none", "label": "No" }
20
+ ]
21
+ }
22
+ },
23
+ "force": {
24
+ "type": "boolean",
25
+ "default": false,
26
+ "description": "Replace an existing \"checkup\" script."
27
+ }
28
+ }
29
+ }