@groma/scanner-go 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.
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@groma/scanner-go",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Go project evidence through Go language tooling.",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "license": "MIT",
8
8
  "os": [
9
- "win32",
10
9
  "linux",
11
- "darwin"
10
+ "darwin",
11
+ "win32"
12
12
  ],
13
13
  "cpu": [
14
14
  "arm64",
@@ -35,10 +35,7 @@
35
35
  }
36
36
  ],
37
37
  "compatibility": {
38
- "groma": "^0.3.0",
39
- "technologyVersions": {
40
- "go": "1.20.0"
41
- }
38
+ "groma": "^0.3.0"
42
39
  }
43
40
  }
44
41
  }
package/src/index.js CHANGED
@@ -1,9 +1,39 @@
1
1
  // @bun
2
- // plugins/scanners/go/src/adapter.ts
2
+ // plugins/scanners/go/src/index.ts
3
+ import path5 from "path";
4
+
5
+ // plugins/scanners/projects.ts
3
6
  import { execFile } from "child_process";
4
- import { access, realpath } from "fs/promises";
7
+ import { existsSync } from "fs";
5
8
  import path from "path";
6
- import { fileURLToPath } from "url";
9
+ import { promisify } from "util";
10
+ var execute = promisify(execFile);
11
+ var excluded = new Set([
12
+ ".git",
13
+ "node_modules",
14
+ "vendor",
15
+ "target",
16
+ "dist",
17
+ "build",
18
+ "bin",
19
+ "obj",
20
+ ".gradle",
21
+ ".angular",
22
+ "coverage",
23
+ "generated",
24
+ "groma",
25
+ ".groma"
26
+ ]);
27
+ async function projectFiles(root, matches) {
28
+ const { stdout } = await execute("git", ["-C", root, "ls-files", "-z", "--cached", "--others", "--exclude-standard"], { maxBuffer: 64 * 1024 * 1024 });
29
+ return [...new Set(stdout.split("\x00").filter((file) => file && matches(file) && !file.split("/").some((part) => excluded.has(part)) && existsSync(path.join(root, file))))].sort();
30
+ }
31
+
32
+ // plugins/scanners/project-scanner.ts
33
+ import path3 from "path";
34
+
35
+ // plugins/scanners/observations.ts
36
+ import path2 from "path";
7
37
 
8
38
  // packages/scanner/src/index.ts
9
39
  function compare(...values) {
@@ -259,11 +289,85 @@ function sourcePosition(position) {
259
289
  return { position: Number(position) };
260
290
  }
261
291
 
292
+ // plugins/scanners/observations.ts
293
+ function relocateObservation(observation, directory) {
294
+ const file = (value) => path2.posix.normalize(path2.posix.join(directory, value));
295
+ return {
296
+ ...observation,
297
+ roots: observation.roots.map((root) => ({ ...root, ...root.file ? { file: file(root.file) } : {} })),
298
+ files: observation.files.map((source) => ({ ...source, file: file(source.file) })),
299
+ operations: observation.operations?.map((operation) => ({ ...operation, file: file(operation.file) })),
300
+ invocations: observation.invocations?.map((call) => ({
301
+ ...call,
302
+ ...call.binding ? { binding: { ...call.binding, file: file(call.binding.file) } } : {}
303
+ })),
304
+ diagnostics: observation.diagnostics.map((item) => ({ ...item, ...item.file ? { file: file(item.file) } : {} }))
305
+ };
306
+ }
307
+ function combineObservations(parts) {
308
+ if (!parts.length)
309
+ return;
310
+ const roots = [];
311
+ const files = new Map;
312
+ const operations = [];
313
+ const invocations = [];
314
+ const diagnostics = [];
315
+ for (const { key, observation } of parts) {
316
+ const id = (value) => JSON.stringify([key, value]);
317
+ roots.push(...observation.roots.map((root) => ({ ...root, id: id(root.id), ...root.parent ? { parent: id(root.parent) } : {} })));
318
+ for (const source of observation.files) {
319
+ const combined = files.get(source.file) ?? { file: source.file, roots: [], symbols: [] };
320
+ combined.roots.push(...source.roots.map(id));
321
+ combined.symbols.push(...source.symbols.map((symbol) => ({ ...symbol, id: id(symbol.id) })));
322
+ files.set(source.file, combined);
323
+ }
324
+ operations.push(...(observation.operations ?? []).map((operation) => ({ ...operation, id: id(operation.id) })));
325
+ invocations.push(...(observation.invocations ?? []).map((call) => ({ ...call, source: id(call.source), targets: call.targets.map(id) })));
326
+ diagnostics.push(...observation.diagnostics);
327
+ }
328
+ return createScanObservation({
329
+ scanner: parts[0].observation.scanner,
330
+ roots,
331
+ files: [...files.values()],
332
+ operations,
333
+ invocations,
334
+ diagnostics
335
+ });
336
+ }
337
+
338
+ // plugins/scanners/project-scanner.ts
339
+ function projectScanner(scanner, select) {
340
+ return {
341
+ ...scanner,
342
+ checkReadiness: async (root, settings = {}) => {
343
+ const projects = await select(root, settings);
344
+ if (!projects.length)
345
+ throw new Error(`${scanner.id}: No supported project declaration was found.`);
346
+ for (const project of projects)
347
+ await scanner.checkReadiness?.(project, settings);
348
+ },
349
+ scan: async (root, settings = {}) => {
350
+ const parts = [];
351
+ for (const project of await select(root, settings)) {
352
+ const observation = await scanner.scan(project, settings);
353
+ const key = path3.relative(root, project).split(path3.sep).join("/");
354
+ if (observation)
355
+ parts.push({ key, observation: relocateObservation(observation, key) });
356
+ }
357
+ return combineObservations(parts);
358
+ }
359
+ };
360
+ }
361
+
262
362
  // plugins/scanners/go/src/adapter.ts
363
+ import { execFile as execFile2 } from "child_process";
364
+ import { access, realpath } from "fs/promises";
365
+ import path4 from "path";
366
+ import { fileURLToPath } from "url";
263
367
  var packagedWorker = fileURLToPath(new URL(`../dist/${process.platform}-${process.arch}/worker${process.platform === "win32" ? ".exe" : ""}`, import.meta.url));
264
368
  function run(command, args, root, env = process.env) {
265
369
  return new Promise((resolve, reject) => {
266
- execFile(command, args, {
370
+ execFile2(command, args, {
267
371
  cwd: root,
268
372
  env,
269
373
  encoding: "utf8",
@@ -293,13 +397,10 @@ async function checkGoReadiness(repositoryRoot, options = {}) {
293
397
  } catch (error) {
294
398
  throw new Error(`GO_TOOLCHAIN_MISSING: Install the project's Go toolchain and add its bin directory to PATH (Go 1.27.1 for the qualified example). ${error}`);
295
399
  }
296
- if (context.GOWORK && context.GOWORK !== "off") {
297
- throw new Error("GO_PROJECT_SCOPE: This scanner supports one root module. Select the supported module with GOWORK=off; workspace analysis is not qualified.");
298
- }
299
- if (context.GOMOD !== path.join(root, "go.mod")) {
300
- throw new Error("GO_PROJECT_SCOPE: Run Groma at the supported Go module root containing go.mod.");
400
+ if (context.GOMOD !== path4.join(root, "go.mod")) {
401
+ throw new Error("GO_PROJECT_SCOPE: The selected project must contain go.mod.");
301
402
  }
302
- env.PATH = `${path.join(context.GOROOT, "bin")}${path.delimiter}${process.env.PATH ?? ""}`;
403
+ env.PATH = `${path4.join(context.GOROOT, "bin")}${path4.delimiter}${process.env.PATH ?? ""}`;
303
404
  try {
304
405
  await run(options.go ?? "go", ["list", "-mod=readonly", "-deps", "./..."], root, env);
305
406
  } catch (error) {
@@ -317,7 +418,7 @@ async function scanGoSource(repositoryRoot, options = {}) {
317
418
  }
318
419
 
319
420
  // plugins/scanners/go/src/index.ts
320
- var src_default = {
421
+ var scanner = {
321
422
  id: "go",
322
423
  watch: { include: ["**/*.go", "**/go.mod", "**/go.sum", "**/go.work"], exclude: [] },
323
424
  checkReadiness: async (root) => {
@@ -325,6 +426,7 @@ var src_default = {
325
426
  },
326
427
  scan: scanGoSource
327
428
  };
429
+ var src_default = projectScanner(scanner, async (root) => (await projectFiles(root, (file) => path5.posix.basename(file) === "go.mod")).map((file) => path5.dirname(path5.join(root, file))));
328
430
  export {
329
431
  src_default as default
330
432
  };