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.
- package/LICENSE +21 -0
- package/README.md +273 -2
- package/dist/checks/change-detection.d.ts +22 -0
- package/dist/checks/change-detection.js +99 -0
- package/dist/checks/change-detection.js.map +1 -0
- package/dist/checks/circular-deps.d.ts +13 -0
- package/dist/checks/circular-deps.js +43 -0
- package/dist/checks/circular-deps.js.map +1 -0
- package/dist/checks/components.d.ts +19 -0
- package/dist/checks/components.js +97 -0
- package/dist/checks/components.js.map +1 -0
- package/dist/checks/context.d.ts +51 -0
- package/dist/checks/context.js +53 -0
- package/dist/checks/context.js.map +1 -0
- package/dist/checks/deprecated-apis.d.ts +14 -0
- package/dist/checks/deprecated-apis.js +141 -0
- package/dist/checks/deprecated-apis.js.map +1 -0
- package/dist/checks/state.d.ts +13 -0
- package/dist/checks/state.js +72 -0
- package/dist/checks/state.js.map +1 -0
- package/dist/checks/testing.d.ts +13 -0
- package/dist/checks/testing.js +98 -0
- package/dist/checks/testing.js.map +1 -0
- package/dist/checks/type-safety.d.ts +14 -0
- package/dist/checks/type-safety.js +117 -0
- package/dist/checks/type-safety.js.map +1 -0
- package/dist/checks/version.d.ts +12 -0
- package/dist/checks/version.js +107 -0
- package/dist/checks/version.js.map +1 -0
- package/dist/cli.d.ts +65 -0
- package/dist/cli.js +285 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +93 -0
- package/dist/config.js.map +1 -0
- package/dist/deprecations.d.ts +30 -0
- package/dist/deprecations.js +279 -0
- package/dist/deprecations.js.map +1 -0
- package/dist/files.d.ts +33 -0
- package/dist/files.js +98 -0
- package/dist/files.js.map +1 -0
- package/dist/graph.d.ts +44 -0
- package/dist/graph.js +151 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +78 -0
- package/dist/init.js +220 -0
- package/dist/init.js.map +1 -0
- package/dist/report/html.d.ts +14 -0
- package/dist/report/html.js +210 -0
- package/dist/report/html.js.map +1 -0
- package/dist/report/markdown.d.ts +8 -0
- package/dist/report/markdown.js +55 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/report/summary.d.ts +45 -0
- package/dist/report/summary.js +65 -0
- package/dist/report/summary.js.map +1 -0
- package/dist/report/terminal.d.ts +17 -0
- package/dist/report/terminal.js +70 -0
- package/dist/report/terminal.js.map +1 -0
- package/dist/scan.d.ts +119 -0
- package/dist/scan.js +270 -0
- package/dist/scan.js.map +1 -0
- package/dist/types.d.ts +97 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/workspace.d.ts +76 -0
- package/dist/workspace.js +156 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +67 -4
- package/schematics/collection.json +10 -0
- package/schematics/ng-add/index.cjs +60 -0
- package/schematics/ng-add/schema.json +29 -0
package/dist/graph.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { SourceFileFacts } from './scan.js';
|
|
3
|
+
/** A group of files that import each other in a loop, directly or through others. */
|
|
4
|
+
export interface DependencyTangle {
|
|
5
|
+
/** Report paths of every file in the group, sorted. */
|
|
6
|
+
files: string[];
|
|
7
|
+
/**
|
|
8
|
+
* One concrete loop through the group, first file repeated at the end
|
|
9
|
+
* (`a.ts → b.ts → a.ts`), so a reader can see where to cut. A large group
|
|
10
|
+
* holds many loops; this is the shortest one through its first file.
|
|
11
|
+
*/
|
|
12
|
+
example: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Finds the import cycles among the analysed files.
|
|
16
|
+
*
|
|
17
|
+
* Counts groups (strongly connected components) rather than individual cycles.
|
|
18
|
+
* Two files that import each other plus a third that closes a second loop
|
|
19
|
+
* through them are one problem to untangle, while a cycle count would call it
|
|
20
|
+
* two or more and grows combinatorially with the size of the group, which is
|
|
21
|
+
* why tools that list every cycle report numbers nobody can act on.
|
|
22
|
+
*
|
|
23
|
+
* Only eager, value-level edges count: `import type` is erased at compile time
|
|
24
|
+
* and `import()` loads later, so neither can produce the initialisation-order
|
|
25
|
+
* crash a cycle is dangerous for. Spec files are left out because nothing
|
|
26
|
+
* imports them.
|
|
27
|
+
*
|
|
28
|
+
* @param compilerOptions The repository's compiler options, for `paths`
|
|
29
|
+
* aliases. Module resolution itself is forced to `bundler`, which is how the
|
|
30
|
+
* Angular CLI resolves and which needs no file extensions.
|
|
31
|
+
*/
|
|
32
|
+
export declare function findTangles(files: SourceFileFacts[], compilerOptions: ts.CompilerOptions): DependencyTangle[];
|
|
33
|
+
/**
|
|
34
|
+
* Tarjan's algorithm, written iteratively. The recursive textbook version
|
|
35
|
+
* overflows the stack on a long import chain, and a 3 000-file app has those.
|
|
36
|
+
*/
|
|
37
|
+
export declare function stronglyConnectedComponents(edges: Map<string, string[]>): string[][];
|
|
38
|
+
/**
|
|
39
|
+
* Compiler options from the repository's root tsconfig, `extends` included,
|
|
40
|
+
* which is where Nx and many CLI workspaces keep their `paths` aliases.
|
|
41
|
+
* Returns empty options when there is no tsconfig or it cannot be parsed; the
|
|
42
|
+
* graph then still resolves every relative import.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readCompilerOptions(tsconfigPath: string | undefined): ts.CompilerOptions;
|
package/dist/graph.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
/**
|
|
4
|
+
* Finds the import cycles among the analysed files.
|
|
5
|
+
*
|
|
6
|
+
* Counts groups (strongly connected components) rather than individual cycles.
|
|
7
|
+
* Two files that import each other plus a third that closes a second loop
|
|
8
|
+
* through them are one problem to untangle, while a cycle count would call it
|
|
9
|
+
* two or more and grows combinatorially with the size of the group, which is
|
|
10
|
+
* why tools that list every cycle report numbers nobody can act on.
|
|
11
|
+
*
|
|
12
|
+
* Only eager, value-level edges count: `import type` is erased at compile time
|
|
13
|
+
* and `import()` loads later, so neither can produce the initialisation-order
|
|
14
|
+
* crash a cycle is dangerous for. Spec files are left out because nothing
|
|
15
|
+
* imports them.
|
|
16
|
+
*
|
|
17
|
+
* @param compilerOptions The repository's compiler options, for `paths`
|
|
18
|
+
* aliases. Module resolution itself is forced to `bundler`, which is how the
|
|
19
|
+
* Angular CLI resolves and which needs no file extensions.
|
|
20
|
+
*/
|
|
21
|
+
export function findTangles(files, compilerOptions) {
|
|
22
|
+
const sources = files.filter((file) => !file.isSpec);
|
|
23
|
+
const byAbsolute = new Map(sources.map((file) => [resolve(file.absolutePath), file]));
|
|
24
|
+
const options = { ...compilerOptions, moduleResolution: ts.ModuleResolutionKind.Bundler, module: ts.ModuleKind.ESNext, allowJs: false };
|
|
25
|
+
const cache = ts.createModuleResolutionCache(process.cwd(), (name) => name, options);
|
|
26
|
+
const edges = new Map();
|
|
27
|
+
for (const file of sources) {
|
|
28
|
+
const targets = new Set();
|
|
29
|
+
for (const fact of file.imports) {
|
|
30
|
+
if (fact.typeOnly || fact.dynamic)
|
|
31
|
+
continue;
|
|
32
|
+
// Bare specifiers that are not path aliases resolve into node_modules and are dropped below;
|
|
33
|
+
// skipping the obvious ones first saves most of the resolution work.
|
|
34
|
+
if (!fact.specifier.startsWith('.') && !options.paths)
|
|
35
|
+
continue;
|
|
36
|
+
const resolved = ts.resolveModuleName(fact.specifier, file.absolutePath, options, ts.sys, cache).resolvedModule;
|
|
37
|
+
if (!resolved || resolved.isExternalLibraryImport)
|
|
38
|
+
continue;
|
|
39
|
+
const target = resolve(resolved.resolvedFileName);
|
|
40
|
+
if (byAbsolute.has(target))
|
|
41
|
+
targets.add(target);
|
|
42
|
+
}
|
|
43
|
+
edges.set(resolve(file.absolutePath), [...targets]);
|
|
44
|
+
}
|
|
45
|
+
return stronglyConnectedComponents(edges)
|
|
46
|
+
.filter((component) => component.length > 1 || (edges.get(component[0]) ?? []).includes(component[0]))
|
|
47
|
+
.map((component) => {
|
|
48
|
+
const members = new Set(component);
|
|
49
|
+
const sorted = component.map((path) => byAbsolute.get(path).path).sort();
|
|
50
|
+
const start = component.find((path) => byAbsolute.get(path).path === sorted[0]);
|
|
51
|
+
return { files: sorted, example: shortestLoop(start, edges, members).map((path) => byAbsolute.get(path).path) };
|
|
52
|
+
})
|
|
53
|
+
.sort((left, right) => right.files.length - left.files.length || left.files[0].localeCompare(right.files[0]));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Tarjan's algorithm, written iteratively. The recursive textbook version
|
|
57
|
+
* overflows the stack on a long import chain, and a 3 000-file app has those.
|
|
58
|
+
*/
|
|
59
|
+
export function stronglyConnectedComponents(edges) {
|
|
60
|
+
let counter = 0;
|
|
61
|
+
const index = new Map();
|
|
62
|
+
const lowLink = new Map();
|
|
63
|
+
const onStack = new Set();
|
|
64
|
+
const stack = [];
|
|
65
|
+
const components = [];
|
|
66
|
+
for (const start of edges.keys()) {
|
|
67
|
+
if (index.has(start))
|
|
68
|
+
continue;
|
|
69
|
+
const work = [{ node: start, next: 0 }];
|
|
70
|
+
index.set(start, counter);
|
|
71
|
+
lowLink.set(start, counter++);
|
|
72
|
+
stack.push(start);
|
|
73
|
+
onStack.add(start);
|
|
74
|
+
while (work.length > 0) {
|
|
75
|
+
const frame = work[work.length - 1];
|
|
76
|
+
const successors = edges.get(frame.node) ?? [];
|
|
77
|
+
if (frame.next < successors.length) {
|
|
78
|
+
const successor = successors[frame.next++];
|
|
79
|
+
if (!index.has(successor)) {
|
|
80
|
+
index.set(successor, counter);
|
|
81
|
+
lowLink.set(successor, counter++);
|
|
82
|
+
stack.push(successor);
|
|
83
|
+
onStack.add(successor);
|
|
84
|
+
work.push({ node: successor, next: 0 });
|
|
85
|
+
}
|
|
86
|
+
else if (onStack.has(successor)) {
|
|
87
|
+
lowLink.set(frame.node, Math.min(lowLink.get(frame.node), index.get(successor)));
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
work.pop();
|
|
92
|
+
const parent = work[work.length - 1];
|
|
93
|
+
if (parent)
|
|
94
|
+
lowLink.set(parent.node, Math.min(lowLink.get(parent.node), lowLink.get(frame.node)));
|
|
95
|
+
if (lowLink.get(frame.node) === index.get(frame.node)) {
|
|
96
|
+
const component = [];
|
|
97
|
+
let member;
|
|
98
|
+
do {
|
|
99
|
+
member = stack.pop();
|
|
100
|
+
onStack.delete(member);
|
|
101
|
+
component.push(member);
|
|
102
|
+
} while (member !== frame.node);
|
|
103
|
+
components.push(component);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return components;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The shortest path from `start` back to itself inside one component, by
|
|
111
|
+
* breadth-first search. Always finds one, since every node of a strongly
|
|
112
|
+
* connected component lies on a loop.
|
|
113
|
+
*/
|
|
114
|
+
function shortestLoop(start, edges, members) {
|
|
115
|
+
const previous = new Map();
|
|
116
|
+
const queue = [start];
|
|
117
|
+
for (let head = 0; head < queue.length; head++) {
|
|
118
|
+
const node = queue[head];
|
|
119
|
+
for (const successor of edges.get(node) ?? []) {
|
|
120
|
+
if (!members.has(successor))
|
|
121
|
+
continue;
|
|
122
|
+
if (successor === start) {
|
|
123
|
+
// Walk back from the node that closes the loop; a self-import leaves the chain empty.
|
|
124
|
+
const chain = [];
|
|
125
|
+
for (let step = node; step !== undefined && step !== start; step = previous.get(step))
|
|
126
|
+
chain.unshift(step);
|
|
127
|
+
return [start, ...chain, start];
|
|
128
|
+
}
|
|
129
|
+
if (!previous.has(successor) && successor !== start) {
|
|
130
|
+
previous.set(successor, node);
|
|
131
|
+
queue.push(successor);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return [start, start];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Compiler options from the repository's root tsconfig, `extends` included,
|
|
139
|
+
* which is where Nx and many CLI workspaces keep their `paths` aliases.
|
|
140
|
+
* Returns empty options when there is no tsconfig or it cannot be parsed; the
|
|
141
|
+
* graph then still resolves every relative import.
|
|
142
|
+
*/
|
|
143
|
+
export function readCompilerOptions(tsconfigPath) {
|
|
144
|
+
if (!tsconfigPath)
|
|
145
|
+
return {};
|
|
146
|
+
const config = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
147
|
+
if (config.error)
|
|
148
|
+
return {};
|
|
149
|
+
return ts.parseJsonConfigFileContent(config.config, ts.sys, dirname(tsconfigPath), undefined, tsconfigPath).options;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,MAAM,YAAY,CAAC;AAe5B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CAAC,KAAwB,EAAE,eAAmC;IACvF,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACtF,MAAM,OAAO,GAAuB,EAAE,GAAG,eAAe,EAAE,gBAAgB,EAAE,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5J,MAAM,KAAK,GAAG,EAAE,CAAC,2BAA2B,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAErF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO;gBAAE,SAAS;YAC5C,6FAA6F;YAC7F,qEAAqE;YACrE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK;gBAAE,SAAS;YAChE,MAAM,QAAQ,GAAG,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,cAAc,CAAC;YAChH,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,uBAAuB;gBAAE,SAAS;YAC5D,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;YAClD,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAClD,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,2BAA2B,CAAC,KAAK,CAAC;SACtC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC;SACvG,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;QACjB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1E,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAE,CAAC;QAClF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC;IACnH,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC;AACpH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,KAA4B;IACtE,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAe,EAAE,CAAC;IAElC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QAC/B,MAAM,IAAI,GAA0C,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/E,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEnB,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;YACrC,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;gBACnC,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,CAAE,CAAC;gBAC5C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC1B,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;oBAC9B,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;oBAClC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACtB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;oBACvB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC1C,CAAC;qBAAM,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;oBAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC,CAAC,CAAC;gBACrF,CAAC;gBACD,SAAS;YACX,CAAC;YACD,IAAI,CAAC,GAAG,EAAE,CAAC;YACX,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACrC,IAAI,MAAM;gBAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAE,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC;YACpG,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtD,MAAM,SAAS,GAAa,EAAE,CAAC;gBAC/B,IAAI,MAAc,CAAC;gBACnB,GAAG,CAAC;oBACF,MAAM,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;oBACtB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBACvB,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACzB,CAAC,QAAQ,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;gBAChC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAa,EAAE,KAA4B,EAAE,OAAoB;IACrF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAE,CAAC;QAC1B,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,SAAS;YACtC,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;gBACxB,sFAAsF;gBACtF,MAAM,KAAK,GAAa,EAAE,CAAC;gBAC3B,KAAK,IAAI,IAAI,GAAuB,IAAI,EAAE,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK,EAAE,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC/H,OAAO,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;YAClC,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;gBACpD,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;gBAC9B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAgC;IAClE,IAAI,CAAC,YAAY;QAAE,OAAO,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,EAAE,CAAC,cAAc,CAAC,YAAY,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,EAAE,CAAC,0BAA0B,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC;AACtH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AnalysisContext, Check } from './checks/context.js';
|
|
2
|
+
import type { CheckupOptions, Report } from './types.js';
|
|
3
|
+
export type * from './types.js';
|
|
4
|
+
export { renderHtml } from './report/html.js';
|
|
5
|
+
export { renderMarkdown } from './report/markdown.js';
|
|
6
|
+
export { overallScore, topPriorities } from './report/summary.js';
|
|
7
|
+
export { CONFIG_FILE, loadConfig, type CheckupConfig } from './config.js';
|
|
8
|
+
export { planInit, type InitChange, type InitOptions, type InitPlan } from './init.js';
|
|
9
|
+
/**
|
|
10
|
+
* The newest Angular major this release of the tool knows about. Used only
|
|
11
|
+
* when the registry cannot be reached or `--offline` is set, and the report
|
|
12
|
+
* says so next to the number, because it goes stale with every Angular release.
|
|
13
|
+
*/
|
|
14
|
+
export declare const BUNDLED_LATEST_ANGULAR_MAJOR = 22;
|
|
15
|
+
/**
|
|
16
|
+
* The checks in report order: the upgrade blockers first (version,
|
|
17
|
+
* deprecations), then the modernisation areas, then the structural ones.
|
|
18
|
+
*/
|
|
19
|
+
export declare const CHECKS: Check[];
|
|
20
|
+
/** This package's own name and version, read from its package.json at runtime so the two never drift. */
|
|
21
|
+
export declare const TOOL: {
|
|
22
|
+
name: string;
|
|
23
|
+
version: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Analyses the Angular codebase at `root` and returns the full report.
|
|
27
|
+
*
|
|
28
|
+
* Reads the disk and, unless `offline` is set, makes one request to the npm
|
|
29
|
+
* registry for the latest `@angular/core` version (three-second timeout,
|
|
30
|
+
* falling back to `BUNDLED_LATEST_ANGULAR_MAJOR`). Nothing else leaves the
|
|
31
|
+
* machine: no source, no file names, no telemetry. Never writes anything.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runCheckup(root: string, options?: CheckupOptions): Promise<Report>;
|
|
34
|
+
/** Gathers everything the checks read. Exported for tests that want to inspect the facts. */
|
|
35
|
+
export declare function buildContext(root: string, options: CheckupOptions): Promise<AnalysisContext>;
|
|
36
|
+
/**
|
|
37
|
+
* `angularCompilerOptions` of a tsconfig, merged along its `extends` chain
|
|
38
|
+
* with the child winning. TypeScript's own config parser resolves `extends`
|
|
39
|
+
* for `compilerOptions` only and drops this block, so it is merged by hand.
|
|
40
|
+
* Follows relative `extends` paths (strings or, since TS 5.0, arrays); a
|
|
41
|
+
* package-name `extends` is skipped, since no shared tsconfig package sets
|
|
42
|
+
* Angular's options.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readAngularCompilerOptions(tsconfigPath: string, depth?: number): Record<string, unknown>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { changeDetectionCheck } from './checks/change-detection.js';
|
|
4
|
+
import { circularDepsCheck } from './checks/circular-deps.js';
|
|
5
|
+
import { componentsCheck } from './checks/components.js';
|
|
6
|
+
import { deprecatedApisCheck } from './checks/deprecated-apis.js';
|
|
7
|
+
import { stateCheck } from './checks/state.js';
|
|
8
|
+
import { testingCheck } from './checks/testing.js';
|
|
9
|
+
import { typeSafetyCheck } from './checks/type-safety.js';
|
|
10
|
+
import { versionCheck } from './checks/version.js';
|
|
11
|
+
import { buildDeprecationIndex } from './deprecations.js';
|
|
12
|
+
import { ignoreMatcher } from './config.js';
|
|
13
|
+
import { collectFiles } from './files.js';
|
|
14
|
+
import { findTangles, readCompilerOptions } from './graph.js';
|
|
15
|
+
import { scanSourceFile, scanTemplateFile } from './scan.js';
|
|
16
|
+
import { asRecord, majorOf, readJson, readWorkspace } from './workspace.js';
|
|
17
|
+
// The renderers and the score are public so a script can turn a report from
|
|
18
|
+
// `runCheckup` into the same HTML or Markdown the CLI writes, e.g. to post it
|
|
19
|
+
// as a pull-request comment.
|
|
20
|
+
export { renderHtml } from './report/html.js';
|
|
21
|
+
export { renderMarkdown } from './report/markdown.js';
|
|
22
|
+
export { overallScore, topPriorities } from './report/summary.js';
|
|
23
|
+
export { CONFIG_FILE, loadConfig } from './config.js';
|
|
24
|
+
export { planInit } from './init.js';
|
|
25
|
+
/**
|
|
26
|
+
* The newest Angular major this release of the tool knows about. Used only
|
|
27
|
+
* when the registry cannot be reached or `--offline` is set, and the report
|
|
28
|
+
* says so next to the number, because it goes stale with every Angular release.
|
|
29
|
+
*/
|
|
30
|
+
export const BUNDLED_LATEST_ANGULAR_MAJOR = 22;
|
|
31
|
+
/**
|
|
32
|
+
* The checks in report order: the upgrade blockers first (version,
|
|
33
|
+
* deprecations), then the modernisation areas, then the structural ones.
|
|
34
|
+
*/
|
|
35
|
+
export const CHECKS = [versionCheck, deprecatedApisCheck, changeDetectionCheck, componentsCheck, stateCheck, testingCheck, typeSafetyCheck, circularDepsCheck];
|
|
36
|
+
/** This package's own name and version, read from its package.json at runtime so the two never drift. */
|
|
37
|
+
export const TOOL = readToolInfo();
|
|
38
|
+
/**
|
|
39
|
+
* Analyses the Angular codebase at `root` and returns the full report.
|
|
40
|
+
*
|
|
41
|
+
* Reads the disk and, unless `offline` is set, makes one request to the npm
|
|
42
|
+
* registry for the latest `@angular/core` version (three-second timeout,
|
|
43
|
+
* falling back to `BUNDLED_LATEST_ANGULAR_MAJOR`). Nothing else leaves the
|
|
44
|
+
* machine: no source, no file names, no telemetry. Never writes anything.
|
|
45
|
+
*/
|
|
46
|
+
export async function runCheckup(root, options = {}) {
|
|
47
|
+
const started = Date.now();
|
|
48
|
+
const absoluteRoot = resolve(root);
|
|
49
|
+
const context = await buildContext(absoluteRoot, options);
|
|
50
|
+
const checks = CHECKS.map((check) => check(context));
|
|
51
|
+
const application = context.sources.filter((file) => !file.isSpec);
|
|
52
|
+
return {
|
|
53
|
+
tool: TOOL,
|
|
54
|
+
generatedAt: new Date().toISOString(),
|
|
55
|
+
root: absoluteRoot,
|
|
56
|
+
project: {
|
|
57
|
+
name: context.workspace.name,
|
|
58
|
+
kind: context.workspace.kind,
|
|
59
|
+
angularVersion: context.workspace.angular.installed ?? context.workspace.angular.declared,
|
|
60
|
+
sourceFiles: context.sources.length,
|
|
61
|
+
templateFiles: context.templates.filter((template) => !template.inline).length,
|
|
62
|
+
linesOfCode: application.reduce((sum, file) => sum + file.linesOfCode, 0),
|
|
63
|
+
},
|
|
64
|
+
checks,
|
|
65
|
+
durationMs: Date.now() - started,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Gathers everything the checks read. Exported for tests that want to inspect the facts. */
|
|
69
|
+
export async function buildContext(root, options) {
|
|
70
|
+
const files = collectFiles(root, ignoreMatcher(options.ignore));
|
|
71
|
+
const workspace = readWorkspace(root, files.projectJsons);
|
|
72
|
+
const sources = files.typescript.map((path) => scanSourceFile(root, path));
|
|
73
|
+
const templates = [...files.html.map((path) => scanTemplateFile(root, path)), ...sources.flatMap((file) => file.inlineTemplates)];
|
|
74
|
+
const appCompilerOptions = readCompilerOptions(workspace.appTsconfig);
|
|
75
|
+
return {
|
|
76
|
+
workspace,
|
|
77
|
+
sources,
|
|
78
|
+
templates,
|
|
79
|
+
deprecations: buildDeprecationIndex(root),
|
|
80
|
+
tangles: findTangles(sources, readCompilerOptions(workspace.rootTsconfig)),
|
|
81
|
+
latestAngular: options.offline ? { major: BUNDLED_LATEST_ANGULAR_MAJOR, source: 'bundled' } : await fetchLatestAngularMajor(),
|
|
82
|
+
appCompilerOptions,
|
|
83
|
+
angularCompilerOptions: workspace.appTsconfig ? readAngularCompilerOptions(workspace.appTsconfig) : {},
|
|
84
|
+
maxExamples: options.maxExamples ?? 5,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The latest `@angular/core` major from the npm registry, or the bundled
|
|
89
|
+
* fallback when the request fails or takes longer than three seconds. A slow
|
|
90
|
+
* or offline network must never stall a run that is otherwise local.
|
|
91
|
+
*/
|
|
92
|
+
async function fetchLatestAngularMajor() {
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch('https://registry.npmjs.org/@angular/core/latest', { signal: AbortSignal.timeout(3000) });
|
|
95
|
+
if (response.ok) {
|
|
96
|
+
const major = majorOf(asRecord(await response.json())['version']);
|
|
97
|
+
if (major !== undefined)
|
|
98
|
+
return { major, source: 'registry' };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Offline, blocked by a proxy, or timed out: the bundled value is the answer.
|
|
103
|
+
}
|
|
104
|
+
return { major: BUNDLED_LATEST_ANGULAR_MAJOR, source: 'bundled' };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* `angularCompilerOptions` of a tsconfig, merged along its `extends` chain
|
|
108
|
+
* with the child winning. TypeScript's own config parser resolves `extends`
|
|
109
|
+
* for `compilerOptions` only and drops this block, so it is merged by hand.
|
|
110
|
+
* Follows relative `extends` paths (strings or, since TS 5.0, arrays); a
|
|
111
|
+
* package-name `extends` is skipped, since no shared tsconfig package sets
|
|
112
|
+
* Angular's options.
|
|
113
|
+
*/
|
|
114
|
+
export function readAngularCompilerOptions(tsconfigPath, depth = 0) {
|
|
115
|
+
const config = readJson(tsconfigPath);
|
|
116
|
+
if (!config || depth > 10)
|
|
117
|
+
return {};
|
|
118
|
+
const parents = (Array.isArray(config['extends']) ? config['extends'] : [config['extends']]).filter((entry) => typeof entry === 'string' && entry.startsWith('.'));
|
|
119
|
+
const inherited = parents.reduce((merged, parent) => {
|
|
120
|
+
const path = resolve(dirname(tsconfigPath), parent.endsWith('.json') ? parent : `${parent}.json`);
|
|
121
|
+
return { ...merged, ...readAngularCompilerOptions(path, depth + 1) };
|
|
122
|
+
}, {});
|
|
123
|
+
return { ...inherited, ...asRecord(config['angularCompilerOptions']) };
|
|
124
|
+
}
|
|
125
|
+
/** Reads name and version from this package's package.json, one level above both `src/` and `dist/`. */
|
|
126
|
+
function readToolInfo() {
|
|
127
|
+
try {
|
|
128
|
+
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
129
|
+
return { name: packageJson.name ?? 'ng-checkup', version: packageJson.version ?? '0.0.0' };
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return { name: 'ng-checkup', version: '0.0.0' };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two reads `planInit` needs. `ng-checkup init` backs it with `node:fs`,
|
|
3
|
+
* `ng add` with the schematic's virtual tree, and tests with a plain object,
|
|
4
|
+
* which is why the planner never touches the disk itself.
|
|
5
|
+
*/
|
|
6
|
+
export interface InitFileSystem {
|
|
7
|
+
/** File content, or undefined when the path is missing. Paths are root-relative with `/`. */
|
|
8
|
+
read(path: string): string | undefined;
|
|
9
|
+
/** True for an existing file or directory. */
|
|
10
|
+
exists(path: string): boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Where the CI step goes. `auto` picks from what the repository already has. */
|
|
13
|
+
export type CiChoice = 'auto' | 'github' | 'gitlab' | 'none';
|
|
14
|
+
/** What `planInit` should do. */
|
|
15
|
+
export interface InitOptions {
|
|
16
|
+
/** Defaults to `auto`: GitHub when `.github/` exists, GitLab when `.gitlab-ci.yml` does, else none. */
|
|
17
|
+
ci?: CiChoice;
|
|
18
|
+
/**
|
|
19
|
+
* Add `ng-checkup` to `devDependencies`. Defaults to true. `ng add` passes
|
|
20
|
+
* false because the Angular CLI has already installed and saved it.
|
|
21
|
+
*/
|
|
22
|
+
addDependency?: boolean;
|
|
23
|
+
/** The version to pin, normally this package's own. */
|
|
24
|
+
version: string;
|
|
25
|
+
/** Replace an existing `checkup` script that runs something else. */
|
|
26
|
+
force?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** One file to write. Content is the whole new file. */
|
|
29
|
+
export interface InitChange {
|
|
30
|
+
path: string;
|
|
31
|
+
action: 'create' | 'update';
|
|
32
|
+
content: string;
|
|
33
|
+
/** One line for the user: what this change does. */
|
|
34
|
+
summary: string;
|
|
35
|
+
}
|
|
36
|
+
/** Everything `init` would do, and what the user has to do afterwards. */
|
|
37
|
+
export interface InitPlan {
|
|
38
|
+
changes: InitChange[];
|
|
39
|
+
/** Things left alone or left to the user, one line each. */
|
|
40
|
+
notes: string[];
|
|
41
|
+
/** Commands the user runs next, in order. */
|
|
42
|
+
nextSteps: string[];
|
|
43
|
+
ci: Exclude<CiChoice, 'auto'>;
|
|
44
|
+
packageManager: PackageManager;
|
|
45
|
+
}
|
|
46
|
+
/** The package managers whose lockfiles `init` recognises. */
|
|
47
|
+
export type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
48
|
+
/**
|
|
49
|
+
* Plans what `ng-checkup init` and `ng add ng-checkup` set up in a repository:
|
|
50
|
+
*
|
|
51
|
+
* 1. a `checkup` script in `package.json` (and `ng-checkup` in `devDependencies`),
|
|
52
|
+
* 2. `ng-checkup.config.json` with the local report and an empty `ignore` list,
|
|
53
|
+
* 3. the generated reports in `.gitignore`, when there is one,
|
|
54
|
+
* 4. a CI step: a GitHub Actions workflow, or a GitLab CI file to `include`.
|
|
55
|
+
*
|
|
56
|
+
* Idempotent: on a repository that already has all of it, the plan is empty.
|
|
57
|
+
* It never overwrites a file the user wrote: an existing workflow, config or
|
|
58
|
+
* different `checkup` script is kept and reported in `notes` instead
|
|
59
|
+
* (`force` replaces the script only). It does not edit `.gitlab-ci.yml`
|
|
60
|
+
* either, since merging into someone's pipeline YAML is where a tool does
|
|
61
|
+
* damage; it writes a separate file and says which `include` to add.
|
|
62
|
+
*
|
|
63
|
+
* @throws When there is no `package.json` or it is not valid JSON. Both mean
|
|
64
|
+
* this is not the root of a Node project, and writing anything would be wrong.
|
|
65
|
+
*/
|
|
66
|
+
export declare function planInit(files: InitFileSystem, options: InitOptions): InitPlan;
|
|
67
|
+
/** The package manager, from its lockfile. npm when there is none or only npm's. */
|
|
68
|
+
export declare function detectPackageManager(files: InitFileSystem): PackageManager;
|
|
69
|
+
/** A GitHub Actions workflow for the repository's package manager. */
|
|
70
|
+
export declare function githubWorkflow(manager: PackageManager): string;
|
|
71
|
+
/** A GitLab CI job file for the repository's package manager, meant to be `include`d. */
|
|
72
|
+
export declare function gitlabJob(manager: PackageManager): string;
|
|
73
|
+
/**
|
|
74
|
+
* Serialises `value` with the indentation and final newline of `original`,
|
|
75
|
+
* so `init` changes only the lines it means to and a `git diff` of
|
|
76
|
+
* `package.json` shows exactly the added keys.
|
|
77
|
+
*/
|
|
78
|
+
export declare function formatJsonLike(original: string, value: unknown): string;
|