legion-audit 0.1.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +84 -0
  4. package/dist/args.d.ts +18 -0
  5. package/dist/args.d.ts.map +1 -0
  6. package/dist/args.js +97 -0
  7. package/dist/args.js.map +1 -0
  8. package/dist/audit.d.ts +4 -0
  9. package/dist/audit.d.ts.map +1 -0
  10. package/dist/audit.js +81 -0
  11. package/dist/audit.js.map +1 -0
  12. package/dist/checks/directives.d.ts +8 -0
  13. package/dist/checks/directives.d.ts.map +1 -0
  14. package/dist/checks/directives.js +108 -0
  15. package/dist/checks/directives.js.map +1 -0
  16. package/dist/checks/gates.d.ts +16 -0
  17. package/dist/checks/gates.d.ts.map +1 -0
  18. package/dist/checks/gates.js +153 -0
  19. package/dist/checks/gates.js.map +1 -0
  20. package/dist/checks/git.d.ts +4 -0
  21. package/dist/checks/git.d.ts.map +1 -0
  22. package/dist/checks/git.js +36 -0
  23. package/dist/checks/git.js.map +1 -0
  24. package/dist/checks/lint.d.ts +10 -0
  25. package/dist/checks/lint.d.ts.map +1 -0
  26. package/dist/checks/lint.js +157 -0
  27. package/dist/checks/lint.js.map +1 -0
  28. package/dist/checks/lockfile.d.ts +3 -0
  29. package/dist/checks/lockfile.d.ts.map +1 -0
  30. package/dist/checks/lockfile.js +21 -0
  31. package/dist/checks/lockfile.js.map +1 -0
  32. package/dist/checks/test-coverage.d.ts +4 -0
  33. package/dist/checks/test-coverage.d.ts.map +1 -0
  34. package/dist/checks/test-coverage.js +62 -0
  35. package/dist/checks/test-coverage.js.map +1 -0
  36. package/dist/cli.d.ts +3 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +100 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/exec.d.ts +15 -0
  41. package/dist/exec.d.ts.map +1 -0
  42. package/dist/exec.js +43 -0
  43. package/dist/exec.js.map +1 -0
  44. package/dist/files.d.ts +7 -0
  45. package/dist/files.d.ts.map +1 -0
  46. package/dist/files.js +87 -0
  47. package/dist/files.js.map +1 -0
  48. package/dist/index.d.ts +14 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +12 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/report/baseline.d.ts +3 -0
  53. package/dist/report/baseline.d.ts.map +1 -0
  54. package/dist/report/baseline.js +54 -0
  55. package/dist/report/baseline.js.map +1 -0
  56. package/dist/report/markdown.d.ts +3 -0
  57. package/dist/report/markdown.d.ts.map +1 -0
  58. package/dist/report/markdown.js +122 -0
  59. package/dist/report/markdown.js.map +1 -0
  60. package/dist/report/score.d.ts +18 -0
  61. package/dist/report/score.d.ts.map +1 -0
  62. package/dist/report/score.js +40 -0
  63. package/dist/report/score.js.map +1 -0
  64. package/dist/types.d.ts +99 -0
  65. package/dist/types.d.ts.map +1 -0
  66. package/dist/types.js +2 -0
  67. package/dist/types.js.map +1 -0
  68. package/package.json +57 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # legion-audit
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ecc18df: Initial release: nine grade A rules with tolerance levels, the oxlint/ESLint plugin, the audit CLI with the `directives` subcommand, the Prettier and TypeScript configs, and the umbrella package.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Veracium LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # legion-audit
2
+
3
+ A read-only scorecard of a repo against
4
+ [LEGION-STANDARDS](https://github.com/Kprince1101/legion-toolkit#readme). It
5
+ runs the gates your CI already runs, counts what the
6
+ [`legion/*` rules](https://www.npmjs.com/package/eslint-plugin-legion) find,
7
+ lists every lint bypass, checks PR hygiene and which components and hooks
8
+ have no test file, and writes the result as Markdown and JSON. Nothing is
9
+ modified.
10
+
11
+ ```sh
12
+ yarn add -D legion-audit
13
+ yarn legion-audit --md AUDIT.md --json audit.json
14
+ ```
15
+
16
+ ## What it checks
17
+
18
+ | Check | Source of truth |
19
+ | ------------------ | ------------------------------------------------------------------------------------- |
20
+ | typecheck | `tsc --noEmit -p tsconfig.json` (skipped when there is no tsconfig or local tsc) |
21
+ | lint | the repo's oxlint or ESLint, `--format json`; counts per rule, `legion/*` findings |
22
+ | tests | the `test:ci` or `test` script, run through the detected package manager |
23
+ | format | `prettier --check .` (skipped when prettier is not installed) |
24
+ | dependency audit | `yarn npm audit` / `npm audit` / `pnpm audit` at high severity (skipped when offline) |
25
+ | lint bypasses | every `eslint-disable` / `oxlint-disable` directive and `NOSONAR`, counted per rule |
26
+ | file-wide disables | a bare `/* eslint-disable */` or `/* oxlint-disable */`; always a hard fail |
27
+ | PR hygiene | share of the last 80 commits whose subject carries a `(#123)` PR number |
28
+ | tests by file | components (`components/**`, `app/**`) and `use*` hooks without a matching test file |
29
+ | lockfile | exactly one lockfile at the root |
30
+
31
+ A tool that is not installed reports `skipped`, never `pass`.
32
+
33
+ ## Output
34
+
35
+ `AUDIT.md` opens with the gate table, then per-rule counts, findings with
36
+ file and line, the bypass list, PR hygiene, tests by file, and the score
37
+ formula. `audit.json` carries the same data (`schema: 1`).
38
+
39
+ The score starts at 100 and subtracts documented penalties (see the bottom of
40
+ any report). It is meant for comparing runs of the same repo over time, not
41
+ for comparing repos to each other.
42
+
43
+ ## In CI
44
+
45
+ ```sh
46
+ legion-audit --baseline audit.json --json audit.json --md AUDIT.md
47
+ ```
48
+
49
+ With `--baseline`, the exit code is non-zero on any regression: a gate that
50
+ flipped from pass to fail, more `legion/*` errors, more lint errors, more
51
+ file-wide disables, more files without tests, or a lower score. `--fail-on
52
+ error` fails on any failed gate or `legion/*` error regardless of baseline;
53
+ `--fail-on never` always exits 0.
54
+
55
+ ## `legion-audit directives`
56
+
57
+ The fast subcommand: only the file-wide disable scan, exit 1 if any. A
58
+ file-wide disable suppresses every rule in the file including the one that
59
+ would report it, so no lint rule can catch it. Put this in front of the
60
+ linter:
61
+
62
+ ```jsonc
63
+ { "scripts": { "lint": "legion-audit directives && oxlint ." } }
64
+ ```
65
+
66
+ ## Options
67
+
68
+ ```
69
+ --root <dir> repo root (default: cwd)
70
+ --md <file> write the Markdown report (default: stdout)
71
+ --json <file> write the JSON report
72
+ --baseline <file> previous JSON report to diff against
73
+ --fail-on <mode> regression | error | never
74
+ --ignore <path> skip a directory for the directive scan and tests-by-file (repeatable)
75
+ --no-deps-audit skip the dependency audit
76
+ --no-tests skip the test script
77
+ --no-typecheck skip tsc
78
+ --no-format skip prettier
79
+ --quiet, -q no progress on stderr
80
+ ```
81
+
82
+ Node 22 or newer. No runtime dependencies.
83
+
84
+ MIT, Copyright (c) 2026 Veracium LLC.
package/dist/args.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ export type FailOn = 'regression' | 'error' | 'never';
2
+ export interface CliArgs {
3
+ command: 'audit' | 'directives' | 'help';
4
+ root: string;
5
+ md: string | null;
6
+ json: string | null;
7
+ baseline: string | null;
8
+ failOn: FailOn | null;
9
+ depsAudit: boolean;
10
+ runTests: boolean;
11
+ runTypecheck: boolean;
12
+ runFormat: boolean;
13
+ quiet: boolean;
14
+ ignore: string[];
15
+ }
16
+ export declare const parseArgs: (argv: string[]) => CliArgs;
17
+ export declare const HELP = "legion-audit [directives] [options]\n\nRuns a read-only scorecard of the current repo against LEGION-STANDARDS.\n\nCommands\n (none) full audit: gates, rule counts, bypasses, PR hygiene, tests by file\n directives only scan for file-wide lint disables; exit 1 if any (fast, for lint scripts)\n\nOptions\n --root <dir> repo root (default: cwd)\n --md <file> write the markdown report (default: print to stdout)\n --json <file> write the JSON report\n --baseline <file> compare against a previous JSON report\n --fail-on <mode> regression | error | never\n default: regression when --baseline is given, otherwise never\n --ignore <path> skip a directory for the directive scan and tests-by-file (repeatable)\n --no-deps-audit skip the dependency audit (needs registry access)\n --no-tests skip running the test script\n --no-typecheck skip tsc\n --no-format skip prettier --check\n --quiet, -q no progress output on stderr\n";
18
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,OAAO,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAYD,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,KAAG,OAoD1C,CAAC;AAEF,eAAO,MAAM,IAAI,2jCAqBhB,CAAC"}
package/dist/args.js ADDED
@@ -0,0 +1,97 @@
1
+ const FAIL_ON_VALUES = new Set(['regression', 'error', 'never']);
2
+ const takeValue = (argv, index, flag) => {
3
+ const value = argv[index + 1];
4
+ if (value === undefined || value.startsWith('--')) {
5
+ throw new Error(`${flag} needs a value`);
6
+ }
7
+ return value;
8
+ };
9
+ export const parseArgs = (argv) => {
10
+ const args = {
11
+ command: 'audit',
12
+ root: process.cwd(),
13
+ md: null,
14
+ json: null,
15
+ baseline: null,
16
+ failOn: null,
17
+ depsAudit: true,
18
+ runTests: true,
19
+ runTypecheck: true,
20
+ runFormat: true,
21
+ quiet: false,
22
+ ignore: [],
23
+ };
24
+ let index = 0;
25
+ while (index < argv.length) {
26
+ const arg = argv[index] ?? '';
27
+ if (arg === 'directives')
28
+ args.command = 'directives';
29
+ else if (arg === 'help' || arg === '--help' || arg === '-h')
30
+ args.command = 'help';
31
+ else if (arg === '--root') {
32
+ args.root = takeValue(argv, index, arg);
33
+ index += 1;
34
+ }
35
+ else if (arg === '--md') {
36
+ args.md = takeValue(argv, index, arg);
37
+ index += 1;
38
+ }
39
+ else if (arg === '--json') {
40
+ args.json = takeValue(argv, index, arg);
41
+ index += 1;
42
+ }
43
+ else if (arg === '--baseline') {
44
+ args.baseline = takeValue(argv, index, arg);
45
+ index += 1;
46
+ }
47
+ else if (arg === '--fail-on') {
48
+ const value = takeValue(argv, index, arg);
49
+ if (!FAIL_ON_VALUES.has(value)) {
50
+ throw new Error(`--fail-on must be one of regression, error, never`);
51
+ }
52
+ args.failOn = value;
53
+ index += 1;
54
+ }
55
+ else if (arg === '--ignore') {
56
+ args.ignore.push(takeValue(argv, index, arg));
57
+ index += 1;
58
+ }
59
+ else if (arg === '--no-deps-audit')
60
+ args.depsAudit = false;
61
+ else if (arg === '--no-tests')
62
+ args.runTests = false;
63
+ else if (arg === '--no-typecheck')
64
+ args.runTypecheck = false;
65
+ else if (arg === '--no-format')
66
+ args.runFormat = false;
67
+ else if (arg === '--quiet' || arg === '-q')
68
+ args.quiet = true;
69
+ else
70
+ throw new Error(`unknown argument: ${arg}`);
71
+ index += 1;
72
+ }
73
+ return args;
74
+ };
75
+ export const HELP = `legion-audit [directives] [options]
76
+
77
+ Runs a read-only scorecard of the current repo against LEGION-STANDARDS.
78
+
79
+ Commands
80
+ (none) full audit: gates, rule counts, bypasses, PR hygiene, tests by file
81
+ directives only scan for file-wide lint disables; exit 1 if any (fast, for lint scripts)
82
+
83
+ Options
84
+ --root <dir> repo root (default: cwd)
85
+ --md <file> write the markdown report (default: print to stdout)
86
+ --json <file> write the JSON report
87
+ --baseline <file> compare against a previous JSON report
88
+ --fail-on <mode> regression | error | never
89
+ default: regression when --baseline is given, otherwise never
90
+ --ignore <path> skip a directory for the directive scan and tests-by-file (repeatable)
91
+ --no-deps-audit skip the dependency audit (needs registry access)
92
+ --no-tests skip running the test script
93
+ --no-typecheck skip tsc
94
+ --no-format skip prettier --check
95
+ --quiet, -q no progress output on stderr
96
+ `;
97
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { AuditOptions, AuditResult } from './types.js';
2
+ export declare const DEFAULT_OPTIONS: AuditOptions;
3
+ export declare const runAudit: (partial?: Partial<AuditOptions>) => AuditResult;
4
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAc,MAAM,YAAY,CAAC;AAExE,eAAO,MAAM,eAAe,EAAE,YAS7B,CAAC;AASF,eAAO,MAAM,QAAQ,GAAI,UAAS,OAAO,CAAC,YAAY,CAAM,KAAG,WA4D9D,CAAC"}
package/dist/audit.js ADDED
@@ -0,0 +1,81 @@
1
+ import { resolve } from 'node:path';
2
+ import { scanDirectives } from './checks/directives.js';
3
+ import { depsAuditGate, formatGate, testsGate, typecheckGate, } from './checks/gates.js';
4
+ import { prHygiene } from './checks/git.js';
5
+ import { runLint } from './checks/lint.js';
6
+ import { lockfileCheck } from './checks/lockfile.js';
7
+ import { testCoverage } from './checks/test-coverage.js';
8
+ import { computeScore } from './report/score.js';
9
+ export const DEFAULT_OPTIONS = {
10
+ root: process.cwd(),
11
+ depsAudit: true,
12
+ runTests: true,
13
+ runTypecheck: true,
14
+ runFormat: true,
15
+ quiet: false,
16
+ commitsToInspect: 80,
17
+ ignore: [],
18
+ };
19
+ const loggerFor = (options) => {
20
+ if (options.quiet)
21
+ return () => undefined;
22
+ return (message) => console.error(message);
23
+ };
24
+ export const runAudit = (partial = {}) => {
25
+ const options = { ...DEFAULT_OPTIONS, ...partial };
26
+ const root = resolve(options.root);
27
+ const log = loggerFor(options);
28
+ log('legion-audit: lockfile');
29
+ const lockfile = lockfileCheck(root);
30
+ const { packageManager } = lockfile;
31
+ const gates = [];
32
+ if (options.runTypecheck) {
33
+ log('legion-audit: typecheck');
34
+ gates.push(typecheckGate(root));
35
+ }
36
+ log('legion-audit: lint');
37
+ const lint = runLint(root);
38
+ const lintGate = {
39
+ name: 'lint',
40
+ status: lint.status,
41
+ summary: `${lint.linter}: ${lint.errors} error(s), ${lint.warnings} warning(s)`,
42
+ };
43
+ if (lint.command !== undefined)
44
+ lintGate.command = lint.command;
45
+ gates.push(lintGate);
46
+ if (options.runTests) {
47
+ log('legion-audit: tests');
48
+ gates.push(testsGate(root, packageManager));
49
+ }
50
+ if (options.runFormat) {
51
+ log('legion-audit: format');
52
+ gates.push(formatGate(root));
53
+ }
54
+ if (options.depsAudit) {
55
+ log('legion-audit: dependency audit');
56
+ gates.push(depsAuditGate(root, packageManager));
57
+ }
58
+ log('legion-audit: directives');
59
+ const directives = scanDirectives(root, options.ignore);
60
+ log('legion-audit: git history');
61
+ const hygiene = prHygiene(root, options.commitsToInspect);
62
+ log('legion-audit: tests by file');
63
+ const coverage = testCoverage(root, options.ignore);
64
+ const partialResult = {
65
+ root,
66
+ packageManager,
67
+ gates,
68
+ lint,
69
+ directives,
70
+ prHygiene: hygiene,
71
+ testCoverage: coverage,
72
+ lockfile,
73
+ };
74
+ return {
75
+ schema: 1,
76
+ generatedAt: new Date().toISOString(),
77
+ score: computeScore(partialResult),
78
+ ...partialResult,
79
+ };
80
+ };
81
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { DirectiveHit, DirectivesResult, NosonarHit } from '../types.js';
2
+ export declare const parseDirectivesInSource: (file: string, source: string) => {
3
+ hits: DirectiveHit[];
4
+ nosonar: NosonarHit[];
5
+ };
6
+ export declare const isFileWideDisable: (hit: DirectiveHit) => boolean;
7
+ export declare const scanDirectives: (root: string, ignore?: string[]) => DirectivesResult;
8
+ //# sourceMappingURL=directives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directives.d.ts","sourceRoot":"","sources":["../../src/checks/directives.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+C9E,eAAO,MAAM,uBAAuB,GAClC,MAAM,MAAM,EACZ,QAAQ,MAAM,KACb;IAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAAC,OAAO,EAAE,UAAU,EAAE,CAAA;CAuB/C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,KAAK,YAAY,KAAG,OACoB,CAAC;AAS3E,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,SAAQ,MAAM,EAAO,KACpB,gBA0BF,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { listSourceFiles, toPosix } from '../files.js';
3
+ const DIRECTIVE_PATTERN = /(?:\/\/|\/\*)\s*(eslint|oxlint)-(disable|enable)(-next-line|-line)?\b([^\n]*?)(?:\*\/|$)/g;
4
+ const NOSONAR_PATTERN = /\bNOSONAR\b/;
5
+ const scopeFromSuffix = (suffix) => {
6
+ if (suffix === '-next-line')
7
+ return 'next-line';
8
+ if (suffix === '-line')
9
+ return 'line';
10
+ return 'file';
11
+ };
12
+ const splitRest = (rest) => {
13
+ const text = rest.trim();
14
+ const separator = text.indexOf('--');
15
+ let rulesText = text;
16
+ let description = '';
17
+ if (separator !== -1) {
18
+ rulesText = text.slice(0, separator);
19
+ description = text.slice(separator + 2).trim();
20
+ }
21
+ const rules = rulesText
22
+ .split(',')
23
+ .map((rule) => rule.trim())
24
+ .filter((rule) => rule.length > 0);
25
+ return { rules, description };
26
+ };
27
+ const QUOTES = new Set(["'", '"', '`']);
28
+ const isInsideString = (text, index) => {
29
+ let open = null;
30
+ let position = 0;
31
+ while (position < index) {
32
+ const char = text[position] ?? '';
33
+ if (char === '\\') {
34
+ position += 2;
35
+ continue;
36
+ }
37
+ if (open === null && QUOTES.has(char))
38
+ open = char;
39
+ else if (open === char)
40
+ open = null;
41
+ position += 1;
42
+ }
43
+ return open !== null;
44
+ };
45
+ export const parseDirectivesInSource = (file, source) => {
46
+ const hits = [];
47
+ const nosonar = [];
48
+ const lines = source.split('\n');
49
+ lines.forEach((text, index) => {
50
+ const line = index + 1;
51
+ if (NOSONAR_PATTERN.test(text))
52
+ nosonar.push({ file, line });
53
+ for (const match of text.matchAll(DIRECTIVE_PATTERN)) {
54
+ if (isInsideString(text, match.index))
55
+ continue;
56
+ const [, tool, kind, suffix, rest] = match;
57
+ const { rules, description } = splitRest(rest ?? '');
58
+ hits.push({
59
+ file,
60
+ line,
61
+ tool: tool,
62
+ kind: kind,
63
+ scope: scopeFromSuffix(suffix),
64
+ rules,
65
+ description,
66
+ });
67
+ }
68
+ });
69
+ return { hits, nosonar };
70
+ };
71
+ export const isFileWideDisable = (hit) => hit.kind === 'disable' && hit.scope === 'file' && hit.rules.length === 0;
72
+ const ALL_RULES = '(all rules)';
73
+ const namesFor = (hit) => {
74
+ if (hit.rules.length === 0)
75
+ return [ALL_RULES];
76
+ return hit.rules;
77
+ };
78
+ export const scanDirectives = (root, ignore = []) => {
79
+ const all = [];
80
+ const nosonar = [];
81
+ for (const file of listSourceFiles(root, ignore)) {
82
+ let source = '';
83
+ try {
84
+ source = readFileSync(file, 'utf8');
85
+ }
86
+ catch {
87
+ continue;
88
+ }
89
+ const relativeFile = toPosix(root, file);
90
+ const parsed = parseDirectivesInSource(relativeFile, source);
91
+ all.push(...parsed.hits);
92
+ nosonar.push(...parsed.nosonar);
93
+ }
94
+ const byRule = {};
95
+ for (const hit of all) {
96
+ if (hit.kind !== 'disable')
97
+ continue;
98
+ for (const name of namesFor(hit))
99
+ byRule[name] = (byRule[name] ?? 0) + 1;
100
+ }
101
+ return {
102
+ fileWide: all.filter(isFileWideDisable),
103
+ all,
104
+ nosonar,
105
+ byRule,
106
+ };
107
+ };
108
+ //# sourceMappingURL=directives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directives.js","sourceRoot":"","sources":["../../src/checks/directives.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { GateResult, PackageManager } from '../types.js';
2
+ interface PackageJson {
3
+ scripts?: Record<string, string>;
4
+ workspaces?: string[] | {
5
+ packages?: string[];
6
+ };
7
+ }
8
+ export declare const readPackageJson: (root: string) => PackageJson;
9
+ export declare const countTypeErrors: (output: string) => number;
10
+ export declare const typecheckGate: (root: string) => GateResult;
11
+ export declare const summarizeTestOutput: (output: string) => string;
12
+ export declare const testsGate: (root: string, manager: PackageManager) => GateResult;
13
+ export declare const formatGate: (root: string) => GateResult;
14
+ export declare const depsAuditGate: (root: string, manager: PackageManager) => GateResult;
15
+ export {};
16
+ //# sourceMappingURL=gates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/checks/gates.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9D,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACjD;AAED,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,WAQ9C,CAAC;AASF,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MACF,CAAC;AAEhD,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,UAoB5C,CAAC;AASF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,MAAM,KAAG,MAkBpD,CAAC;AAEF,eAAO,MAAM,SAAS,GACpB,MAAM,MAAM,EACZ,SAAS,cAAc,KACtB,UAiBF,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,UAmBzC,CAAC;AA2CF,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EACZ,SAAS,cAAc,KACtB,UAUF,CAAC"}
@@ -0,0 +1,153 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { localBin, packageManagerRun, run } from '../exec.js';
4
+ export const readPackageJson = (root) => {
5
+ try {
6
+ return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
7
+ }
8
+ catch {
9
+ return {};
10
+ }
11
+ };
12
+ const statusFromCode = (code) => {
13
+ if (code === 0)
14
+ return 'pass';
15
+ return 'fail';
16
+ };
17
+ const TS_ERROR_PATTERN = /error TS\d+/g;
18
+ export const countTypeErrors = (output) => (output.match(TS_ERROR_PATTERN) ?? []).length;
19
+ export const typecheckGate = (root) => {
20
+ const bin = localBin(root, 'tsc');
21
+ if (!bin || !existsSync(join(root, 'tsconfig.json'))) {
22
+ return {
23
+ name: 'typecheck',
24
+ status: 'skipped',
25
+ summary: 'no tsconfig.json or no local tsc',
26
+ };
27
+ }
28
+ const result = run(bin, ['--noEmit', '-p', 'tsconfig.json'], root);
29
+ const errors = countTypeErrors(result.stdout + result.stderr);
30
+ let summary = 'clean';
31
+ if (result.code !== 0)
32
+ summary = `${errors} error(s)`;
33
+ return {
34
+ name: 'typecheck',
35
+ status: statusFromCode(result.code),
36
+ summary,
37
+ command: result.command,
38
+ exitCode: result.code,
39
+ };
40
+ };
41
+ const JEST_TESTS_PATTERN = /Tests:\s+(?:(\d+) failed, )?(?:(\d+) skipped, )?(?:(\d+) passed, )?(\d+) total/;
42
+ const JEST_SUITES_PATTERN = /Test Suites:\s+(?:(\d+) failed, )?(?:(\d+) skipped, )?(?:(\d+) passed, )?(\d+) total/;
43
+ const VITEST_PATTERN = /Tests\s+(?:(\d+) failed \| )?(\d+) passed(?: \| (\d+) skipped)?\s*\((\d+)\)/;
44
+ export const summarizeTestOutput = (output) => {
45
+ const jest = JEST_TESTS_PATTERN.exec(output);
46
+ if (jest) {
47
+ const [, failed, , passed, total] = jest;
48
+ const suites = JEST_SUITES_PATTERN.exec(output);
49
+ let suiteText = '';
50
+ if (suites)
51
+ suiteText = `, ${suites[4]} suite(s)`;
52
+ if (failed)
53
+ return `${failed} failed, ${passed ?? 0}/${total} passed${suiteText}`;
54
+ return `${passed ?? total}/${total} passed${suiteText}`;
55
+ }
56
+ const vitest = VITEST_PATTERN.exec(output);
57
+ if (vitest) {
58
+ const [, failed, passed, , total] = vitest;
59
+ if (failed)
60
+ return `${failed} failed, ${passed}/${total} passed`;
61
+ return `${passed}/${total} passed`;
62
+ }
63
+ return 'no summary parsed, exit code only';
64
+ };
65
+ export const testsGate = (root, manager) => {
66
+ const scripts = readPackageJson(root).scripts ?? {};
67
+ let script = null;
68
+ if (scripts['test:ci'])
69
+ script = 'test:ci';
70
+ else if (scripts['test'])
71
+ script = 'test';
72
+ if (!script) {
73
+ return { name: 'tests', status: 'skipped', summary: 'no test script' };
74
+ }
75
+ const { file, args } = packageManagerRun(manager, script);
76
+ const result = run(file, args, root);
77
+ return {
78
+ name: 'tests',
79
+ status: statusFromCode(result.code),
80
+ summary: summarizeTestOutput(result.stdout + result.stderr),
81
+ command: result.command,
82
+ exitCode: result.code,
83
+ };
84
+ };
85
+ export const formatGate = (root) => {
86
+ const bin = localBin(root, 'prettier');
87
+ if (!bin) {
88
+ return { name: 'format', status: 'skipped', summary: 'no local prettier' };
89
+ }
90
+ const result = run(bin, ['--check', '--no-color', '.'], root);
91
+ const unformatted = (result.stdout + result.stderr)
92
+ .split('\n')
93
+ .filter((line) => line.startsWith('[warn]') && !line.includes('Code style'))
94
+ .filter((line) => !line.includes('Run Prettier')).length;
95
+ let summary = 'clean';
96
+ if (result.code !== 0)
97
+ summary = `${unformatted} file(s) need formatting`;
98
+ return {
99
+ name: 'format',
100
+ status: statusFromCode(result.code),
101
+ summary,
102
+ command: result.command,
103
+ exitCode: result.code,
104
+ };
105
+ };
106
+ const NETWORK_FAILURE = /ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|network|offline/i;
107
+ const depsAuditCommand = (manager) => {
108
+ if (manager === 'yarn') {
109
+ return {
110
+ file: 'yarn',
111
+ args: ['npm', 'audit', '--all', '--severity', 'high'],
112
+ };
113
+ }
114
+ if (manager === 'npm')
115
+ return { file: 'npm', args: ['audit', '--audit-level=high'] };
116
+ if (manager === 'pnpm')
117
+ return { file: 'pnpm', args: ['audit', '--audit-level=high'] };
118
+ return null;
119
+ };
120
+ const depsAuditSummary = (result) => {
121
+ const output = result.stdout + result.stderr;
122
+ if (result.code !== 0 && NETWORK_FAILURE.test(output)) {
123
+ return {
124
+ name: 'dependency audit',
125
+ status: 'skipped',
126
+ summary: 'registry unreachable',
127
+ command: result.command,
128
+ exitCode: result.code,
129
+ };
130
+ }
131
+ let summary = 'no high or critical advisories';
132
+ if (result.code !== 0)
133
+ summary = 'advisories at high severity or above';
134
+ return {
135
+ name: 'dependency audit',
136
+ status: statusFromCode(result.code),
137
+ summary,
138
+ command: result.command,
139
+ exitCode: result.code,
140
+ };
141
+ };
142
+ export const depsAuditGate = (root, manager) => {
143
+ const command = depsAuditCommand(manager);
144
+ if (!command) {
145
+ return {
146
+ name: 'dependency audit',
147
+ status: 'skipped',
148
+ summary: `no audit command for ${manager}`,
149
+ };
150
+ }
151
+ return depsAuditSummary(run(command.file, command.args, root));
152
+ };
153
+ //# sourceMappingURL=gates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/checks/gates.ts"],"names":[],"mappings":"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"}