ng-checkup 0.0.0-stage → 0.1.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +107 -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 +105 -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 +28 -0
  31. package/dist/cli.js +166 -0
  32. package/dist/cli.js.map +1 -0
  33. package/dist/deprecations.d.ts +30 -0
  34. package/dist/deprecations.js +279 -0
  35. package/dist/deprecations.js.map +1 -0
  36. package/dist/files.d.ts +29 -0
  37. package/dist/files.js +92 -0
  38. package/dist/files.js.map +1 -0
  39. package/dist/graph.d.ts +44 -0
  40. package/dist/graph.js +151 -0
  41. package/dist/graph.js.map +1 -0
  42. package/dist/index.d.ts +39 -0
  43. package/dist/index.js +126 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/report/html.d.ts +14 -0
  46. package/dist/report/html.js +210 -0
  47. package/dist/report/html.js.map +1 -0
  48. package/dist/report/markdown.d.ts +8 -0
  49. package/dist/report/markdown.js +55 -0
  50. package/dist/report/markdown.js.map +1 -0
  51. package/dist/report/summary.d.ts +45 -0
  52. package/dist/report/summary.js +65 -0
  53. package/dist/report/summary.js.map +1 -0
  54. package/dist/report/terminal.d.ts +17 -0
  55. package/dist/report/terminal.js +70 -0
  56. package/dist/report/terminal.js.map +1 -0
  57. package/dist/scan.d.ts +119 -0
  58. package/dist/scan.js +270 -0
  59. package/dist/scan.js.map +1 -0
  60. package/dist/types.d.ts +95 -0
  61. package/dist/types.js +9 -0
  62. package/dist/types.js.map +1 -0
  63. package/dist/workspace.d.ts +70 -0
  64. package/dist/workspace.js +155 -0
  65. package/dist/workspace.js.map +1 -0
  66. package/package.json +60 -4
package/dist/scan.js ADDED
@@ -0,0 +1,270 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import ts from 'typescript';
3
+ import { isSpecFile, toReportPath } from './files.js';
4
+ /**
5
+ * Parses one file and extracts its facts. Parsing is syntax-only (no type
6
+ * checker, no program), which is what lets a 3 000-file repository finish in
7
+ * seconds and lets the tool run without the project's dependencies installed.
8
+ * The cost is that identifiers are matched by their import, never by type.
9
+ */
10
+ export function scanSourceFile(root, absolutePath) {
11
+ const text = readFileSync(absolutePath, 'utf8');
12
+ const source = ts.createSourceFile(absolutePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
13
+ const path = toReportPath(root, absolutePath);
14
+ const facts = {
15
+ path,
16
+ absolutePath,
17
+ isSpec: isSpecFile(absolutePath),
18
+ linesOfCode: countLinesOfCode(text),
19
+ imports: [],
20
+ components: [],
21
+ ngModules: 0,
22
+ injectables: 0,
23
+ decoratorInputs: 0,
24
+ decoratorOutputs: 0,
25
+ calls: new Map(),
26
+ constructions: new Map(),
27
+ memberAccesses: new Map(),
28
+ jasmineCalls: 0,
29
+ anyLines: [],
30
+ nonNullLines: [],
31
+ definiteAssignments: 0,
32
+ typeSuppressions: (text.match(/@ts-(ignore|expect-error|nocheck)\b/g) ?? []).length,
33
+ inlineTemplates: [],
34
+ };
35
+ /** Imported bindings by local name: what `signal` in this file actually refers to. */
36
+ const locals = new Map();
37
+ for (const statement of source.statements) {
38
+ const fact = importFactOf(statement, source);
39
+ if (!fact)
40
+ continue;
41
+ facts.imports.push(fact);
42
+ for (const binding of fact.bindings)
43
+ locals.set(binding.local, { module: fact.specifier, imported: binding.imported });
44
+ }
45
+ const lineOf = (node) => source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
46
+ /** `module#name` for an identifier bound by an import, undefined for anything local or global. */
47
+ const keyOf = (expression) => {
48
+ if (ts.isIdentifier(expression)) {
49
+ const binding = locals.get(expression.text);
50
+ return binding && binding.imported !== '*' ? `${binding.module}#${binding.imported}` : undefined;
51
+ }
52
+ if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression)) {
53
+ const binding = locals.get(expression.expression.text);
54
+ if (!binding)
55
+ return undefined;
56
+ // `core.signal()` on a namespace import is `signal`; `input.required()` on a named import is `input.required`.
57
+ return binding.imported === '*' ? `${binding.module}#${expression.name.text}` : `${binding.module}#${binding.imported}.${expression.name.text}`;
58
+ }
59
+ return undefined;
60
+ };
61
+ const record = (map, key, line) => {
62
+ const lines = map.get(key);
63
+ if (lines)
64
+ lines.push(line);
65
+ else
66
+ map.set(key, [line]);
67
+ };
68
+ const visit = (node) => {
69
+ if (ts.isCallExpression(node)) {
70
+ const key = keyOf(node.expression);
71
+ if (key)
72
+ record(facts.calls, key, lineOf(node));
73
+ else if (isJasmineGlobalCall(node.expression, locals))
74
+ facts.jasmineCalls++;
75
+ if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
76
+ const [argument] = node.arguments;
77
+ if (argument && ts.isStringLiteralLike(argument)) {
78
+ facts.imports.push({ specifier: argument.text, line: lineOf(node), typeOnly: false, dynamic: true, bindings: [] });
79
+ }
80
+ }
81
+ }
82
+ else if (ts.isNewExpression(node)) {
83
+ const key = keyOf(node.expression);
84
+ if (key)
85
+ record(facts.constructions, key, lineOf(node));
86
+ }
87
+ else if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression)) {
88
+ const binding = locals.get(node.expression.text);
89
+ if (binding && binding.imported !== '*')
90
+ record(facts.memberAccesses, `${binding.module}#${binding.imported}.${node.name.text}`, lineOf(node));
91
+ }
92
+ else if (node.kind === ts.SyntaxKind.AnyKeyword) {
93
+ facts.anyLines.push(lineOf(node));
94
+ }
95
+ else if (ts.isNonNullExpression(node)) {
96
+ facts.nonNullLines.push(lineOf(node));
97
+ }
98
+ else if (ts.isPropertyDeclaration(node) && node.exclamationToken) {
99
+ facts.definiteAssignments++;
100
+ }
101
+ if (ts.isClassDeclaration(node))
102
+ visitClassDecorators(node);
103
+ if ((ts.isPropertyDeclaration(node) || ts.isSetAccessorDeclaration(node) || ts.isGetAccessorDeclaration(node)) && ts.canHaveDecorators(node)) {
104
+ for (const decorator of ts.getDecorators(node) ?? []) {
105
+ const key = decoratorKey(decorator);
106
+ if (key === '@angular/core#Input')
107
+ facts.decoratorInputs++;
108
+ else if (key === '@angular/core#Output')
109
+ facts.decoratorOutputs++;
110
+ }
111
+ }
112
+ ts.forEachChild(node, visit);
113
+ };
114
+ /** `module#Name` of a decorator such as `@Component({...})`, resolved through the imports. */
115
+ const decoratorKey = (decorator) => {
116
+ const expression = ts.isCallExpression(decorator.expression) ? decorator.expression.expression : decorator.expression;
117
+ return keyOf(expression);
118
+ };
119
+ const visitClassDecorators = (node) => {
120
+ for (const decorator of ts.getDecorators(node) ?? []) {
121
+ const key = decoratorKey(decorator);
122
+ if (key === '@angular/core#NgModule')
123
+ facts.ngModules++;
124
+ else if (key === '@angular/core#Injectable')
125
+ facts.injectables++;
126
+ else if (key === '@angular/core#Component') {
127
+ const metadata = ts.isCallExpression(decorator.expression) ? decorator.expression.arguments[0] : undefined;
128
+ const component = { name: node.name?.text ?? '(anonymous)', line: lineOf(node) };
129
+ if (metadata && ts.isObjectLiteralExpression(metadata)) {
130
+ for (const property of metadata.properties) {
131
+ if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name))
132
+ continue;
133
+ const value = property.initializer;
134
+ if (property.name.text === 'standalone') {
135
+ if (value.kind === ts.SyntaxKind.TrueKeyword)
136
+ component.standalone = true;
137
+ else if (value.kind === ts.SyntaxKind.FalseKeyword)
138
+ component.standalone = false;
139
+ }
140
+ else if (property.name.text === 'changeDetection' && ts.isPropertyAccessExpression(value)) {
141
+ component.changeDetection = value.name.text;
142
+ }
143
+ else if (property.name.text === 'template') {
144
+ const template = templateTextOf(value);
145
+ if (template !== undefined) {
146
+ const stats = scanTemplate(template.text);
147
+ const legacyOffset = firstLegacyOffset(template.text);
148
+ facts.inlineTemplates.push({
149
+ path,
150
+ inline: true,
151
+ stats,
152
+ firstLegacyLine: legacyOffset === undefined ? undefined : source.getLineAndCharacterOfPosition(template.start + legacyOffset).line + 1,
153
+ });
154
+ }
155
+ }
156
+ }
157
+ }
158
+ facts.components.push(component);
159
+ }
160
+ }
161
+ };
162
+ visit(source);
163
+ return facts;
164
+ }
165
+ /** Turns an import or re-export statement into an `ImportFact`; undefined for any other statement. */
166
+ function importFactOf(statement, source) {
167
+ const line = source.getLineAndCharacterOfPosition(statement.getStart(source)).line + 1;
168
+ if (ts.isImportDeclaration(statement) && ts.isStringLiteralLike(statement.moduleSpecifier)) {
169
+ const clause = statement.importClause;
170
+ const bindings = [];
171
+ const clauseTypeOnly = clause?.isTypeOnly ?? false;
172
+ if (clause?.name)
173
+ bindings.push({ imported: 'default', local: clause.name.text, typeOnly: clauseTypeOnly });
174
+ const named = clause?.namedBindings;
175
+ if (named && ts.isNamespaceImport(named))
176
+ bindings.push({ imported: '*', local: named.name.text, typeOnly: clauseTypeOnly });
177
+ if (named && ts.isNamedImports(named)) {
178
+ for (const element of named.elements) {
179
+ bindings.push({ imported: (element.propertyName ?? element.name).text, local: element.name.text, typeOnly: clauseTypeOnly || element.isTypeOnly });
180
+ }
181
+ }
182
+ return {
183
+ specifier: statement.moduleSpecifier.text,
184
+ line,
185
+ typeOnly: clauseTypeOnly || (bindings.length > 0 && bindings.every((binding) => binding.typeOnly)),
186
+ dynamic: false,
187
+ bindings,
188
+ };
189
+ }
190
+ if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteralLike(statement.moduleSpecifier)) {
191
+ const clause = statement.exportClause;
192
+ const bindings = clause && ts.isNamedExports(clause)
193
+ ? clause.elements.map((element) => ({
194
+ imported: (element.propertyName ?? element.name).text,
195
+ // A re-export binds nothing locally; the empty name keeps it out of the local lookup.
196
+ local: '',
197
+ typeOnly: statement.isTypeOnly || element.isTypeOnly,
198
+ }))
199
+ : [];
200
+ return {
201
+ specifier: statement.moduleSpecifier.text,
202
+ line,
203
+ typeOnly: statement.isTypeOnly || (bindings.length > 0 && bindings.every((binding) => binding.typeOnly)),
204
+ dynamic: false,
205
+ bindings,
206
+ };
207
+ }
208
+ return undefined;
209
+ }
210
+ /**
211
+ * True for `spyOn(...)` and `jasmine.x(...)` when neither name is imported. An
212
+ * imported `spyOn` (some Vitest setups re-export one) is not Jasmine.
213
+ */
214
+ function isJasmineGlobalCall(expression, locals) {
215
+ if (ts.isIdentifier(expression))
216
+ return (expression.text === 'spyOn' || expression.text === 'spyOnProperty') && !locals.has(expression.text);
217
+ return ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === 'jasmine' && !locals.has('jasmine');
218
+ }
219
+ /**
220
+ * The text of an inline template and the offset of its first character in the
221
+ * file. For a template literal with `${}` substitutions only the literal parts
222
+ * are kept, which is enough for counting control flow and rare in practice.
223
+ */
224
+ function templateTextOf(value) {
225
+ if (ts.isStringLiteral(value) || ts.isNoSubstitutionTemplateLiteral(value))
226
+ return { text: value.text, start: value.getStart() + 1 };
227
+ if (ts.isTemplateExpression(value)) {
228
+ return { text: [value.head.text, ...value.templateSpans.map((span) => span.literal.text)].join(' '), start: value.getStart() + 1 };
229
+ }
230
+ return undefined;
231
+ }
232
+ /** Counts control flow in one template's text. */
233
+ export function scanTemplate(text) {
234
+ const count = (pattern) => (text.match(pattern) ?? []).length;
235
+ return {
236
+ ngIf: count(/\*ngIf\s*=|\[ngIf\]\s*=/g),
237
+ ngFor: count(/\*ngFor\s*=|\[ngForOf\]\s*=/g),
238
+ ngSwitch: count(/\[ngSwitch\]\s*=/g),
239
+ ifBlocks: count(/@if\s*\(/g),
240
+ forBlocks: count(/@for\s*\(/g),
241
+ switchBlocks: count(/@switch\s*\(/g),
242
+ deferBlocks: count(/@defer\b/g),
243
+ anyCasts: count(/\$any\s*\(/g),
244
+ };
245
+ }
246
+ /** Character offset of the first legacy structural directive in a template, or undefined. */
247
+ export function firstLegacyOffset(text) {
248
+ const match = /\*ngIf\s*=|\[ngIf\]\s*=|\*ngFor\s*=|\[ngForOf\]\s*=|\[ngSwitch\]\s*=/.exec(text);
249
+ return match?.index;
250
+ }
251
+ /** Scans an external template file. */
252
+ export function scanTemplateFile(root, absolutePath) {
253
+ const text = readFileSync(absolutePath, 'utf8');
254
+ const offset = firstLegacyOffset(text);
255
+ return {
256
+ path: toReportPath(root, absolutePath),
257
+ inline: false,
258
+ stats: scanTemplate(text),
259
+ firstLegacyLine: offset === undefined ? undefined : text.slice(0, offset).split('\n').length,
260
+ };
261
+ }
262
+ /** Non-blank lines, the figure a reader expects behind "lines of code". */
263
+ function countLinesOfCode(text) {
264
+ let count = 0;
265
+ for (const line of text.split('\n'))
266
+ if (line.trim() !== '')
267
+ count++;
268
+ return count;
269
+ }
270
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
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
+ }
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,70 @@
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
+ targets: TargetInfo[];
31
+ /** Every polyfill entry of every target, which is where `zone.js` is wired in for CLI 15+ projects. */
32
+ polyfills: string[];
33
+ /** The tsconfig the application is compiled with, used for the strictness flags. */
34
+ appTsconfig?: string;
35
+ /** The tsconfig holding repository-wide `paths`, used to resolve imports for the dependency graph. */
36
+ rootTsconfig?: string;
37
+ }
38
+ /**
39
+ * Reads the repository-level configuration: `package.json`, `angular.json` or
40
+ * the Nx `project.json` files, and the tsconfig files that matter.
41
+ *
42
+ * Every read is defensive. A repository is analysed as far as it can be, so a
43
+ * missing or malformed file leaves the corresponding field empty instead of
44
+ * aborting the run; the checks that depend on it then grade `info`.
45
+ *
46
+ * @param projectJsons The `project.json` paths the file walk already found, so
47
+ * an Nx workspace is not walked a second time.
48
+ */
49
+ export declare function readWorkspace(root: string, projectJsons: string[]): WorkspaceInfo;
50
+ /**
51
+ * The major version in a version or a range: `^17.3.0` gives 17, `~22.0.8`
52
+ * gives 22, `22.0.8` gives 22. Undefined for anything without a leading number,
53
+ * such as `latest`, a git URL or a `workspace:` protocol.
54
+ */
55
+ export declare function majorOf(versionOrRange: string | undefined): number | undefined;
56
+ /**
57
+ * The `version` field of an installed package, or undefined when the package
58
+ * is not in `node_modules`. Reads `package.json` directly instead of using
59
+ * `require.resolve`, which would honour the analysed package's `exports` map
60
+ * and refuse a path it does not export.
61
+ */
62
+ export declare function readInstalledVersion(root: string, packageName: string): string | undefined;
63
+ /**
64
+ * Parses a JSON file that may contain comments and trailing commas, which
65
+ * `angular.json`, `project.json` and every tsconfig are allowed to. Returns
66
+ * undefined for a missing file, a parse error, or anything that is not an object.
67
+ */
68
+ export declare function readJson(path: string): Record<string, unknown> | undefined;
69
+ /** Narrows an unknown JSON value to an object, treating anything else as empty. */
70
+ export declare function asRecord(value: unknown): Record<string, unknown>;
@@ -0,0 +1,155 @@
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
+ targets,
35
+ polyfills: targets.flatMap((target) => polyfillsOf(target.options)),
36
+ appTsconfig: findAppTsconfig(root, targets),
37
+ rootTsconfig: ['tsconfig.base.json', 'tsconfig.json'].map((name) => join(root, name)).find((path) => existsSync(path)),
38
+ };
39
+ }
40
+ /**
41
+ * The major version in a version or a range: `^17.3.0` gives 17, `~22.0.8`
42
+ * gives 22, `22.0.8` gives 22. Undefined for anything without a leading number,
43
+ * such as `latest`, a git URL or a `workspace:` protocol.
44
+ */
45
+ export function majorOf(versionOrRange) {
46
+ const match = versionOrRange?.match(/(\d+)\.\d+/) ?? versionOrRange?.match(/^[\^~>=<\s]*(\d+)$/);
47
+ return match ? Number(match[1]) : undefined;
48
+ }
49
+ /**
50
+ * The `version` field of an installed package, or undefined when the package
51
+ * is not in `node_modules`. Reads `package.json` directly instead of using
52
+ * `require.resolve`, which would honour the analysed package's `exports` map
53
+ * and refuse a path it does not export.
54
+ */
55
+ export function readInstalledVersion(root, packageName) {
56
+ const version = readJson(join(root, 'node_modules', packageName, 'package.json'))?.['version'];
57
+ return typeof version === 'string' ? version : undefined;
58
+ }
59
+ /** Targets from `angular.json`, which calls them `architect` in older files and `targets` in newer ones. */
60
+ function readAngularJsonTargets(root, angularJson) {
61
+ const projects = asRecord(angularJson['projects']);
62
+ const targets = [];
63
+ for (const [projectName, value] of Object.entries(projects)) {
64
+ const project = asRecord(value);
65
+ const projectRoot = typeof project['root'] === 'string' ? project['root'] : '';
66
+ const declared = asRecord(project['architect'] ?? project['targets']);
67
+ for (const [targetName, target] of Object.entries(declared)) {
68
+ const builder = asRecord(target)['builder'];
69
+ if (typeof builder !== 'string')
70
+ continue;
71
+ // angular.json resolves option paths against the workspace root, not the project root.
72
+ targets.push({ project: projectName || projectRoot, target: targetName, builder, options: asRecord(asRecord(target)['options']), baseDirectory: root });
73
+ }
74
+ }
75
+ return targets;
76
+ }
77
+ /** Targets from Nx `project.json` files, which name the builder `executor`. */
78
+ function readProjectJsonTargets(projectJsons) {
79
+ const targets = [];
80
+ for (const path of projectJsons) {
81
+ const project = readJson(path);
82
+ if (!project)
83
+ continue;
84
+ const projectName = typeof project['name'] === 'string' ? project['name'] : basename(dirname(path));
85
+ for (const [targetName, target] of Object.entries(asRecord(project['targets']))) {
86
+ const record = asRecord(target);
87
+ const builder = record['executor'] ?? record['builder'];
88
+ if (typeof builder !== 'string')
89
+ continue;
90
+ // Nx resolves option paths against the workspace root as well, which is
91
+ // two or more levels above the project.json in a typical apps/ layout.
92
+ targets.push({ project: projectName, target: targetName, builder, options: asRecord(record['options']), baseDirectory: findWorkspaceRoot(path) });
93
+ }
94
+ }
95
+ return targets;
96
+ }
97
+ /** The nearest ancestor of `path` holding `nx.json`, falling back to the file's own directory. */
98
+ function findWorkspaceRoot(path) {
99
+ let directory = dirname(path);
100
+ while (true) {
101
+ if (existsSync(join(directory, 'nx.json')))
102
+ return directory;
103
+ const parent = dirname(directory);
104
+ if (parent === directory)
105
+ return dirname(path);
106
+ directory = parent;
107
+ }
108
+ }
109
+ /** `polyfills` is a string in CLI 15–16 projects and an array from CLI 17 on. */
110
+ function polyfillsOf(options) {
111
+ const polyfills = options['polyfills'];
112
+ if (typeof polyfills === 'string')
113
+ return [polyfills];
114
+ return Array.isArray(polyfills) ? polyfills.filter((entry) => typeof entry === 'string') : [];
115
+ }
116
+ /**
117
+ * The application's tsconfig: the `tsConfig` option of the first application
118
+ * build target, then a root `tsconfig.app.json`, then the root `tsconfig.json`.
119
+ *
120
+ * A library's build target is skipped on purpose. Libraries are commonly built
121
+ * with laxer settings than the app, and the app config is what decides how
122
+ * strictly the shipped code is checked.
123
+ */
124
+ function findAppTsconfig(root, targets) {
125
+ const applicationBuild = targets.find((target) => target.target === 'build' && /:(application|browser|browser-esbuild|webpack-browser)$/.test(target.builder));
126
+ const fromTarget = applicationBuild?.options['tsConfig'];
127
+ if (applicationBuild && typeof fromTarget === 'string') {
128
+ const path = resolve(applicationBuild.baseDirectory, fromTarget);
129
+ if (existsSync(path))
130
+ return path;
131
+ }
132
+ return ['tsconfig.app.json', 'tsconfig.json'].map((name) => join(root, name)).find((path) => existsSync(path));
133
+ }
134
+ /**
135
+ * Parses a JSON file that may contain comments and trailing commas, which
136
+ * `angular.json`, `project.json` and every tsconfig are allowed to. Returns
137
+ * undefined for a missing file, a parse error, or anything that is not an object.
138
+ */
139
+ export function readJson(path) {
140
+ if (!existsSync(path))
141
+ return undefined;
142
+ const parsed = ts.parseConfigFileTextToJson(path, readFileSync(path, 'utf8'));
143
+ if (parsed.error || typeof parsed.config !== 'object' || parsed.config === null)
144
+ return undefined;
145
+ return parsed.config;
146
+ }
147
+ /** Narrows an unknown JSON value to an object, treating anything else as empty. */
148
+ export function asRecord(value) {
149
+ return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {};
150
+ }
151
+ /** Narrows a dependency map, dropping any entry whose version is not a string. */
152
+ function asStringRecord(value) {
153
+ return Object.fromEntries(Object.entries(asRecord(value)).filter((entry) => typeof entry[1] === 'string'));
154
+ }
155
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}