@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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +60 -41
  3. 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 scanner add /absolute/path/to/groma/plugins/scanners/angular/dist/package
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 importer = programs.find(({ program }) => program.getSourceFile(file) !== undefined);
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.files.add(file);
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({ program: ts.createProgram(loose, { noEmit: true }), files: new Set(loose) });
279
- return programs.map(({ program, files: own }) => {
280
- const owned = [...own].map((file) => program.getSourceFile(file));
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
- for (const value of values) {
28404
- const id = key(value);
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 [...values].sort((left, right) => key(left).localeCompare(key(right)));
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
- ])).values()].sort((left, right) => {
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: [...new Map(input.diagnostics.map((diagnostic) => [
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: array(operation.tokens, "operation.tokens").map((token) => string(token, "operation.token"))
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
- if (name === "package.json") {
28717
- const manifest = JSON.parse(text);
28718
- entries.push(...declaredBins(file, manifest), ...declaredScripts(file, manifest.scripts));
28719
- } else if (name === "angular.json")
28720
- entries.push(...angularEntries(file, text));
28721
- else if (name === "project.json")
28722
- entries.push(...nxEntries(file, text, workspaces));
28723
- else if (file.endsWith(".html"))
28724
- entries.push(...browserEntries(file, text, packages));
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 entries = [...inputs.entries, ...await declaredEntries(root, declarations, packages, workspaces)];
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({ ...observation, files: inventoryFiles, entryPoints });
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 && mine.has(directive.declaration.getSourceFile()))) {
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 = mine.has(node.getSourceFile()) ? enclosingOperation(node) : undefined;
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()}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groma/scanner-angular",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Complementary Angular framework evidence with its own compatible TypeScript tooling.",
5
5
  "private": false,
6
6
  "type": "module",