@groma/scanner-vue 0.1.0 → 0.1.1

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 +11 -1
  2. package/dist/index.js +105 -16
  3. package/package.json +2 -5
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The Vue scanner adds single-file component (SFC) event bindings to Groma's
4
4
  TypeScript scanner evidence. Install the project's dependencies using its
5
- package manager and lockfile. The scanner reads the root `tsconfig.json`;
5
+ package manager and lockfile. The scanner reads each selected project's `tsconfig.json`;
6
6
  it does not install dependencies or execute application code.
7
7
 
8
8
  ```sh
@@ -65,3 +65,13 @@ owns that interpretation. No new architecture metadata is added.
65
65
 
66
66
  See [validation](validation.md) for the pinned project and executed release
67
67
  checks. This prototype package name does not imply public publication.
68
+
69
+ ## Nested projects
70
+
71
+ Run Groma from the repository root. The scanner finds package declarations in
72
+ tracked and unignored files, including nested apps and libraries. Dependencies,
73
+ dev dependencies, peer dependencies and optional dependencies identify framework
74
+ projects. Each compiler uses that project's configuration and installed dependencies;
75
+ imported source in sibling repository libraries keeps its original source path.
76
+ Readiness checks all selected projects. A project error fails the complete scan.
77
+ Source and nested package/configuration changes use the shared scanner watch flow.
package/dist/index.js CHANGED
@@ -208497,12 +208497,54 @@ var import_typescript3 = __toESM(require_typescript(), 1);
208497
208497
  var import_typescript = __toESM(require_typescript2(), 1);
208498
208498
  var import_language_core = __toESM(require_language_core2(), 1);
208499
208499
  var import_typescript2 = __toESM(require_typescript(), 1);
208500
- import { existsSync, readFileSync } from "fs";
208500
+ import { existsSync as existsSync2, readFileSync } from "fs";
208501
208501
  import { createRequire } from "module";
208502
+ import path2 from "path";
208503
+
208504
+ // plugins/scanners/projects.ts
208505
+ import { execFile } from "child_process";
208506
+ import { existsSync } from "fs";
208507
+ import { readFile } from "fs/promises";
208502
208508
  import path from "path";
208509
+ import { promisify } from "util";
208510
+ var execute = promisify(execFile);
208511
+ var excluded = new Set([
208512
+ ".git",
208513
+ "node_modules",
208514
+ "vendor",
208515
+ "target",
208516
+ "dist",
208517
+ "build",
208518
+ "bin",
208519
+ "obj",
208520
+ ".gradle",
208521
+ ".angular",
208522
+ "coverage",
208523
+ "generated",
208524
+ "groma",
208525
+ ".groma"
208526
+ ]);
208527
+ async function projectFiles(root, matches) {
208528
+ const { stdout } = await execute("git", ["-C", root, "ls-files", "-z", "--cached", "--others", "--exclude-standard"], { maxBuffer: 64 * 1024 * 1024 });
208529
+ return [...new Set(stdout.split("\x00").filter((file) => file && matches(file) && !file.split("/").some((part) => excluded.has(part)) && existsSync(path.join(root, file))))].sort();
208530
+ }
208531
+ function hasDependency(manifest, dependency) {
208532
+ return ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"].some((section) => typeof manifest[section]?.[dependency] === "string");
208533
+ }
208534
+ async function frameworkProjects(root, dependency) {
208535
+ const projects = [];
208536
+ for (const file of await projectFiles(root, (file) => path.posix.basename(file) === "package.json")) {
208537
+ const manifest = JSON.parse(await readFile(path.join(root, file), "utf8"));
208538
+ if (hasDependency(manifest, dependency))
208539
+ projects.push(path.dirname(path.join(root, file)));
208540
+ }
208541
+ return projects;
208542
+ }
208543
+
208544
+ // plugins/scanners/vue/src/project.ts
208503
208545
  var vueTypeScript = import_typescript2.default;
208504
208546
  function relative(root, file) {
208505
- return path.relative(root, file).split(path.sep).join("/");
208547
+ return path2.relative(root, file).split(path2.sep).join("/");
208506
208548
  }
208507
208549
  function nodeAt(node, position) {
208508
208550
  let result = node;
@@ -208528,9 +208570,9 @@ class VueProject {
208528
208570
  options;
208529
208571
  language;
208530
208572
  plugin;
208531
- constructor(root) {
208532
- this.root = root;
208533
- const configFile = path.join(root, "tsconfig.json");
208573
+ constructor(root, repositoryRoot = root) {
208574
+ this.root = repositoryRoot;
208575
+ const configFile = path2.join(root, "tsconfig.json");
208534
208576
  const config = import_typescript2.default.readJsonConfigFile(configFile, import_typescript2.default.sys.readFile);
208535
208577
  const vue = import_language_core.createParsedCommandLine(vueTypeScript, import_typescript2.default.sys, configFile);
208536
208578
  this.options = vue.vueOptions;
@@ -208597,18 +208639,18 @@ class VueProject {
208597
208639
  `).length;
208598
208640
  }
208599
208641
  }
208600
- function vueProject(root) {
208601
- const manifestFile = path.join(root, "package.json");
208602
- if (!existsSync(manifestFile))
208642
+ function vueProject(root, repositoryRoot = root) {
208643
+ const manifestFile = path2.join(root, "package.json");
208644
+ if (!existsSync2(manifestFile))
208603
208645
  return;
208604
208646
  const manifest = JSON.parse(readFileSync(manifestFile, "utf8"));
208605
- if (!manifest.dependencies?.vue && !manifest.devDependencies?.vue)
208647
+ if (!hasDependency(manifest, "vue"))
208606
208648
  return;
208607
208649
  try {
208608
208650
  createRequire(manifestFile).resolve("vue/package.json");
208609
- return { manifest, project: new VueProject(root) };
208651
+ return { manifest, project: new VueProject(root, repositoryRoot) };
208610
208652
  } catch (error) {
208611
- throw new Error(`VUE_PROJECT_PREPARATION: Install project dependencies with its declared package manager and lockfile; ensure root tsconfig.json and Vue sources are valid. ${error}`);
208653
+ throw new Error(`VUE_PROJECT_PREPARATION: Install project dependencies with its declared package manager and lockfile; ensure project tsconfig.json and Vue sources are valid. ${error}`);
208612
208654
  }
208613
208655
  }
208614
208656
 
@@ -208782,9 +208824,53 @@ class VueEvidence {
208782
208824
  }
208783
208825
  }
208784
208826
 
208827
+ // plugins/scanners/vue/src/index.ts
208828
+ import path3 from "path";
208829
+
208830
+ // plugins/scanners/observations.ts
208831
+ function combineObservations(parts) {
208832
+ if (!parts.length)
208833
+ return;
208834
+ const roots = [];
208835
+ const files = new Map;
208836
+ const operations = [];
208837
+ const invocations = [];
208838
+ const diagnostics = [];
208839
+ for (const { key, observation } of parts) {
208840
+ const id = (value) => JSON.stringify([key, value]);
208841
+ roots.push(...observation.roots.map((root) => ({ ...root, id: id(root.id), ...root.parent ? { parent: id(root.parent) } : {} })));
208842
+ for (const source of observation.files) {
208843
+ const combined = files.get(source.file) ?? { file: source.file, roots: [], symbols: [] };
208844
+ combined.roots.push(...source.roots.map(id));
208845
+ combined.symbols.push(...source.symbols.map((symbol) => ({ ...symbol, id: id(symbol.id) })));
208846
+ files.set(source.file, combined);
208847
+ }
208848
+ operations.push(...(observation.operations ?? []).map((operation) => ({ ...operation, id: id(operation.id) })));
208849
+ invocations.push(...(observation.invocations ?? []).map((call) => ({ ...call, source: id(call.source), targets: call.targets.map(id) })));
208850
+ diagnostics.push(...observation.diagnostics);
208851
+ }
208852
+ return createScanObservation({
208853
+ scanner: parts[0].observation.scanner,
208854
+ roots,
208855
+ files: [...files.values()],
208856
+ operations,
208857
+ invocations,
208858
+ diagnostics
208859
+ });
208860
+ }
208861
+
208785
208862
  // plugins/scanners/vue/src/index.ts
208786
208863
  async function scanVue(root) {
208787
- const prepared = vueProject(root);
208864
+ const parts = [];
208865
+ for (const project of await frameworkProjects(root, "vue")) {
208866
+ const observation = await scanVueProject(project, root);
208867
+ if (observation)
208868
+ parts.push({ key: relative(root, project), observation });
208869
+ }
208870
+ return combineObservations(parts);
208871
+ }
208872
+ async function scanVueProject(projectRoot, root) {
208873
+ const prepared = vueProject(projectRoot, root);
208788
208874
  if (!prepared)
208789
208875
  return;
208790
208876
  const { manifest, project } = prepared;
@@ -208797,7 +208883,7 @@ async function scanVue(root) {
208797
208883
  const files = project.files.map((source) => ({ file: relative(root, source.fileName), symbols: [] }));
208798
208884
  return createScanObservation({
208799
208885
  scanner: { id: "vue", technology: "typescript/vue", engine: "@vue/language-core", engineVersion: "3.3.11" },
208800
- roots: [{ id: "vue-project", kind: "package", name: manifest.name, file: "package.json" }],
208886
+ roots: [{ id: "vue-project", kind: "package", name: manifest.name, file: relative(root, path3.join(projectRoot, "package.json")) }],
208801
208887
  files: files.map((file) => ({ ...file, roots: ["vue-project"] })),
208802
208888
  operations: [...evidence.operations.values()],
208803
208889
  invocations: evidence.invocations,
@@ -208806,10 +208892,13 @@ async function scanVue(root) {
208806
208892
  }
208807
208893
  var src_default = {
208808
208894
  id: "vue",
208809
- watch: { include: ["**/*.vue", "**/*.ts", "tsconfig.json", "package.json"], exclude: [] },
208895
+ watch: { include: ["**/*.vue", "**/*.ts", "**/tsconfig*.json", "**/package.json"], exclude: [] },
208810
208896
  checkReadiness: async (root) => {
208811
- if (!vueProject(root))
208812
- throw new Error("VUE_PROJECT_REQUIRED: Select a project with vue in its root package.json, or disable the vue scanner.");
208897
+ const projects = await frameworkProjects(root, "vue");
208898
+ if (!projects.length)
208899
+ throw new Error("VUE_PROJECT_REQUIRED: No Vue project declaration was found.");
208900
+ for (const project of projects)
208901
+ vueProject(project, root);
208813
208902
  },
208814
208903
  scan: scanVue
208815
208904
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groma/scanner-vue",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Complementary Vue framework evidence; a dependency declaration does not verify runtime use.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -25,10 +25,7 @@
25
25
  }
26
26
  ],
27
27
  "compatibility": {
28
- "groma": "^0.3.0",
29
- "technologyVersions": {
30
- "vue": "3.5.18 || 3.5.42"
31
- }
28
+ "groma": "^0.3.0"
32
29
  }
33
30
  }
34
31
  }