@groma/scanner-angular 0.2.1 → 0.2.3
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/README.md +1 -1
- package/dist/index.js +60 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ bun plugins/scanners/angular/build.ts
|
|
|
16
16
|
From the project being scanned:
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
groma
|
|
19
|
+
groma plugin add /absolute/path/to/groma/plugins/scanners/angular/dist/package
|
|
20
20
|
groma scan
|
|
21
21
|
```
|
|
22
22
|
|
package/dist/index.js
CHANGED
|
@@ -254,31 +254,33 @@ function configOwners(wanted, configs) {
|
|
|
254
254
|
}
|
|
255
255
|
return owners;
|
|
256
256
|
}
|
|
257
|
+
function programWithOwnedSources(program, paths) {
|
|
258
|
+
return { program, owned: new Set(paths.map((file) => program.getSourceFile(file))) };
|
|
259
|
+
}
|
|
257
260
|
function angularPrograms(root, files, configs) {
|
|
258
261
|
const wanted = new Set(files.map((file) => path3.resolve(root, file)));
|
|
259
262
|
const owners = configOwners(wanted, configs);
|
|
260
263
|
const groups = new Map;
|
|
261
264
|
for (const [file, owner] of owners)
|
|
262
265
|
groups.set(owner, [...groups.get(owner) ?? [], file]);
|
|
263
|
-
const programs = [...groups].map(([owner, rootNames]) => ({
|
|
264
|
-
program: ts.createProgram(rootNames, { ...owner.config.options, noEmit: true }),
|
|
265
|
-
files: new Set(rootNames)
|
|
266
|
-
}));
|
|
266
|
+
const programs = [...groups].map(([owner, rootNames]) => programWithOwnedSources(ts.createProgram(rootNames, { ...owner.config.options, noEmit: true }), rootNames));
|
|
267
267
|
const loose = [];
|
|
268
268
|
for (const file of wanted) {
|
|
269
269
|
if (owners.has(file))
|
|
270
270
|
continue;
|
|
271
|
-
const
|
|
271
|
+
const readers = programs.filter(({ program }) => program.getSourceFile(file) !== undefined);
|
|
272
|
+
if (readers.some(({ program, owned }) => owned.has(program.getSourceFile(file))))
|
|
273
|
+
continue;
|
|
274
|
+
const [importer] = readers;
|
|
272
275
|
if (importer)
|
|
273
|
-
importer.
|
|
276
|
+
importer.owned.add(importer.program.getSourceFile(file));
|
|
274
277
|
else
|
|
275
278
|
loose.push(file);
|
|
276
279
|
}
|
|
277
280
|
if (loose.length)
|
|
278
|
-
programs.push(
|
|
279
|
-
return programs.map(({ program,
|
|
280
|
-
|
|
281
|
-
failDiagnostics(owned.flatMap((source) => program.getSyntacticDiagnostics(source)));
|
|
281
|
+
programs.push(programWithOwnedSources(ts.createProgram(loose, { noEmit: true }), loose));
|
|
282
|
+
return programs.map(({ program, owned }) => {
|
|
283
|
+
failDiagnostics([...owned].flatMap((source) => program.getSyntacticDiagnostics(source)));
|
|
282
284
|
return { program, owned, sources: program.getSourceFiles().filter((source) => inRepository(root, source)) };
|
|
283
285
|
});
|
|
284
286
|
}
|
|
@@ -28400,13 +28402,13 @@ function compare(...values) {
|
|
|
28400
28402
|
}
|
|
28401
28403
|
function uniqueBy(values, key, label) {
|
|
28402
28404
|
const seen = new Set;
|
|
28403
|
-
|
|
28404
|
-
|
|
28405
|
+
const keyed = values.map((value) => ({ id: key(value), value }));
|
|
28406
|
+
for (const { id } of keyed) {
|
|
28405
28407
|
if (seen.has(id))
|
|
28406
28408
|
throw new Error(`duplicate ${label}: ${id}`);
|
|
28407
28409
|
seen.add(id);
|
|
28408
28410
|
}
|
|
28409
|
-
return
|
|
28411
|
+
return keyed.sort((left, right) => left.id.localeCompare(right.id)).map((entry) => entry.value);
|
|
28410
28412
|
}
|
|
28411
28413
|
function createScanObservation(input) {
|
|
28412
28414
|
const roots = validateRoots(input.roots);
|
|
@@ -28417,9 +28419,7 @@ function createScanObservation(input) {
|
|
|
28417
28419
|
symbols: [...new Map(file.symbols.map((symbol) => [
|
|
28418
28420
|
compare(symbol.id, symbol.kind),
|
|
28419
28421
|
symbol
|
|
28420
|
-
]))
|
|
28421
|
-
return compare(left.id, left.kind).localeCompare(compare(right.id, right.kind));
|
|
28422
|
-
})
|
|
28422
|
+
]))].sort(([left], [right]) => left.localeCompare(right)).map(([, symbol]) => symbol)
|
|
28423
28423
|
})), (file) => file.file, "file path");
|
|
28424
28424
|
const filePaths = new Set(files.map((file) => file.file));
|
|
28425
28425
|
for (const file of files) {
|
|
@@ -28430,8 +28430,6 @@ function createScanObservation(input) {
|
|
|
28430
28430
|
throw new Error(`file references unknown root: ${root}`);
|
|
28431
28431
|
}
|
|
28432
28432
|
}
|
|
28433
|
-
for (const diagnostic of input.diagnostics)
|
|
28434
|
-
diagnosticLocation(diagnostic);
|
|
28435
28433
|
return {
|
|
28436
28434
|
schemaVersion: 1,
|
|
28437
28435
|
scanner: input.scanner,
|
|
@@ -28441,14 +28439,14 @@ function createScanObservation(input) {
|
|
|
28441
28439
|
...entryPoints(input.entryPoints, filePaths),
|
|
28442
28440
|
...operationEvidence(input, filePaths),
|
|
28443
28441
|
...httpEvidence(input.httpEndpoints, input.httpRequests, input.operations && new Set(input.operations.map((operation) => operation.id))),
|
|
28444
|
-
diagnostics:
|
|
28445
|
-
diagnosticKey(diagnostic),
|
|
28446
|
-
diagnostic
|
|
28447
|
-
])).values()].sort((left, right) => {
|
|
28448
|
-
return diagnosticKey(left).localeCompare(diagnosticKey(right));
|
|
28449
|
-
})
|
|
28442
|
+
diagnostics: normalizeScanDiagnostics(input.diagnostics)
|
|
28450
28443
|
};
|
|
28451
28444
|
}
|
|
28445
|
+
function normalizeScanDiagnostics(diagnostics) {
|
|
28446
|
+
for (const diagnostic of diagnostics)
|
|
28447
|
+
diagnosticLocation(diagnostic);
|
|
28448
|
+
return [...new Map(diagnostics.map((diagnostic) => [diagnosticKey(diagnostic), diagnostic]))].sort(([left], [right]) => left.localeCompare(right)).map(([, diagnostic]) => diagnostic);
|
|
28449
|
+
}
|
|
28452
28450
|
function entryPoints(input, paths) {
|
|
28453
28451
|
if (input === undefined)
|
|
28454
28452
|
return {};
|
|
@@ -28557,10 +28555,14 @@ function operationTokens(operation) {
|
|
|
28557
28555
|
const endLine = Number(operation.endLine);
|
|
28558
28556
|
if (endLine < startLine)
|
|
28559
28557
|
throw new Error("operation.endLine must be at or after startLine");
|
|
28558
|
+
const tokens = array(operation.tokens, "operation.tokens");
|
|
28559
|
+
tokens.forEach((token) => {
|
|
28560
|
+
string(token, "operation.token");
|
|
28561
|
+
});
|
|
28560
28562
|
return {
|
|
28561
28563
|
startLine,
|
|
28562
28564
|
endLine,
|
|
28563
|
-
tokens
|
|
28565
|
+
tokens
|
|
28564
28566
|
};
|
|
28565
28567
|
}
|
|
28566
28568
|
function validateOperationTokens(operation) {
|
|
@@ -28710,20 +28712,32 @@ function reachedFiles(entry, compiled, imports, packages, units) {
|
|
|
28710
28712
|
}
|
|
28711
28713
|
async function declaredEntries(root, declarations, packages, workspaces) {
|
|
28712
28714
|
const entries = [];
|
|
28715
|
+
const diagnostics = [];
|
|
28713
28716
|
for (const file of declarations) {
|
|
28714
28717
|
const text = await readFile3(path5.join(root, file), "utf8");
|
|
28715
28718
|
const name = path5.posix.basename(file);
|
|
28716
|
-
|
|
28717
|
-
|
|
28718
|
-
|
|
28719
|
-
|
|
28720
|
-
|
|
28721
|
-
|
|
28722
|
-
|
|
28723
|
-
|
|
28724
|
-
|
|
28719
|
+
try {
|
|
28720
|
+
if (name === "package.json") {
|
|
28721
|
+
const manifest = JSON.parse(text);
|
|
28722
|
+
entries.push(...declaredBins(file, manifest), ...declaredScripts(file, manifest.scripts));
|
|
28723
|
+
} else if (name === "angular.json")
|
|
28724
|
+
entries.push(...angularEntries(file, text));
|
|
28725
|
+
else if (name === "project.json")
|
|
28726
|
+
entries.push(...nxEntries(file, text, workspaces));
|
|
28727
|
+
else if (file.endsWith(".html"))
|
|
28728
|
+
entries.push(...browserEntries(file, text, packages));
|
|
28729
|
+
} catch (error) {
|
|
28730
|
+
if (!(error instanceof SyntaxError))
|
|
28731
|
+
throw error;
|
|
28732
|
+
diagnostics.push({
|
|
28733
|
+
severity: "warning",
|
|
28734
|
+
code: "JAVASCRIPT_MANIFEST_INVALID",
|
|
28735
|
+
file,
|
|
28736
|
+
message: `The file is not valid JSON; the entry points it declares are not read. ${error.message}`
|
|
28737
|
+
});
|
|
28738
|
+
}
|
|
28725
28739
|
}
|
|
28726
|
-
return entries;
|
|
28740
|
+
return { entries, diagnostics };
|
|
28727
28741
|
}
|
|
28728
28742
|
function sourced(entry, files, outputs = []) {
|
|
28729
28743
|
const file = sourceOf(entry.file, outputs, files);
|
|
@@ -28737,7 +28751,8 @@ async function withJavaScriptEntries(root, observation, inputs, files) {
|
|
|
28737
28751
|
const packages = new Set(inventory.filter((file) => path5.posix.basename(file) === "package.json").map((file) => path5.posix.dirname(file)));
|
|
28738
28752
|
const active = new Set(observation.files.map((file) => packageFor(file.file, packages)));
|
|
28739
28753
|
const declarations = inventory.filter((file) => path5.posix.basename(file) !== "nx.json" && active.has(packageFor(file, packages)));
|
|
28740
|
-
const
|
|
28754
|
+
const declared = await declaredEntries(root, declarations, packages, workspaces);
|
|
28755
|
+
const entries = [...inputs.entries, ...declared.entries];
|
|
28741
28756
|
const inventoryFiles = [...observation.files];
|
|
28742
28757
|
const visible = new Set(inventoryFiles.map((file) => file.file));
|
|
28743
28758
|
const readable = new Set(files);
|
|
@@ -28754,7 +28769,12 @@ async function withJavaScriptEntries(root, observation, inputs, files) {
|
|
|
28754
28769
|
}
|
|
28755
28770
|
return [{ ...entry, files: [...new Set([entry.file, ...members.map((file) => file.file)])] }];
|
|
28756
28771
|
});
|
|
28757
|
-
return createScanObservation({
|
|
28772
|
+
return createScanObservation({
|
|
28773
|
+
...observation,
|
|
28774
|
+
files: inventoryFiles,
|
|
28775
|
+
entryPoints,
|
|
28776
|
+
diagnostics: [...observation.diagnostics, ...declared.diagnostics]
|
|
28777
|
+
});
|
|
28758
28778
|
}
|
|
28759
28779
|
|
|
28760
28780
|
// plugins/scanners/entry-points/source.ts
|
|
@@ -30039,17 +30059,16 @@ async function scanProject(root, directory, programs, inputs, readable) {
|
|
|
30039
30059
|
const httpRequests = [];
|
|
30040
30060
|
for (const { program, owned, sources } of programs) {
|
|
30041
30061
|
const checker = program.getTypeChecker();
|
|
30042
|
-
const mine = new Set(owned);
|
|
30043
30062
|
const directives = sources.filter((source) => readable.has(relative(root, source.fileName))).flatMap((source) => source.statements.filter(ts6.isClassDeclaration).flatMap((node) => sourceDirective(node, checker) ?? []));
|
|
30044
|
-
for (const component of directives.filter((directive) => directive.view &&
|
|
30063
|
+
for (const component of directives.filter((directive) => directive.view && owned.has(directive.declaration.getSourceFile()))) {
|
|
30045
30064
|
templates.bindOutputs(component, directives, checker);
|
|
30046
30065
|
sourceUnits.push(templates.unit(component));
|
|
30047
30066
|
}
|
|
30048
30067
|
httpRequests.push(...await angularHttpRequests(sources, checker, (node) => {
|
|
30049
|
-
const caller =
|
|
30068
|
+
const caller = owned.has(node.getSourceFile()) ? enclosingOperation(node) : undefined;
|
|
30050
30069
|
return caller && evidence.operationId(caller);
|
|
30051
30070
|
}));
|
|
30052
|
-
files.push(...owned.map((source) => ({
|
|
30071
|
+
files.push(...[...owned].map((source) => ({
|
|
30053
30072
|
file: relative(root, source.fileName),
|
|
30054
30073
|
symbols: source.statements.filter(ts6.isClassDeclaration).map((node) => ({
|
|
30055
30074
|
id: `${relative(root, source.fileName)}#${node.getStart()}`,
|