@titan-design/style-checker 0.0.0 → 0.2.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.
- package/README.md +75 -11
- package/dist/index.d.ts +41 -1
- package/dist/index.js +287 -40
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -21,8 +21,13 @@ import { generateRuffConfig, orchestrate } from "@titan-design/style-checker";
|
|
|
21
21
|
generateRuffConfig(profile);
|
|
22
22
|
// { lint: { select: ["N", "D", "C90"], pydocstyle: { convention: "google" }, mccabe: { "max-complexity": 30 } }, "line-length": 100 }
|
|
23
23
|
|
|
24
|
-
const { diagnostics, summary } = await orchestrate({
|
|
24
|
+
const { diagnostics, failures, skippedRules, summary } = await orchestrate({
|
|
25
|
+
profile,
|
|
26
|
+
files: ["src/app.ts", "app.py"],
|
|
27
|
+
});
|
|
25
28
|
// diagnostics: [{ file: "app.py", line: 5, column: 4, severity: "warn", category: "naming", rule: "N806", ... }]
|
|
29
|
+
// failures: [] means every tool that ran checked every file it was given
|
|
30
|
+
// skippedRules: [{ tool: "eslint", rule: "unicorn/filename-case", plugin: "unicorn", reason: "eslint-plugin-unicorn is not installed in /app" }]
|
|
26
31
|
```
|
|
27
32
|
|
|
28
33
|
## API
|
|
@@ -33,20 +38,75 @@ const { diagnostics, summary } = await orchestrate({ profile, files: ["src/app.t
|
|
|
33
38
|
and mccabe max complexity.
|
|
34
39
|
- `generateEslintConfig(profile)` returns an `EslintFlatConfigEntry[]`: one entry for
|
|
35
40
|
`**/*.ts` and `**/*.tsx` holding the rules style-profile's rule builders produce, or an
|
|
36
|
-
empty array when there are none.
|
|
41
|
+
empty array when there are none. It is data: rule names and options, with no plugin
|
|
42
|
+
objects and no parser. Style-profile's `info` tier becomes `warn`, because ESLint accepts
|
|
43
|
+
only `off`, `warn` and `error`.
|
|
37
44
|
- `orchestrate({ profile, files, fix?, language? })` picks the files by extension, runs
|
|
38
|
-
ESLint (through `npx eslint`) on `.ts`, `.tsx`, `.js`, `.jsx` and ruff on `.py`,
|
|
39
|
-
returns `{ diagnostics, summary }`. A tool is skipped when
|
|
40
|
-
`language` restricts it to one tool; otherwise it is
|
|
45
|
+
ESLint (through `npx --no -- eslint`) on `.ts`, `.tsx`, `.js`, `.jsx` and ruff on `.py`,
|
|
46
|
+
and returns `{ diagnostics, failures, skippedRules, summary }`. A tool is skipped when
|
|
47
|
+
its generated config is empty. `language` restricts it to one tool; otherwise it is
|
|
48
|
+
detected from the file list.
|
|
41
49
|
- `parseEslintJsonOutput(json)` and `parseRuffJsonOutput(json)` turn each tool's
|
|
42
50
|
`--format json` output into `CheckDiagnostic[]`. Both throw on output that is not JSON.
|
|
51
|
+
Messages with no rule (ESLint parse errors and ignored files) and ruff syntax errors
|
|
52
|
+
(`"code": "invalid-syntax"` in ruff 0.16.8, a null `code` in 0.9.10) are not
|
|
53
|
+
diagnostics; the runners report them as `file-not-checked` failures.
|
|
43
54
|
- `formatDiagnostic(d)` prints `file:line:column severity message [category.rule]`.
|
|
44
55
|
- `diffAgainstProfile(profile, observations)` compares each observation's value with the
|
|
45
56
|
profile's convention for its `type` and returns `{ deviations, summary }`. Observations
|
|
46
57
|
whose type has no profile rule count toward `total` but neither match nor deviate.
|
|
47
58
|
- Types: `CheckDiagnostic`, `CheckResult`, `OrchestratorOptions`, `OrchestratorResult`,
|
|
48
|
-
`
|
|
49
|
-
(re-exported from
|
|
59
|
+
`ToolFailure`, `ToolFailureKind`, `ToolName`, `SkippedRule`, `RuffConfig`,
|
|
60
|
+
`EslintFlatConfigEntry`, `Deviation`, `DiffResult`, and `Severity` (re-exported from
|
|
61
|
+
style-profile).
|
|
62
|
+
|
|
63
|
+
## Failures
|
|
64
|
+
|
|
65
|
+
A run that could not check the files is never reported as a clean run. Each problem
|
|
66
|
+
becomes a `ToolFailure` (`{ tool, kind, message, file? }`) in `failures`; the message
|
|
67
|
+
carries the tool's stderr. `diagnostics: []` with `failures: []` means the tools ran and
|
|
68
|
+
found nothing.
|
|
69
|
+
|
|
70
|
+
| `kind` | When |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `spawn-failed` | the executable (`npx`, `ruff`) could not be started |
|
|
73
|
+
| `timeout` | the run outlived its timeout (60 s) and was killed |
|
|
74
|
+
| `signal` | the process was killed by a signal |
|
|
75
|
+
| `exit-code` | an exit code other than 0 or 1 |
|
|
76
|
+
| `unparseable-output` | exit 0 or 1 with empty or non-JSON stdout |
|
|
77
|
+
| `file-not-checked` | the tool ran but could not check one file (`file` names it): an ESLint parse error or ignored file, a ruff syntax error, or a path ruff could not read (which it reports only on stderr, with exit 0) |
|
|
78
|
+
| `missing-dependency` | no TypeScript parser in the project, so ESLint was not run |
|
|
79
|
+
|
|
80
|
+
An unreadable ruff path is detected by matching ruff's stderr warning `warning: Failed to
|
|
81
|
+
lint <path>: <reason>`, captured from ruff 0.16.8. ruff has no structured signal for it
|
|
82
|
+
and does not treat the wording as stable; if a later ruff rewords it, that case goes
|
|
83
|
+
silent again.
|
|
84
|
+
|
|
85
|
+
Exit codes 0 and 1 are both successful runs. ESLint documents 0 as no errors, 1 as at
|
|
86
|
+
least one error, and 2 as a configuration problem or internal error
|
|
87
|
+
([CLI reference](https://eslint.org/docs/latest/use/command-line-interface#exit-codes)).
|
|
88
|
+
ruff documents 0 as no violations (or all fixed), 1 as violations found, and 2 as abnormal
|
|
89
|
+
termination from invalid configuration, invalid options or an internal error
|
|
90
|
+
([linter docs](https://docs.astral.sh/ruff/linter/#exit-codes)).
|
|
91
|
+
|
|
92
|
+
## ESLint plugins
|
|
93
|
+
|
|
94
|
+
The checker runs ESLint in the project (the current working directory), so plugins must
|
|
95
|
+
come from the project's own `node_modules`. At run time it resolves each plugin a rule
|
|
96
|
+
needs from that directory and writes a config that imports it by absolute path:
|
|
97
|
+
|
|
98
|
+
| Rule prefix | Package |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `@typescript-eslint/` | `@typescript-eslint/eslint-plugin`, or `typescript-eslint` |
|
|
101
|
+
| `perfectionist/` | `eslint-plugin-perfectionist` |
|
|
102
|
+
| `unicorn/` | `eslint-plugin-unicorn` |
|
|
103
|
+
| `jsdoc/` | `eslint-plugin-jsdoc` |
|
|
104
|
+
|
|
105
|
+
A rule whose plugin is not installed is left out of the run and listed in `skippedRules`
|
|
106
|
+
with the package it needs; the rest of the rules still run. The TypeScript parser
|
|
107
|
+
(`@typescript-eslint/parser`, or `typescript-eslint`) is required: without it ESLint
|
|
108
|
+
cannot parse `.ts`, so the run is not attempted and a `missing-dependency` failure names
|
|
109
|
+
the package. `npx --no` stops npx from downloading an ESLint the project does not have.
|
|
50
110
|
|
|
51
111
|
Which runners fire, with which configs, is product policy. The runner seam is one
|
|
52
112
|
function per tool (`runEslint`, `runRuff`) over a shared `runTool` spawn helper; new
|
|
@@ -54,11 +114,15 @@ runners are added beside them.
|
|
|
54
114
|
|
|
55
115
|
## Gotchas
|
|
56
116
|
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
117
|
+
- The generated ESLint config covers only `**/*.ts` and `**/*.tsx`, though `orchestrate`
|
|
118
|
+
sends `.js` and `.jsx` to ESLint too. A `.jsx` file comes back as a `file-not-checked`
|
|
119
|
+
failure. A `.js` file matches ESLint's built-in defaults, so it is linted with none of
|
|
120
|
+
the profile's rules and comes back clean.
|
|
121
|
+
- ESLint runs from the current working directory. A file outside it comes back as a
|
|
122
|
+
`file-not-checked` failure ("File ignored because outside of base path").
|
|
60
123
|
- ruff's `max-complexity` is set from the profile's `functionMaxLines`, a line count, not a
|
|
61
124
|
cyclomatic complexity.
|
|
62
125
|
- Every ruff diagnostic has severity `warn`. ESLint severity 2 maps to `error`, anything
|
|
63
|
-
else to `warn`. Nothing produces `info`.
|
|
126
|
+
else to `warn`. Nothing produces `info`. ruff 0.16.8 emits a `severity` field, but it is
|
|
127
|
+
`"error"` for every rule finding, so it is ignored.
|
|
64
128
|
- `summary.fixed` is always 0, even with `fix: true`.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { Observation } from '@titan-design/style-analyzer';
|
|
|
5
5
|
interface CheckDiagnostic {
|
|
6
6
|
file: string;
|
|
7
7
|
line: number;
|
|
8
|
+
/** Last line of the flagged range, when the tool reports one. */
|
|
9
|
+
endLine?: number;
|
|
8
10
|
column: number;
|
|
9
11
|
severity: Exclude<Severity, "off">;
|
|
10
12
|
message: string;
|
|
@@ -21,6 +23,20 @@ interface CheckResult {
|
|
|
21
23
|
tool: "eslint" | "ruff";
|
|
22
24
|
exitCode: number;
|
|
23
25
|
}
|
|
26
|
+
type ToolName = "eslint" | "ruff";
|
|
27
|
+
type ToolFailureKind = "spawn-failed" | "timeout" | "signal" | "exit-code" | "unparseable-output" | "file-not-checked" | "missing-dependency";
|
|
28
|
+
interface ToolFailure {
|
|
29
|
+
tool: ToolName;
|
|
30
|
+
kind: ToolFailureKind;
|
|
31
|
+
message: string;
|
|
32
|
+
file?: string;
|
|
33
|
+
}
|
|
34
|
+
interface SkippedRule {
|
|
35
|
+
tool: "eslint";
|
|
36
|
+
rule: string;
|
|
37
|
+
plugin: string;
|
|
38
|
+
reason: string;
|
|
39
|
+
}
|
|
24
40
|
interface OrchestratorOptions {
|
|
25
41
|
profile: Profile;
|
|
26
42
|
files: string[];
|
|
@@ -29,6 +45,9 @@ interface OrchestratorOptions {
|
|
|
29
45
|
}
|
|
30
46
|
interface OrchestratorResult {
|
|
31
47
|
diagnostics: CheckDiagnostic[];
|
|
48
|
+
/** Empty only when every tool that ran checked every file it was given. */
|
|
49
|
+
failures: ToolFailure[];
|
|
50
|
+
skippedRules: SkippedRule[];
|
|
32
51
|
summary: {
|
|
33
52
|
total: number;
|
|
34
53
|
errors: number;
|
|
@@ -65,9 +84,30 @@ interface RuffConfig {
|
|
|
65
84
|
};
|
|
66
85
|
};
|
|
67
86
|
"line-length"?: number;
|
|
87
|
+
preview?: boolean;
|
|
68
88
|
}
|
|
69
89
|
declare function generateRuffConfig(profile: Profile): RuffConfig;
|
|
70
90
|
|
|
91
|
+
/** The audit's pinned ruff selection: complexity, size, dead code, and defensive-bloat signals. */
|
|
92
|
+
declare const AUDIT_RUFF_RULES: readonly string[];
|
|
93
|
+
/** Preview is on because ruff 0.16.8 silently skips PLR0904, PLR0914, PLR0916 and PLR1702 without it. */
|
|
94
|
+
declare function generateRuffAuditConfig(): RuffConfig;
|
|
95
|
+
|
|
96
|
+
interface RunnerResult {
|
|
97
|
+
diagnostics: CheckDiagnostic[];
|
|
98
|
+
exitCode: number | null;
|
|
99
|
+
failures: ToolFailure[];
|
|
100
|
+
skippedRules: SkippedRule[];
|
|
101
|
+
}
|
|
102
|
+
interface RunnerOptions {
|
|
103
|
+
fix?: boolean;
|
|
104
|
+
cwd?: string;
|
|
105
|
+
timeout?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Runs the pinned audit rule set; diagnostics carry paths relative to `cwd`, since ruff reports absolute ones. */
|
|
109
|
+
declare function runRuffAudit(files: string[], options?: Omit<RunnerOptions, "fix">): Promise<RunnerResult>;
|
|
110
|
+
|
|
71
111
|
declare function parseEslintJsonOutput(jsonStr: string): CheckDiagnostic[];
|
|
72
112
|
declare function parseRuffJsonOutput(jsonStr: string): CheckDiagnostic[];
|
|
73
113
|
declare function formatDiagnostic(d: CheckDiagnostic): string;
|
|
@@ -90,4 +130,4 @@ interface DiffResult {
|
|
|
90
130
|
}
|
|
91
131
|
declare function diffAgainstProfile(profile: Profile, observations: Observation[]): DiffResult;
|
|
92
132
|
|
|
93
|
-
export { type CheckDiagnostic, type CheckResult, type Deviation, type DiffResult, type EslintFlatConfigEntry, type OrchestratorOptions, type OrchestratorResult, type RuffConfig, diffAgainstProfile, formatDiagnostic, generateEslintConfig, generateRuffConfig, orchestrate, parseEslintJsonOutput, parseRuffJsonOutput };
|
|
133
|
+
export { AUDIT_RUFF_RULES, type CheckDiagnostic, type CheckResult, type Deviation, type DiffResult, type EslintFlatConfigEntry, type OrchestratorOptions, type OrchestratorResult, type RuffConfig, type RunnerOptions, type RunnerResult, type SkippedRule, type ToolFailure, type ToolFailureKind, type ToolName, diffAgainstProfile, formatDiagnostic, generateEslintConfig, generateRuffAuditConfig, generateRuffConfig, orchestrate, parseEslintJsonOutput, parseRuffJsonOutput, runRuffAudit };
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,11 @@ import {
|
|
|
6
6
|
buildFileNamingRule,
|
|
7
7
|
buildJsdocRules
|
|
8
8
|
} from "@titan-design/style-profile";
|
|
9
|
-
function
|
|
10
|
-
|
|
9
|
+
function withEslintSeverity(value) {
|
|
10
|
+
if (Array.isArray(value) && value[0] === "info") return ["warn", ...value.slice(1)];
|
|
11
|
+
return value === "info" ? "warn" : value;
|
|
12
|
+
}
|
|
13
|
+
function collectRules(profile) {
|
|
11
14
|
const rules = {};
|
|
12
15
|
const namingRule = buildNamingConventionRule(profile);
|
|
13
16
|
if (namingRule) rules[namingRule[0]] = namingRule[1];
|
|
@@ -21,6 +24,12 @@ function generateEslintConfig(profile) {
|
|
|
21
24
|
for (const [name, value] of jsdocRules) {
|
|
22
25
|
rules[name] = value;
|
|
23
26
|
}
|
|
27
|
+
for (const name of Object.keys(rules)) rules[name] = withEslintSeverity(rules[name]);
|
|
28
|
+
return rules;
|
|
29
|
+
}
|
|
30
|
+
function generateEslintConfig(profile) {
|
|
31
|
+
const entries = [];
|
|
32
|
+
const rules = collectRules(profile);
|
|
24
33
|
if (Object.keys(rules).length > 0) {
|
|
25
34
|
entries.push({
|
|
26
35
|
files: ["**/*.ts", "**/*.tsx"],
|
|
@@ -83,7 +92,7 @@ function generateRuffConfig(profile) {
|
|
|
83
92
|
|
|
84
93
|
// src/runners/eslint-runner.ts
|
|
85
94
|
import { writeFileSync, mkdtempSync, rmSync } from "fs";
|
|
86
|
-
import { join } from "path";
|
|
95
|
+
import { join as join2 } from "path";
|
|
87
96
|
import { tmpdir } from "os";
|
|
88
97
|
|
|
89
98
|
// src/runners/tool-runner.ts
|
|
@@ -92,26 +101,176 @@ function runTool(command, args, options) {
|
|
|
92
101
|
return new Promise((resolve, reject) => {
|
|
93
102
|
const child = spawn(command, args, {
|
|
94
103
|
cwd: options?.cwd,
|
|
95
|
-
timeout: options?.timeout ?? 6e4,
|
|
96
104
|
stdio: ["ignore", "pipe", "pipe"]
|
|
97
105
|
});
|
|
98
106
|
const chunks = [];
|
|
99
107
|
const errChunks = [];
|
|
108
|
+
let timedOut = false;
|
|
109
|
+
const timer = setTimeout(() => {
|
|
110
|
+
timedOut = true;
|
|
111
|
+
child.kill("SIGKILL");
|
|
112
|
+
}, options?.timeout ?? 6e4);
|
|
100
113
|
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
101
114
|
child.stderr.on("data", (chunk) => errChunks.push(chunk));
|
|
102
115
|
child.on("error", (err) => {
|
|
116
|
+
clearTimeout(timer);
|
|
103
117
|
reject(new Error(`Failed to spawn ${command}: ${err.message}`));
|
|
104
118
|
});
|
|
105
|
-
child.on("close", (code) => {
|
|
119
|
+
child.on("close", (code, signal) => {
|
|
120
|
+
clearTimeout(timer);
|
|
106
121
|
resolve({
|
|
107
122
|
stdout: Buffer.concat(chunks).toString("utf-8"),
|
|
108
123
|
stderr: Buffer.concat(errChunks).toString("utf-8"),
|
|
109
|
-
exitCode: code
|
|
124
|
+
exitCode: code,
|
|
125
|
+
signal,
|
|
126
|
+
timedOut
|
|
110
127
|
});
|
|
111
128
|
});
|
|
112
129
|
});
|
|
113
130
|
}
|
|
114
131
|
|
|
132
|
+
// src/runners/failures.ts
|
|
133
|
+
var SUCCESS_EXIT_CODES = /* @__PURE__ */ new Set([0, 1]);
|
|
134
|
+
function excerpt(text) {
|
|
135
|
+
const trimmed = text.trim();
|
|
136
|
+
return trimmed.length > 500 ? `${trimmed.slice(0, 500)}\u2026` : trimmed;
|
|
137
|
+
}
|
|
138
|
+
function withStderr(message, stderr) {
|
|
139
|
+
const detail = excerpt(stderr);
|
|
140
|
+
return detail ? `${message}: ${detail}` : message;
|
|
141
|
+
}
|
|
142
|
+
function classifyRun(tool, run) {
|
|
143
|
+
const fail = (kind, message) => ({
|
|
144
|
+
ok: false,
|
|
145
|
+
failure: { tool, kind, message: withStderr(message, run.stderr) },
|
|
146
|
+
exitCode: run.exitCode
|
|
147
|
+
});
|
|
148
|
+
if (run.timedOut) return fail("timeout", `${tool} timed out and was killed`);
|
|
149
|
+
if (run.exitCode === null) {
|
|
150
|
+
return fail("signal", `${tool} was killed by ${run.signal ?? "a signal"}`);
|
|
151
|
+
}
|
|
152
|
+
if (!SUCCESS_EXIT_CODES.has(run.exitCode)) {
|
|
153
|
+
return fail("exit-code", `${tool} exited with code ${run.exitCode}`);
|
|
154
|
+
}
|
|
155
|
+
if (!run.stdout.trim()) {
|
|
156
|
+
return fail("unparseable-output", `${tool} exited ${run.exitCode} with no output`);
|
|
157
|
+
}
|
|
158
|
+
return { ok: true, stdout: run.stdout, stderr: run.stderr, exitCode: run.exitCode };
|
|
159
|
+
}
|
|
160
|
+
async function runAndClassify(tool, command, args, options) {
|
|
161
|
+
try {
|
|
162
|
+
return classifyRun(tool, await runTool(command, args, options));
|
|
163
|
+
} catch (err) {
|
|
164
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
165
|
+
return { ok: false, failure: { tool, kind: "spawn-failed", message }, exitCode: null };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function parseOrFail(tool, parse) {
|
|
169
|
+
try {
|
|
170
|
+
return { ok: true, value: parse() };
|
|
171
|
+
} catch (err) {
|
|
172
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
173
|
+
return { ok: false, failure: { tool, kind: "unparseable-output", message } };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/runners/eslint-plugins.ts
|
|
178
|
+
import { createRequire } from "module";
|
|
179
|
+
import { join } from "path";
|
|
180
|
+
import { pathToFileURL } from "url";
|
|
181
|
+
var PLUGIN_SOURCES = {
|
|
182
|
+
"@typescript-eslint": [
|
|
183
|
+
{ specifier: "@typescript-eslint/eslint-plugin" },
|
|
184
|
+
{ specifier: "typescript-eslint", member: "plugin" }
|
|
185
|
+
],
|
|
186
|
+
perfectionist: [{ specifier: "eslint-plugin-perfectionist" }],
|
|
187
|
+
unicorn: [{ specifier: "eslint-plugin-unicorn" }],
|
|
188
|
+
jsdoc: [{ specifier: "eslint-plugin-jsdoc" }]
|
|
189
|
+
};
|
|
190
|
+
var PARSER_SOURCES = [
|
|
191
|
+
{ specifier: "@typescript-eslint/parser" },
|
|
192
|
+
{ specifier: "typescript-eslint", member: "parser" }
|
|
193
|
+
];
|
|
194
|
+
function pluginNamespace(rule) {
|
|
195
|
+
const slash = rule.lastIndexOf("/");
|
|
196
|
+
return slash === -1 ? null : rule.slice(0, slash);
|
|
197
|
+
}
|
|
198
|
+
function describeSources(sources) {
|
|
199
|
+
return sources.map((s) => s.specifier).join(" or ");
|
|
200
|
+
}
|
|
201
|
+
function resolveModule(sources, fromDir) {
|
|
202
|
+
const require2 = createRequire(join(fromDir, "package.json"));
|
|
203
|
+
for (const source of sources) {
|
|
204
|
+
try {
|
|
205
|
+
const url = pathToFileURL(require2.resolve(source.specifier)).href;
|
|
206
|
+
return { url, member: source.member };
|
|
207
|
+
} catch {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/runners/eslint-config-module.ts
|
|
215
|
+
function importExpr(ctx, mod) {
|
|
216
|
+
let id = ctx.ids.get(mod.url);
|
|
217
|
+
if (!id) {
|
|
218
|
+
id = `m${ctx.ids.size}`;
|
|
219
|
+
ctx.ids.set(mod.url, id);
|
|
220
|
+
ctx.imports.push(`import ${id} from ${JSON.stringify(mod.url)};`);
|
|
221
|
+
}
|
|
222
|
+
return mod.member ? `${id}.${mod.member}` : id;
|
|
223
|
+
}
|
|
224
|
+
function resolvePlugin(ctx, namespace) {
|
|
225
|
+
if (!ctx.plugins.has(namespace)) {
|
|
226
|
+
const sources = PLUGIN_SOURCES[namespace];
|
|
227
|
+
ctx.plugins.set(namespace, sources ? resolveModule(sources, ctx.fromDir) : null);
|
|
228
|
+
}
|
|
229
|
+
return ctx.plugins.get(namespace) ?? null;
|
|
230
|
+
}
|
|
231
|
+
function skipReason(ctx, namespace) {
|
|
232
|
+
const sources = PLUGIN_SOURCES[namespace];
|
|
233
|
+
if (!sources) return `no known package provides the ESLint plugin "${namespace}"`;
|
|
234
|
+
return `${describeSources(sources)} is not installed in ${ctx.fromDir}`;
|
|
235
|
+
}
|
|
236
|
+
function renderEntry(ctx, entry, parserExpr, skipped) {
|
|
237
|
+
const rules = {};
|
|
238
|
+
const plugins = [];
|
|
239
|
+
for (const [rule, value] of Object.entries(entry.rules ?? {})) {
|
|
240
|
+
const namespace = pluginNamespace(rule);
|
|
241
|
+
const plugin = namespace === null ? null : resolvePlugin(ctx, namespace);
|
|
242
|
+
if (namespace !== null && !plugin) {
|
|
243
|
+
skipped.push({ tool: "eslint", rule, plugin: namespace, reason: skipReason(ctx, namespace) });
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
rules[rule] = value;
|
|
247
|
+
if (namespace !== null && plugin) plugins.push(`${JSON.stringify(namespace)}: ${importExpr(ctx, plugin)}`);
|
|
248
|
+
}
|
|
249
|
+
const data = JSON.stringify({ files: entry.files, rules });
|
|
250
|
+
const text = `{ ...${data}, languageOptions: { parser: ${parserExpr} }, plugins: { ${[...new Set(plugins)].join(", ")} } }`;
|
|
251
|
+
return { text, ruleCount: Object.keys(rules).length };
|
|
252
|
+
}
|
|
253
|
+
function buildEslintConfigModule(config, fromDir) {
|
|
254
|
+
const parser = resolveModule(PARSER_SOURCES, fromDir);
|
|
255
|
+
if (!parser) {
|
|
256
|
+
const message = `${describeSources(PARSER_SOURCES)} is not installed in ${fromDir}, so ESLint cannot parse TypeScript`;
|
|
257
|
+
return { ok: false, failure: { tool: "eslint", kind: "missing-dependency", message } };
|
|
258
|
+
}
|
|
259
|
+
const ctx = { fromDir, imports: [], ids: /* @__PURE__ */ new Map(), plugins: /* @__PURE__ */ new Map() };
|
|
260
|
+
const parserExpr = importExpr(ctx, parser);
|
|
261
|
+
const skippedRules = [];
|
|
262
|
+
const entries = config.map((entry) => renderEntry(ctx, entry, parserExpr, skippedRules));
|
|
263
|
+
const ruleCount = entries.reduce((sum, e) => sum + e.ruleCount, 0);
|
|
264
|
+
const body = entries.map((e) => e.text).join(",\n ");
|
|
265
|
+
const source = `${ctx.imports.join("\n")}
|
|
266
|
+
|
|
267
|
+
export default [
|
|
268
|
+
${body},
|
|
269
|
+
];
|
|
270
|
+
`;
|
|
271
|
+
return { ok: true, source, ruleCount, skippedRules };
|
|
272
|
+
}
|
|
273
|
+
|
|
115
274
|
// src/formatters/unified.ts
|
|
116
275
|
var RUFF_CODE_CATEGORY = {
|
|
117
276
|
N: "naming",
|
|
@@ -175,6 +334,9 @@ ${jsonStr.slice(0, 500)}`
|
|
|
175
334
|
}
|
|
176
335
|
return diagnostics;
|
|
177
336
|
}
|
|
337
|
+
function isRuffSyntaxError(code) {
|
|
338
|
+
return code === null || code === "invalid-syntax";
|
|
339
|
+
}
|
|
178
340
|
function parseRuffJsonOutput(jsonStr) {
|
|
179
341
|
let entries;
|
|
180
342
|
try {
|
|
@@ -185,39 +347,55 @@ function parseRuffJsonOutput(jsonStr) {
|
|
|
185
347
|
${jsonStr.slice(0, 500)}`
|
|
186
348
|
);
|
|
187
349
|
}
|
|
188
|
-
return entries.
|
|
350
|
+
return entries.flatMap((entry) => isRuffSyntaxError(entry.code) ? [] : [{
|
|
189
351
|
file: entry.filename,
|
|
190
352
|
line: entry.location.row,
|
|
353
|
+
endLine: entry.end_location.row,
|
|
191
354
|
column: entry.location.column,
|
|
192
355
|
severity: "warn",
|
|
193
356
|
message: entry.message,
|
|
194
357
|
category: categorizeRuffCode(entry.code),
|
|
195
358
|
rule: entry.code,
|
|
196
359
|
fixable: entry.fix != null
|
|
197
|
-
})
|
|
360
|
+
}]);
|
|
198
361
|
}
|
|
199
362
|
function formatDiagnostic(d) {
|
|
200
363
|
return `${d.file}:${d.line}:${d.column} ${d.severity} ${d.message} [${d.category}.${d.rule}]`;
|
|
201
364
|
}
|
|
202
365
|
|
|
203
366
|
// src/runners/eslint-runner.ts
|
|
367
|
+
function uncheckedFiles(stdout) {
|
|
368
|
+
const entries = JSON.parse(stdout);
|
|
369
|
+
return entries.flatMap(
|
|
370
|
+
(entry) => entry.messages.filter((m) => m.ruleId === null && (m.fatal === true || m.message.startsWith("File ignored"))).map((m) => ({ tool: "eslint", kind: "file-not-checked", file: entry.filePath, message: m.message }))
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
function readEslintOutput(stdout) {
|
|
374
|
+
const parsed = parseOrFail("eslint", () => ({
|
|
375
|
+
diagnostics: parseEslintJsonOutput(stdout),
|
|
376
|
+
failures: uncheckedFiles(stdout)
|
|
377
|
+
}));
|
|
378
|
+
return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };
|
|
379
|
+
}
|
|
380
|
+
function eslintArgs(configPath, files, fix) {
|
|
381
|
+
return ["--no", "--", "eslint", "--config", configPath, "--format", "json", ...fix ? ["--fix"] : [], ...files];
|
|
382
|
+
}
|
|
204
383
|
async function runEslint(config, files, options) {
|
|
205
|
-
const
|
|
206
|
-
const
|
|
384
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
385
|
+
const built = buildEslintConfigModule(config, cwd);
|
|
386
|
+
if (!built.ok) return { diagnostics: [], exitCode: null, failures: [built.failure], skippedRules: [] };
|
|
387
|
+
const { skippedRules } = built;
|
|
388
|
+
if (built.ruleCount === 0) return { diagnostics: [], exitCode: null, failures: [], skippedRules };
|
|
389
|
+
const tempDir = mkdtempSync(join2(tmpdir(), "codewatch-eslint-"));
|
|
390
|
+
const configPath = join2(tempDir, "eslint.config.mjs");
|
|
207
391
|
try {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
...options?.fix ? ["--fix"] : [],
|
|
216
|
-
...files
|
|
217
|
-
];
|
|
218
|
-
const result = await runTool("npx", ["eslint", ...args]);
|
|
219
|
-
const diagnostics = result.stdout.trim() ? parseEslintJsonOutput(result.stdout) : [];
|
|
220
|
-
return { diagnostics, exitCode: result.exitCode };
|
|
392
|
+
writeFileSync(configPath, built.source, "utf-8");
|
|
393
|
+
const run = await runAndClassify("eslint", "npx", eslintArgs(configPath, files, options?.fix), {
|
|
394
|
+
cwd,
|
|
395
|
+
timeout: options?.timeout
|
|
396
|
+
});
|
|
397
|
+
if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules };
|
|
398
|
+
return { ...readEslintOutput(run.stdout), exitCode: run.exitCode, skippedRules };
|
|
221
399
|
} finally {
|
|
222
400
|
rmSync(tempDir, { recursive: true, force: true });
|
|
223
401
|
}
|
|
@@ -225,7 +403,7 @@ async function runEslint(config, files, options) {
|
|
|
225
403
|
|
|
226
404
|
// src/runners/ruff-runner.ts
|
|
227
405
|
import { writeFileSync as writeFileSync2, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
|
|
228
|
-
import { join as
|
|
406
|
+
import { join as join3 } from "path";
|
|
229
407
|
import { tmpdir as tmpdir2 } from "os";
|
|
230
408
|
function quoteList(values) {
|
|
231
409
|
return `[${values.map((s) => `"${s}"`).join(", ")}]`;
|
|
@@ -263,14 +441,36 @@ function toToml(config) {
|
|
|
263
441
|
if (config["line-length"]) {
|
|
264
442
|
lines.push(`line-length = ${config["line-length"]}`);
|
|
265
443
|
}
|
|
444
|
+
if (config.preview) lines.push("preview = true");
|
|
266
445
|
if (config.lint) {
|
|
267
446
|
lines.push("[lint]", ...lintSelectionLines(config.lint), ...lintSubsectionLines(config.lint));
|
|
268
447
|
}
|
|
269
448
|
return lines.join("\n") + "\n";
|
|
270
449
|
}
|
|
450
|
+
function notChecked(file, message) {
|
|
451
|
+
return { tool: "ruff", kind: "file-not-checked", file, message };
|
|
452
|
+
}
|
|
453
|
+
function unparsedFiles(stdout) {
|
|
454
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
455
|
+
for (const e of JSON.parse(stdout)) {
|
|
456
|
+
if (!isRuffSyntaxError(e.code)) continue;
|
|
457
|
+
byFile.set(e.filename, [...byFile.get(e.filename) ?? [], e.message]);
|
|
458
|
+
}
|
|
459
|
+
return [...byFile].map(([file, messages]) => notChecked(file, messages.join("; ")));
|
|
460
|
+
}
|
|
461
|
+
function unreadFiles(stderr) {
|
|
462
|
+
return [...stderr.matchAll(/^warning: Failed to lint (.+?): (.+)$/gm)].map((m) => notChecked(m[1], m[2]));
|
|
463
|
+
}
|
|
464
|
+
function readRuffOutput(stdout, stderr) {
|
|
465
|
+
const parsed = parseOrFail("ruff", () => ({
|
|
466
|
+
diagnostics: parseRuffJsonOutput(stdout),
|
|
467
|
+
failures: [...unparsedFiles(stdout), ...unreadFiles(stderr)]
|
|
468
|
+
}));
|
|
469
|
+
return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };
|
|
470
|
+
}
|
|
271
471
|
async function runRuff(config, files, options) {
|
|
272
|
-
const tempDir = mkdtempSync2(
|
|
273
|
-
const configPath =
|
|
472
|
+
const tempDir = mkdtempSync2(join3(tmpdir2(), "codewatch-ruff-"));
|
|
473
|
+
const configPath = join3(tempDir, "ruff.toml");
|
|
274
474
|
try {
|
|
275
475
|
writeFileSync2(configPath, toToml(config), "utf-8");
|
|
276
476
|
const args = [
|
|
@@ -282,9 +482,9 @@ async function runRuff(config, files, options) {
|
|
|
282
482
|
...options?.fix ? ["--fix"] : [],
|
|
283
483
|
...files
|
|
284
484
|
];
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
return {
|
|
485
|
+
const run = await runAndClassify("ruff", "ruff", args, { cwd: options?.cwd, timeout: options?.timeout });
|
|
486
|
+
if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules: [] };
|
|
487
|
+
return { ...readRuffOutput(run.stdout, run.stderr), exitCode: run.exitCode, skippedRules: [] };
|
|
288
488
|
} finally {
|
|
289
489
|
rmSync2(tempDir, { recursive: true, force: true });
|
|
290
490
|
}
|
|
@@ -307,32 +507,76 @@ function buildSummary(diagnostics) {
|
|
|
307
507
|
fixed: 0
|
|
308
508
|
};
|
|
309
509
|
}
|
|
310
|
-
async function
|
|
510
|
+
async function runTools(options) {
|
|
311
511
|
const { profile, files, fix } = options;
|
|
312
512
|
const language = options.language ?? detectLanguage(files);
|
|
313
|
-
const allDiagnostics = [];
|
|
314
513
|
const tsFiles = files.filter((f) => /\.[tj]sx?$/.test(f));
|
|
315
514
|
const pyFiles = files.filter((f) => /\.py$/.test(f));
|
|
515
|
+
const results = [];
|
|
316
516
|
if ((language === "typescript" || language === "mixed") && tsFiles.length > 0) {
|
|
317
517
|
const eslintConfig = generateEslintConfig(profile);
|
|
318
|
-
if (eslintConfig.length > 0) {
|
|
319
|
-
const result = await runEslint(eslintConfig, tsFiles, { fix });
|
|
320
|
-
allDiagnostics.push(...result.diagnostics);
|
|
321
|
-
}
|
|
518
|
+
if (eslintConfig.length > 0) results.push(await runEslint(eslintConfig, tsFiles, { fix }));
|
|
322
519
|
}
|
|
323
520
|
if ((language === "python" || language === "mixed") && pyFiles.length > 0) {
|
|
324
521
|
const ruffConfig = generateRuffConfig(profile);
|
|
325
522
|
if (ruffConfig.lint?.select && ruffConfig.lint.select.length > 0) {
|
|
326
|
-
|
|
327
|
-
allDiagnostics.push(...result.diagnostics);
|
|
523
|
+
results.push(await runRuff(ruffConfig, pyFiles, { fix }));
|
|
328
524
|
}
|
|
329
525
|
}
|
|
526
|
+
return results;
|
|
527
|
+
}
|
|
528
|
+
async function orchestrate(options) {
|
|
529
|
+
const results = await runTools(options);
|
|
530
|
+
const diagnostics = results.flatMap((r) => r.diagnostics);
|
|
330
531
|
return {
|
|
331
|
-
diagnostics
|
|
332
|
-
|
|
532
|
+
diagnostics,
|
|
533
|
+
failures: results.flatMap((r) => r.failures),
|
|
534
|
+
skippedRules: results.flatMap((r) => r.skippedRules),
|
|
535
|
+
summary: buildSummary(diagnostics)
|
|
333
536
|
};
|
|
334
537
|
}
|
|
335
538
|
|
|
539
|
+
// src/generators/ruff-audit.ts
|
|
540
|
+
var AUDIT_RUFF_RULES = [
|
|
541
|
+
"C901",
|
|
542
|
+
"PLR0904",
|
|
543
|
+
"PLR0911",
|
|
544
|
+
"PLR0912",
|
|
545
|
+
"PLR0913",
|
|
546
|
+
"PLR0914",
|
|
547
|
+
"PLR0915",
|
|
548
|
+
"PLR0916",
|
|
549
|
+
"PLR0917",
|
|
550
|
+
"PLR1702",
|
|
551
|
+
"ARG",
|
|
552
|
+
"FBT",
|
|
553
|
+
"ERA001",
|
|
554
|
+
"BLE001",
|
|
555
|
+
"S110",
|
|
556
|
+
"TRY203",
|
|
557
|
+
"SIM105",
|
|
558
|
+
"F401",
|
|
559
|
+
"F841",
|
|
560
|
+
"PIE790",
|
|
561
|
+
"RUF100"
|
|
562
|
+
];
|
|
563
|
+
function generateRuffAuditConfig() {
|
|
564
|
+
return { preview: true, lint: { select: [...AUDIT_RUFF_RULES] } };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/runners/ruff-audit.ts
|
|
568
|
+
import { realpathSync } from "fs";
|
|
569
|
+
import { isAbsolute, relative } from "path";
|
|
570
|
+
async function runRuffAudit(files, options) {
|
|
571
|
+
const result = await runRuff(generateRuffAuditConfig(), files, options);
|
|
572
|
+
const root = realpathSync(options?.cwd ?? process.cwd());
|
|
573
|
+
const diagnostics = result.diagnostics.map((d) => ({ ...d, file: relativeTo(root, d.file) }));
|
|
574
|
+
return { ...result, diagnostics };
|
|
575
|
+
}
|
|
576
|
+
function relativeTo(root, file) {
|
|
577
|
+
return isAbsolute(file) ? relative(root, file) : file;
|
|
578
|
+
}
|
|
579
|
+
|
|
336
580
|
// src/profile-diff/diff-against-profile.ts
|
|
337
581
|
import { PROFILE_CATEGORIES } from "@titan-design/style-profile";
|
|
338
582
|
function getSeverity(confidence, thresholds) {
|
|
@@ -386,12 +630,15 @@ function diffAgainstProfile(profile, observations) {
|
|
|
386
630
|
};
|
|
387
631
|
}
|
|
388
632
|
export {
|
|
633
|
+
AUDIT_RUFF_RULES,
|
|
389
634
|
diffAgainstProfile,
|
|
390
635
|
formatDiagnostic,
|
|
391
636
|
generateEslintConfig,
|
|
637
|
+
generateRuffAuditConfig,
|
|
392
638
|
generateRuffConfig,
|
|
393
639
|
orchestrate,
|
|
394
640
|
parseEslintJsonOutput,
|
|
395
|
-
parseRuffJsonOutput
|
|
641
|
+
parseRuffJsonOutput,
|
|
642
|
+
runRuffAudit
|
|
396
643
|
};
|
|
397
644
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generators/eslint.ts","../src/generators/ruff.ts","../src/runners/eslint-runner.ts","../src/runners/tool-runner.ts","../src/formatters/unified.ts","../src/runners/ruff-runner.ts","../src/orchestrator/orchestrate.ts","../src/profile-diff/diff-against-profile.ts"],"sourcesContent":["import type { Profile } from \"@titan-design/style-profile\";\nimport {\n buildNamingConventionRule,\n buildImportOrderRule,\n buildFunctionLengthRule,\n buildFileNamingRule,\n buildJsdocRules,\n} from \"@titan-design/style-profile\";\n\nexport interface EslintFlatConfigEntry {\n plugins?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n files?: string[];\n}\n\nexport function generateEslintConfig(profile: Profile): EslintFlatConfigEntry[] {\n const entries: EslintFlatConfigEntry[] = [];\n const rules: Record<string, unknown> = {};\n\n const namingRule = buildNamingConventionRule(profile);\n if (namingRule) rules[namingRule[0]] = namingRule[1];\n\n const importRule = buildImportOrderRule(profile);\n if (importRule) rules[importRule[0]] = importRule[1];\n\n const fnLengthRule = buildFunctionLengthRule(profile);\n if (fnLengthRule) rules[fnLengthRule[0]] = fnLengthRule[1];\n\n const fileNamingRule = buildFileNamingRule(profile);\n if (fileNamingRule) rules[fileNamingRule[0]] = fileNamingRule[1];\n\n const jsdocRules = buildJsdocRules(profile);\n for (const [name, value] of jsdocRules) {\n rules[name] = value;\n }\n\n if (Object.keys(rules).length > 0) {\n entries.push({\n files: [\"**/*.ts\", \"**/*.tsx\"],\n rules,\n });\n }\n\n return entries;\n}\n","import type { Profile } from \"@titan-design/style-profile\";\n\nexport interface RuffConfig {\n lint?: {\n select?: string[];\n ignore?: string[];\n fixable?: string[];\n \"per-file-ignores\"?: Record<string, string[]>;\n mccabe?: { \"max-complexity\"?: number };\n pydocstyle?: { convention?: string };\n isort?: {\n \"known-first-party\"?: string[];\n \"section-order\"?: string[];\n };\n };\n \"line-length\"?: number;\n}\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]> & { select: string[] };\n\nfunction addNaming(profile: Profile, lint: RuffLint): void {\n if (profile.naming) {\n const hasNamingRules = Object.values(profile.naming).some(\n (rule) => rule.confidence >= profile.severityThresholds.info,\n );\n if (hasNamingRules) lint.select.push(\"N\");\n }\n}\n\nfunction addImportOrder(profile: Profile, lint: RuffLint): void {\n const importOrder = profile.structure?.importOrder;\n if (importOrder && importOrder.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"I\");\n if (Array.isArray(importOrder.convention)) {\n lint.isort = {\n \"section-order\": importOrder.convention as string[],\n };\n }\n }\n}\n\nfunction addFunctionDocs(profile: Profile, lint: RuffLint): void {\n const functionDocs = profile.documentation?.functionDocs;\n if (functionDocs && functionDocs.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"D\");\n const conv = functionDocs.convention;\n if (\n typeof conv === \"string\" &&\n conv !== \"jsdoc-selective\" &&\n conv !== \"jsdoc-all\"\n ) {\n lint.pydocstyle = { convention: conv };\n }\n }\n}\n\nfunction addComplexity(profile: Profile, lint: RuffLint): void {\n const maxLines = profile.structure?.functionMaxLines;\n if (\n maxLines &&\n maxLines.confidence >= profile.severityThresholds.info &&\n typeof maxLines.convention === \"number\"\n ) {\n lint.select.push(\"C90\");\n lint.mccabe = { \"max-complexity\": maxLines.convention };\n }\n}\n\nexport function generateRuffConfig(profile: Profile): RuffConfig {\n const lint: RuffLint = { select: [] };\n const config: RuffConfig = { lint };\n\n addNaming(profile, lint);\n addImportOrder(profile, lint);\n addFunctionDocs(profile, lint);\n addComplexity(profile, lint);\n\n const lineLength = profile.formatting?.lineLength;\n if (lineLength && typeof lineLength.convention === \"number\") {\n config[\"line-length\"] = lineLength.convention;\n }\n\n return config;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runTool } from \"./tool-runner.js\";\nimport { parseEslintJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic } from \"../orchestrator/types.js\";\nimport type { EslintFlatConfigEntry } from \"../generators/eslint.js\";\n\nexport async function runEslint(\n config: EslintFlatConfigEntry[],\n files: string[],\n options?: { fix?: boolean },\n): Promise<{ diagnostics: CheckDiagnostic[]; exitCode: number }> {\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-eslint-\"));\n const configPath = join(tempDir, \"eslint.config.js\");\n\n try {\n const configContent = `export default ${JSON.stringify(config, null, 2)};`;\n writeFileSync(configPath, configContent, \"utf-8\");\n\n const args = [\n \"--config\",\n configPath,\n \"--format\",\n \"json\",\n ...(options?.fix ? [\"--fix\"] : []),\n ...files,\n ];\n\n const result = await runTool(\"npx\", [\"eslint\", ...args]);\n const diagnostics =\n result.stdout.trim() ? parseEslintJsonOutput(result.stdout) : [];\n\n return { diagnostics, exitCode: result.exitCode };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { spawn } from \"node:child_process\";\n\nexport interface ToolRunResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\nexport function runTool(\n command: string,\n args: string[],\n options?: { cwd?: string; timeout?: number },\n): Promise<ToolRunResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd: options?.cwd,\n timeout: options?.timeout ?? 60_000,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const chunks: Buffer[] = [];\n const errChunks: Buffer[] = [];\n\n child.stdout.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => errChunks.push(chunk));\n\n child.on(\"error\", (err) => {\n reject(new Error(`Failed to spawn ${command}: ${err.message}`));\n });\n\n child.on(\"close\", (code) => {\n resolve({\n stdout: Buffer.concat(chunks).toString(\"utf-8\"),\n stderr: Buffer.concat(errChunks).toString(\"utf-8\"),\n exitCode: code ?? 1,\n });\n });\n });\n}\n","import type { CheckDiagnostic } from \"../orchestrator/types.js\";\n\nconst RUFF_CODE_CATEGORY: Record<string, string> = {\n N: \"naming\",\n I: \"imports\",\n D: \"documentation\",\n C: \"complexity\",\n E: \"formatting\",\n W: \"formatting\",\n F: \"errors\",\n};\n\nconst ESLINT_RULE_CATEGORY: Record<string, string> = {\n \"@typescript-eslint/naming-convention\": \"naming\",\n \"unicorn/filename-case\": \"naming\",\n \"perfectionist/sort-imports\": \"imports\",\n \"import/order\": \"imports\",\n \"max-lines-per-function\": \"structure\",\n \"max-depth\": \"structure\",\n \"jsdoc/require-jsdoc\": \"documentation\",\n};\n\nfunction eslintSeverityToSeverity(eslintSeverity: number): \"error\" | \"warn\" {\n return eslintSeverity === 2 ? \"error\" : \"warn\";\n}\n\nfunction categorizeEslintRule(ruleId: string): string {\n if (ESLINT_RULE_CATEGORY[ruleId]) return ESLINT_RULE_CATEGORY[ruleId];\n if (ruleId.startsWith(\"@typescript-eslint/\")) return \"typescript\";\n if (ruleId.startsWith(\"jsdoc/\")) return \"documentation\";\n if (ruleId.startsWith(\"unicorn/\")) return \"style\";\n if (ruleId.startsWith(\"import/\")) return \"imports\";\n return \"other\";\n}\n\nfunction categorizeRuffCode(code: string): string {\n const prefix = code.replace(/[0-9]/g, \"\");\n return RUFF_CODE_CATEGORY[prefix] ?? \"other\";\n}\n\ninterface EslintJsonEntry {\n filePath: string;\n messages: Array<{\n ruleId: string | null;\n severity: number;\n message: string;\n line: number;\n column: number;\n fix?: { range: [number, number]; text: string } | null;\n }>;\n}\n\ninterface RuffJsonEntry {\n code: string;\n message: string;\n filename: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n fix?: {\n edits: Array<{\n content: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n }>;\n } | null;\n}\n\nexport function parseEslintJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: EslintJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse ESLint JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n const diagnostics: CheckDiagnostic[] = [];\n\n for (const entry of entries) {\n for (const msg of entry.messages) {\n if (!msg.ruleId) continue;\n diagnostics.push({\n file: entry.filePath,\n line: msg.line,\n column: msg.column,\n severity: eslintSeverityToSeverity(msg.severity),\n message: msg.message,\n category: categorizeEslintRule(msg.ruleId),\n rule: msg.ruleId,\n fixable: msg.fix != null,\n fix: msg.fix ?? undefined,\n });\n }\n }\n\n return diagnostics;\n}\n\nexport function parseRuffJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: RuffJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse Ruff JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n return entries.map((entry) => ({\n file: entry.filename,\n line: entry.location.row,\n column: entry.location.column,\n severity: \"warn\" as const,\n message: entry.message,\n category: categorizeRuffCode(entry.code),\n rule: entry.code,\n fixable: entry.fix != null,\n }));\n}\n\nexport function formatDiagnostic(d: CheckDiagnostic): string {\n return `${d.file}:${d.line}:${d.column} ${d.severity} ${d.message} [${d.category}.${d.rule}]`;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runTool } from \"./tool-runner.js\";\nimport { parseRuffJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic } from \"../orchestrator/types.js\";\nimport type { RuffConfig } from \"../generators/ruff.js\";\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]>;\n\nfunction quoteList(values: string[]): string {\n return `[${values.map((s) => `\"${s}\"`).join(\", \")}]`;\n}\n\nfunction lintSelectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.select) lines.push(`select = ${quoteList(lint.select)}`);\n if (lint.ignore) lines.push(`ignore = ${quoteList(lint.ignore)}`);\n return lines;\n}\n\nfunction lintSubsectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.mccabe) {\n lines.push(\"[lint.mccabe]\");\n if (lint.mccabe[\"max-complexity\"]) {\n lines.push(`max-complexity = ${lint.mccabe[\"max-complexity\"]}`);\n }\n }\n if (lint.pydocstyle) {\n lines.push(\"[lint.pydocstyle]\");\n if (lint.pydocstyle.convention) {\n lines.push(`convention = \"${lint.pydocstyle.convention}\"`);\n }\n }\n if (lint.isort) {\n lines.push(\"[lint.isort]\");\n if (lint.isort[\"section-order\"]) {\n lines.push(`section-order = ${quoteList(lint.isort[\"section-order\"])}`);\n }\n }\n return lines;\n}\n\nfunction toToml(config: RuffConfig): string {\n const lines: string[] = [];\n\n if (config[\"line-length\"]) {\n lines.push(`line-length = ${config[\"line-length\"]}`);\n }\n\n if (config.lint) {\n lines.push(\"[lint]\", ...lintSelectionLines(config.lint), ...lintSubsectionLines(config.lint));\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n\nexport async function runRuff(\n config: RuffConfig,\n files: string[],\n options?: { fix?: boolean },\n): Promise<{ diagnostics: CheckDiagnostic[]; exitCode: number }> {\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-ruff-\"));\n const configPath = join(tempDir, \"ruff.toml\");\n\n try {\n writeFileSync(configPath, toToml(config), \"utf-8\");\n\n const args = [\n \"check\",\n \"--config\",\n configPath,\n \"--output-format\",\n \"json\",\n ...(options?.fix ? [\"--fix\"] : []),\n ...files,\n ];\n\n const result = await runTool(\"ruff\", args);\n const diagnostics =\n result.stdout.trim() ? parseRuffJsonOutput(result.stdout) : [];\n\n return { diagnostics, exitCode: result.exitCode };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { generateEslintConfig } from \"../generators/eslint.js\";\nimport { generateRuffConfig } from \"../generators/ruff.js\";\nimport { runEslint } from \"../runners/eslint-runner.js\";\nimport { runRuff } from \"../runners/ruff-runner.js\";\nimport type {\n OrchestratorOptions,\n OrchestratorResult,\n CheckDiagnostic,\n} from \"./types.js\";\n\nfunction detectLanguage(files: string[]): \"typescript\" | \"python\" | \"mixed\" {\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n if (tsFiles.length > 0 && pyFiles.length === 0) return \"typescript\";\n if (pyFiles.length > 0 && tsFiles.length === 0) return \"python\";\n return \"mixed\";\n}\n\nfunction buildSummary(\n diagnostics: CheckDiagnostic[],\n): OrchestratorResult[\"summary\"] {\n return {\n total: diagnostics.length,\n errors: diagnostics.filter((d) => d.severity === \"error\").length,\n warnings: diagnostics.filter((d) => d.severity === \"warn\").length,\n infos: diagnostics.filter((d) => d.severity === \"info\").length,\n fixed: 0,\n };\n}\n\nexport async function orchestrate(\n options: OrchestratorOptions,\n): Promise<OrchestratorResult> {\n const { profile, files, fix } = options;\n const language = options.language ?? detectLanguage(files);\n const allDiagnostics: CheckDiagnostic[] = [];\n\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n\n if (\n (language === \"typescript\" || language === \"mixed\") &&\n tsFiles.length > 0\n ) {\n const eslintConfig = generateEslintConfig(profile);\n if (eslintConfig.length > 0) {\n const result = await runEslint(eslintConfig, tsFiles, { fix });\n allDiagnostics.push(...result.diagnostics);\n }\n }\n\n if ((language === \"python\" || language === \"mixed\") && pyFiles.length > 0) {\n const ruffConfig = generateRuffConfig(profile);\n if (ruffConfig.lint?.select && ruffConfig.lint.select.length > 0) {\n const result = await runRuff(ruffConfig, pyFiles, { fix });\n allDiagnostics.push(...result.diagnostics);\n }\n }\n\n return {\n diagnostics: allDiagnostics,\n summary: buildSummary(allDiagnostics),\n };\n}\n","import { PROFILE_CATEGORIES } from \"@titan-design/style-profile\";\nimport type { Profile, ProfileCategory } from \"@titan-design/style-profile\";\nimport type { Observation } from \"@titan-design/style-analyzer\";\n\nexport interface Deviation {\n file: string;\n line: number;\n rule: string;\n expected: string;\n found: string;\n severity: \"error\" | \"warn\" | \"info\";\n}\n\nexport interface DiffResult {\n deviations: Deviation[];\n summary: {\n total: number;\n matching: number;\n deviating: number;\n };\n}\n\nfunction getSeverity(\n confidence: number,\n thresholds: Profile[\"severityThresholds\"],\n): \"error\" | \"warn\" | \"info\" {\n if (confidence >= thresholds.error) return \"error\";\n if (confidence >= thresholds.warn) return \"warn\";\n return \"info\";\n}\n\nfunction resolveProfileRule(\n profile: Profile,\n observationType: string,\n): { convention: unknown; confidence: number } | undefined {\n const [category, rule] = observationType.split(\".\");\n if (!PROFILE_CATEGORIES.includes(category as ProfileCategory)) return undefined;\n const section = profile[category as ProfileCategory];\n if (!section || typeof section !== \"object\") return undefined;\n const ruleObj = rule === undefined ? undefined : section[rule];\n if (!ruleObj || typeof ruleObj !== \"object\") return undefined;\n if (ruleObj.convention === undefined || ruleObj.confidence === undefined) {\n return undefined;\n }\n return { convention: ruleObj.convention, confidence: ruleObj.confidence };\n}\n\nexport function diffAgainstProfile(\n profile: Profile,\n observations: Observation[],\n): DiffResult {\n const deviations: Deviation[] = [];\n let matching = 0;\n\n for (const obs of observations) {\n const profileRule = resolveProfileRule(profile, obs.type);\n if (!profileRule) continue;\n\n const expected = String(profileRule.convention);\n const found = String(obs.value);\n\n if (found === expected) {\n matching++;\n } else {\n deviations.push({\n file: obs.file,\n line: obs.line,\n rule: obs.type,\n expected,\n found,\n severity: getSeverity(\n profileRule.confidence,\n profile.severityThresholds,\n ),\n });\n }\n }\n\n return {\n deviations,\n summary: {\n total: observations.length,\n matching,\n deviating: deviations.length,\n },\n };\n}\n"],"mappings":"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","names":["writeFileSync","mkdtempSync","rmSync","join","tmpdir","mkdtempSync","join","tmpdir","writeFileSync","rmSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/generators/eslint.ts","../src/generators/ruff.ts","../src/runners/eslint-runner.ts","../src/runners/tool-runner.ts","../src/runners/failures.ts","../src/runners/eslint-plugins.ts","../src/runners/eslint-config-module.ts","../src/formatters/unified.ts","../src/runners/ruff-runner.ts","../src/orchestrator/orchestrate.ts","../src/generators/ruff-audit.ts","../src/runners/ruff-audit.ts","../src/profile-diff/diff-against-profile.ts"],"sourcesContent":["import type { Profile } from \"@titan-design/style-profile\";\nimport {\n buildNamingConventionRule,\n buildImportOrderRule,\n buildFunctionLengthRule,\n buildFileNamingRule,\n buildJsdocRules,\n} from \"@titan-design/style-profile\";\n\nexport interface EslintFlatConfigEntry {\n plugins?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n files?: string[];\n}\n\n// ESLint accepts only off, warn and error; style-profile's \"info\" tier becomes warn, as its own exporter does.\nfunction withEslintSeverity(value: unknown): unknown {\n if (Array.isArray(value) && value[0] === \"info\") return [\"warn\", ...value.slice(1)];\n return value === \"info\" ? \"warn\" : value;\n}\n\nfunction collectRules(profile: Profile): Record<string, unknown> {\n const rules: Record<string, unknown> = {};\n\n const namingRule = buildNamingConventionRule(profile);\n if (namingRule) rules[namingRule[0]] = namingRule[1];\n\n const importRule = buildImportOrderRule(profile);\n if (importRule) rules[importRule[0]] = importRule[1];\n\n const fnLengthRule = buildFunctionLengthRule(profile);\n if (fnLengthRule) rules[fnLengthRule[0]] = fnLengthRule[1];\n\n const fileNamingRule = buildFileNamingRule(profile);\n if (fileNamingRule) rules[fileNamingRule[0]] = fileNamingRule[1];\n\n const jsdocRules = buildJsdocRules(profile);\n for (const [name, value] of jsdocRules) {\n rules[name] = value;\n }\n\n for (const name of Object.keys(rules)) rules[name] = withEslintSeverity(rules[name]);\n return rules;\n}\n\nexport function generateEslintConfig(profile: Profile): EslintFlatConfigEntry[] {\n const entries: EslintFlatConfigEntry[] = [];\n const rules = collectRules(profile);\n\n if (Object.keys(rules).length > 0) {\n entries.push({\n files: [\"**/*.ts\", \"**/*.tsx\"],\n rules,\n });\n }\n\n return entries;\n}\n","import type { Profile } from \"@titan-design/style-profile\";\n\nexport interface RuffConfig {\n lint?: {\n select?: string[];\n ignore?: string[];\n fixable?: string[];\n \"per-file-ignores\"?: Record<string, string[]>;\n mccabe?: { \"max-complexity\"?: number };\n pydocstyle?: { convention?: string };\n isort?: {\n \"known-first-party\"?: string[];\n \"section-order\"?: string[];\n };\n };\n \"line-length\"?: number;\n preview?: boolean;\n}\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]> & { select: string[] };\n\nfunction addNaming(profile: Profile, lint: RuffLint): void {\n if (profile.naming) {\n const hasNamingRules = Object.values(profile.naming).some(\n (rule) => rule.confidence >= profile.severityThresholds.info,\n );\n if (hasNamingRules) lint.select.push(\"N\");\n }\n}\n\nfunction addImportOrder(profile: Profile, lint: RuffLint): void {\n const importOrder = profile.structure?.importOrder;\n if (importOrder && importOrder.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"I\");\n if (Array.isArray(importOrder.convention)) {\n lint.isort = {\n \"section-order\": importOrder.convention as string[],\n };\n }\n }\n}\n\nfunction addFunctionDocs(profile: Profile, lint: RuffLint): void {\n const functionDocs = profile.documentation?.functionDocs;\n if (functionDocs && functionDocs.confidence >= profile.severityThresholds.info) {\n lint.select.push(\"D\");\n const conv = functionDocs.convention;\n if (\n typeof conv === \"string\" &&\n conv !== \"jsdoc-selective\" &&\n conv !== \"jsdoc-all\"\n ) {\n lint.pydocstyle = { convention: conv };\n }\n }\n}\n\nfunction addComplexity(profile: Profile, lint: RuffLint): void {\n const maxLines = profile.structure?.functionMaxLines;\n if (\n maxLines &&\n maxLines.confidence >= profile.severityThresholds.info &&\n typeof maxLines.convention === \"number\"\n ) {\n lint.select.push(\"C90\");\n lint.mccabe = { \"max-complexity\": maxLines.convention };\n }\n}\n\nexport function generateRuffConfig(profile: Profile): RuffConfig {\n const lint: RuffLint = { select: [] };\n const config: RuffConfig = { lint };\n\n addNaming(profile, lint);\n addImportOrder(profile, lint);\n addFunctionDocs(profile, lint);\n addComplexity(profile, lint);\n\n const lineLength = profile.formatting?.lineLength;\n if (lineLength && typeof lineLength.convention === \"number\") {\n config[\"line-length\"] = lineLength.convention;\n }\n\n return config;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runAndClassify, parseOrFail } from \"./failures.js\";\nimport { buildEslintConfigModule } from \"./eslint-config-module.js\";\nimport { parseEslintJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { EslintFlatConfigEntry } from \"../generators/eslint.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\n\ninterface EslintJsonMessage {\n ruleId: string | null;\n fatal?: boolean;\n message: string;\n}\n\n// ESLint reports a file it could not lint as a rule-less message: a fatal parse error, or \"File ignored ...\".\nfunction uncheckedFiles(stdout: string): ToolFailure[] {\n const entries = JSON.parse(stdout) as Array<{ filePath: string; messages: EslintJsonMessage[] }>;\n return entries.flatMap((entry) =>\n entry.messages\n .filter((m) => m.ruleId === null && (m.fatal === true || m.message.startsWith(\"File ignored\")))\n .map((m) => ({ tool: \"eslint\" as const, kind: \"file-not-checked\" as const, file: entry.filePath, message: m.message })),\n );\n}\n\nfunction readEslintOutput(stdout: string): { diagnostics: CheckDiagnostic[]; failures: ToolFailure[] } {\n const parsed = parseOrFail(\"eslint\", () => ({\n diagnostics: parseEslintJsonOutput(stdout),\n failures: uncheckedFiles(stdout),\n }));\n return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };\n}\n\nfunction eslintArgs(configPath: string, files: string[], fix?: boolean): string[] {\n // --no stops npx from downloading an ESLint the project has not installed.\n return [\"--no\", \"--\", \"eslint\", \"--config\", configPath, \"--format\", \"json\", ...(fix ? [\"--fix\"] : []), ...files];\n}\n\nexport async function runEslint(\n config: EslintFlatConfigEntry[],\n files: string[],\n options?: RunnerOptions,\n): Promise<RunnerResult> {\n const cwd = options?.cwd ?? process.cwd();\n const built = buildEslintConfigModule(config, cwd);\n if (!built.ok) return { diagnostics: [], exitCode: null, failures: [built.failure], skippedRules: [] };\n const { skippedRules } = built;\n if (built.ruleCount === 0) return { diagnostics: [], exitCode: null, failures: [], skippedRules };\n\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-eslint-\"));\n const configPath = join(tempDir, \"eslint.config.mjs\");\n try {\n writeFileSync(configPath, built.source, \"utf-8\");\n const run = await runAndClassify(\"eslint\", \"npx\", eslintArgs(configPath, files, options?.fix), {\n cwd,\n timeout: options?.timeout,\n });\n if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules };\n return { ...readEslintOutput(run.stdout), exitCode: run.exitCode, skippedRules };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { spawn } from \"node:child_process\";\n\nexport interface ToolRunResult {\n stdout: string;\n stderr: string;\n /** Null when the child was killed by a signal; see `signal`. */\n exitCode: number | null;\n signal: NodeJS.Signals | null;\n timedOut: boolean;\n}\n\nexport function runTool(\n command: string,\n args: string[],\n options?: { cwd?: string; timeout?: number },\n): Promise<ToolRunResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd: options?.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const chunks: Buffer[] = [];\n const errChunks: Buffer[] = [];\n let timedOut = false;\n // Our own timer, because spawn's `timeout` option kills without telling us it did.\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGKILL\");\n }, options?.timeout ?? 60_000);\n\n child.stdout.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => errChunks.push(chunk));\n\n child.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(new Error(`Failed to spawn ${command}: ${err.message}`));\n });\n\n child.on(\"close\", (code, signal) => {\n clearTimeout(timer);\n resolve({\n stdout: Buffer.concat(chunks).toString(\"utf-8\"),\n stderr: Buffer.concat(errChunks).toString(\"utf-8\"),\n exitCode: code,\n signal,\n timedOut,\n });\n });\n });\n}\n","import { runTool } from \"./tool-runner.js\";\nimport type { ToolRunResult } from \"./tool-runner.js\";\nimport type { ToolFailure, ToolName } from \"../orchestrator/types.js\";\n\n// ESLint and ruff both document 0 (clean) and 1 (problems found) as successful runs.\nconst SUCCESS_EXIT_CODES = new Set([0, 1]);\n\nexport type ToolRun =\n | { ok: true; stdout: string; stderr: string; exitCode: number }\n | { ok: false; failure: ToolFailure; exitCode: number | null };\n\nfunction excerpt(text: string): string {\n const trimmed = text.trim();\n return trimmed.length > 500 ? `${trimmed.slice(0, 500)}…` : trimmed;\n}\n\nfunction withStderr(message: string, stderr: string): string {\n const detail = excerpt(stderr);\n return detail ? `${message}: ${detail}` : message;\n}\n\nexport function classifyRun(tool: ToolName, run: ToolRunResult): ToolRun {\n const fail = (kind: ToolFailure[\"kind\"], message: string): ToolRun => ({\n ok: false,\n failure: { tool, kind, message: withStderr(message, run.stderr) },\n exitCode: run.exitCode,\n });\n if (run.timedOut) return fail(\"timeout\", `${tool} timed out and was killed`);\n if (run.exitCode === null) {\n return fail(\"signal\", `${tool} was killed by ${run.signal ?? \"a signal\"}`);\n }\n if (!SUCCESS_EXIT_CODES.has(run.exitCode)) {\n return fail(\"exit-code\", `${tool} exited with code ${run.exitCode}`);\n }\n if (!run.stdout.trim()) {\n return fail(\"unparseable-output\", `${tool} exited ${run.exitCode} with no output`);\n }\n return { ok: true, stdout: run.stdout, stderr: run.stderr, exitCode: run.exitCode };\n}\n\nexport async function runAndClassify(\n tool: ToolName,\n command: string,\n args: string[],\n options?: { cwd?: string; timeout?: number },\n): Promise<ToolRun> {\n try {\n return classifyRun(tool, await runTool(command, args, options));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, failure: { tool, kind: \"spawn-failed\", message }, exitCode: null };\n }\n}\n\nexport function parseOrFail<T>(\n tool: ToolName,\n parse: () => T,\n): { ok: true; value: T } | { ok: false; failure: ToolFailure } {\n try {\n return { ok: true, value: parse() };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, failure: { tool, kind: \"unparseable-output\", message } };\n }\n}\n","import { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nexport interface ModuleSource {\n specifier: string;\n member?: \"plugin\" | \"parser\";\n}\n\nexport interface ResolvedModule {\n url: string;\n member?: \"plugin\" | \"parser\";\n}\n\n// The typescript-eslint meta-package is the fallback because under pnpm it is often the only one a project can resolve.\nexport const PLUGIN_SOURCES: Record<string, ModuleSource[]> = {\n \"@typescript-eslint\": [\n { specifier: \"@typescript-eslint/eslint-plugin\" },\n { specifier: \"typescript-eslint\", member: \"plugin\" },\n ],\n perfectionist: [{ specifier: \"eslint-plugin-perfectionist\" }],\n unicorn: [{ specifier: \"eslint-plugin-unicorn\" }],\n jsdoc: [{ specifier: \"eslint-plugin-jsdoc\" }],\n};\n\nexport const PARSER_SOURCES: ModuleSource[] = [\n { specifier: \"@typescript-eslint/parser\" },\n { specifier: \"typescript-eslint\", member: \"parser\" },\n];\n\nexport function pluginNamespace(rule: string): string | null {\n const slash = rule.lastIndexOf(\"/\");\n return slash === -1 ? null : rule.slice(0, slash);\n}\n\nexport function describeSources(sources: ModuleSource[]): string {\n return sources.map((s) => s.specifier).join(\" or \");\n}\n\nexport function resolveModule(\n sources: ModuleSource[],\n fromDir: string,\n): ResolvedModule | null {\n // Resolve from the project, not from this package: ESLint runs there and loads plugins from there.\n const require = createRequire(join(fromDir, \"package.json\"));\n for (const source of sources) {\n try {\n const url = pathToFileURL(require.resolve(source.specifier)).href;\n return { url, member: source.member };\n } catch {\n continue;\n }\n }\n return null;\n}\n","import {\n PARSER_SOURCES,\n PLUGIN_SOURCES,\n describeSources,\n pluginNamespace,\n resolveModule,\n} from \"./eslint-plugins.js\";\nimport type { ResolvedModule } from \"./eslint-plugins.js\";\nimport type { EslintFlatConfigEntry } from \"../generators/eslint.js\";\nimport type { SkippedRule, ToolFailure } from \"../orchestrator/types.js\";\n\nexport type EslintConfigModule =\n | { ok: true; source: string; ruleCount: number; skippedRules: SkippedRule[] }\n | { ok: false; failure: ToolFailure };\n\ninterface ModuleContext {\n fromDir: string;\n imports: string[];\n ids: Map<string, string>;\n plugins: Map<string, ResolvedModule | null>;\n}\n\nfunction importExpr(ctx: ModuleContext, mod: ResolvedModule): string {\n let id = ctx.ids.get(mod.url);\n if (!id) {\n id = `m${ctx.ids.size}`;\n ctx.ids.set(mod.url, id);\n ctx.imports.push(`import ${id} from ${JSON.stringify(mod.url)};`);\n }\n return mod.member ? `${id}.${mod.member}` : id;\n}\n\nfunction resolvePlugin(ctx: ModuleContext, namespace: string): ResolvedModule | null {\n if (!ctx.plugins.has(namespace)) {\n const sources = PLUGIN_SOURCES[namespace];\n ctx.plugins.set(namespace, sources ? resolveModule(sources, ctx.fromDir) : null);\n }\n return ctx.plugins.get(namespace) ?? null;\n}\n\nfunction skipReason(ctx: ModuleContext, namespace: string): string {\n const sources = PLUGIN_SOURCES[namespace];\n if (!sources) return `no known package provides the ESLint plugin \"${namespace}\"`;\n return `${describeSources(sources)} is not installed in ${ctx.fromDir}`;\n}\n\nfunction renderEntry(\n ctx: ModuleContext,\n entry: EslintFlatConfigEntry,\n parserExpr: string,\n skipped: SkippedRule[],\n): { text: string; ruleCount: number } {\n const rules: Record<string, unknown> = {};\n const plugins: string[] = [];\n for (const [rule, value] of Object.entries(entry.rules ?? {})) {\n const namespace = pluginNamespace(rule);\n const plugin = namespace === null ? null : resolvePlugin(ctx, namespace);\n if (namespace !== null && !plugin) {\n skipped.push({ tool: \"eslint\", rule, plugin: namespace, reason: skipReason(ctx, namespace) });\n continue;\n }\n rules[rule] = value;\n if (namespace !== null && plugin) plugins.push(`${JSON.stringify(namespace)}: ${importExpr(ctx, plugin)}`);\n }\n const data = JSON.stringify({ files: entry.files, rules });\n const text = `{ ...${data}, languageOptions: { parser: ${parserExpr} }, plugins: { ${[...new Set(plugins)].join(\", \")} } }`;\n return { text, ruleCount: Object.keys(rules).length };\n}\n\nexport function buildEslintConfigModule(\n config: EslintFlatConfigEntry[],\n fromDir: string,\n): EslintConfigModule {\n const parser = resolveModule(PARSER_SOURCES, fromDir);\n if (!parser) {\n const message = `${describeSources(PARSER_SOURCES)} is not installed in ${fromDir}, so ESLint cannot parse TypeScript`;\n return { ok: false, failure: { tool: \"eslint\", kind: \"missing-dependency\", message } };\n }\n const ctx: ModuleContext = { fromDir, imports: [], ids: new Map(), plugins: new Map() };\n const parserExpr = importExpr(ctx, parser);\n const skippedRules: SkippedRule[] = [];\n const entries = config.map((entry) => renderEntry(ctx, entry, parserExpr, skippedRules));\n const ruleCount = entries.reduce((sum, e) => sum + e.ruleCount, 0);\n const body = entries.map((e) => e.text).join(\",\\n \");\n const source = `${ctx.imports.join(\"\\n\")}\\n\\nexport default [\\n ${body},\\n];\\n`;\n return { ok: true, source, ruleCount, skippedRules };\n}\n","import type { CheckDiagnostic } from \"../orchestrator/types.js\";\n\nconst RUFF_CODE_CATEGORY: Record<string, string> = {\n N: \"naming\",\n I: \"imports\",\n D: \"documentation\",\n C: \"complexity\",\n E: \"formatting\",\n W: \"formatting\",\n F: \"errors\",\n};\n\nconst ESLINT_RULE_CATEGORY: Record<string, string> = {\n \"@typescript-eslint/naming-convention\": \"naming\",\n \"unicorn/filename-case\": \"naming\",\n \"perfectionist/sort-imports\": \"imports\",\n \"import/order\": \"imports\",\n \"max-lines-per-function\": \"structure\",\n \"max-depth\": \"structure\",\n \"jsdoc/require-jsdoc\": \"documentation\",\n};\n\nfunction eslintSeverityToSeverity(eslintSeverity: number): \"error\" | \"warn\" {\n return eslintSeverity === 2 ? \"error\" : \"warn\";\n}\n\nfunction categorizeEslintRule(ruleId: string): string {\n if (ESLINT_RULE_CATEGORY[ruleId]) return ESLINT_RULE_CATEGORY[ruleId];\n if (ruleId.startsWith(\"@typescript-eslint/\")) return \"typescript\";\n if (ruleId.startsWith(\"jsdoc/\")) return \"documentation\";\n if (ruleId.startsWith(\"unicorn/\")) return \"style\";\n if (ruleId.startsWith(\"import/\")) return \"imports\";\n return \"other\";\n}\n\nfunction categorizeRuffCode(code: string): string {\n const prefix = code.replace(/[0-9]/g, \"\");\n return RUFF_CODE_CATEGORY[prefix] ?? \"other\";\n}\n\ninterface EslintJsonEntry {\n filePath: string;\n messages: Array<{\n ruleId: string | null;\n severity: number;\n message: string;\n line: number;\n column: number;\n fix?: { range: [number, number]; text: string } | null;\n }>;\n}\n\ninterface RuffJsonEntry {\n code: string | null;\n message: string;\n filename: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n fix?: {\n edits: Array<{\n content: string;\n location: { row: number; column: number };\n end_location: { row: number; column: number };\n }>;\n } | null;\n}\n\nexport function parseEslintJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: EslintJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse ESLint JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n const diagnostics: CheckDiagnostic[] = [];\n\n for (const entry of entries) {\n for (const msg of entry.messages) {\n if (!msg.ruleId) continue;\n diagnostics.push({\n file: entry.filePath,\n line: msg.line,\n column: msg.column,\n severity: eslintSeverityToSeverity(msg.severity),\n message: msg.message,\n category: categorizeEslintRule(msg.ruleId),\n rule: msg.ruleId,\n fixable: msg.fix != null,\n fix: msg.fix ?? undefined,\n });\n }\n }\n\n return diagnostics;\n}\n\n// ruff 0.16.8 reports a syntax error as code \"invalid-syntax\"; 0.9.10 used a null code.\nexport function isRuffSyntaxError(code: string | null): code is \"invalid-syntax\" | null {\n return code === null || code === \"invalid-syntax\";\n}\n\nexport function parseRuffJsonOutput(jsonStr: string): CheckDiagnostic[] {\n let entries: RuffJsonEntry[];\n try {\n entries = JSON.parse(jsonStr);\n } catch {\n throw new Error(\n `Failed to parse Ruff JSON output. Raw output:\\n${jsonStr.slice(0, 500)}`,\n );\n }\n return entries.flatMap((entry) => isRuffSyntaxError(entry.code) ? [] : [{\n file: entry.filename,\n line: entry.location.row,\n endLine: entry.end_location.row,\n column: entry.location.column,\n severity: \"warn\" as const,\n message: entry.message,\n category: categorizeRuffCode(entry.code),\n rule: entry.code,\n fixable: entry.fix != null,\n }]);\n}\n\nexport function formatDiagnostic(d: CheckDiagnostic): string {\n return `${d.file}:${d.line}:${d.column} ${d.severity} ${d.message} [${d.category}.${d.rule}]`;\n}\n","import { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { runAndClassify, parseOrFail } from \"./failures.js\";\nimport { isRuffSyntaxError, parseRuffJsonOutput } from \"../formatters/unified.js\";\nimport type { CheckDiagnostic, ToolFailure } from \"../orchestrator/types.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\nimport type { RuffConfig } from \"../generators/ruff.js\";\n\ntype RuffLint = NonNullable<RuffConfig[\"lint\"]>;\n\nfunction quoteList(values: string[]): string {\n return `[${values.map((s) => `\"${s}\"`).join(\", \")}]`;\n}\n\nfunction lintSelectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.select) lines.push(`select = ${quoteList(lint.select)}`);\n if (lint.ignore) lines.push(`ignore = ${quoteList(lint.ignore)}`);\n return lines;\n}\n\nfunction lintSubsectionLines(lint: RuffLint): string[] {\n const lines: string[] = [];\n if (lint.mccabe) {\n lines.push(\"[lint.mccabe]\");\n if (lint.mccabe[\"max-complexity\"]) {\n lines.push(`max-complexity = ${lint.mccabe[\"max-complexity\"]}`);\n }\n }\n if (lint.pydocstyle) {\n lines.push(\"[lint.pydocstyle]\");\n if (lint.pydocstyle.convention) {\n lines.push(`convention = \"${lint.pydocstyle.convention}\"`);\n }\n }\n if (lint.isort) {\n lines.push(\"[lint.isort]\");\n if (lint.isort[\"section-order\"]) {\n lines.push(`section-order = ${quoteList(lint.isort[\"section-order\"])}`);\n }\n }\n return lines;\n}\n\nfunction toToml(config: RuffConfig): string {\n const lines: string[] = [];\n\n if (config[\"line-length\"]) {\n lines.push(`line-length = ${config[\"line-length\"]}`);\n }\n if (config.preview) lines.push(\"preview = true\");\n\n if (config.lint) {\n lines.push(\"[lint]\", ...lintSelectionLines(config.lint), ...lintSubsectionLines(config.lint));\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n\ninterface RuffJsonMessage {\n code: string | null;\n filename: string;\n message: string;\n}\n\nfunction notChecked(file: string, message: string): ToolFailure {\n return { tool: \"ruff\", kind: \"file-not-checked\", file, message };\n}\n\n// ruff emits one entry per syntax error; a file that fails to parse is reported once.\nfunction unparsedFiles(stdout: string): ToolFailure[] {\n const byFile = new Map<string, string[]>();\n for (const e of JSON.parse(stdout) as RuffJsonMessage[]) {\n if (!isRuffSyntaxError(e.code)) continue;\n byFile.set(e.filename, [...(byFile.get(e.filename) ?? []), e.message]);\n }\n return [...byFile].map(([file, messages]) => notChecked(file, messages.join(\"; \")));\n}\n\n// ruff 0.16.8 flags an unreadable path (exit 0, \"[]\") only in this stderr wording; if ruff rewords it, the case goes silent again.\nfunction unreadFiles(stderr: string): ToolFailure[] {\n return [...stderr.matchAll(/^warning: Failed to lint (.+?): (.+)$/gm)].map((m) => notChecked(m[1]!, m[2]!));\n}\n\nfunction readRuffOutput(stdout: string, stderr: string): { diagnostics: CheckDiagnostic[]; failures: ToolFailure[] } {\n const parsed = parseOrFail(\"ruff\", () => ({\n diagnostics: parseRuffJsonOutput(stdout),\n failures: [...unparsedFiles(stdout), ...unreadFiles(stderr)],\n }));\n return parsed.ok ? parsed.value : { diagnostics: [], failures: [parsed.failure] };\n}\n\nexport async function runRuff(\n config: RuffConfig,\n files: string[],\n options?: RunnerOptions,\n): Promise<RunnerResult> {\n const tempDir = mkdtempSync(join(tmpdir(), \"codewatch-ruff-\"));\n const configPath = join(tempDir, \"ruff.toml\");\n\n try {\n writeFileSync(configPath, toToml(config), \"utf-8\");\n\n const args = [\n \"check\",\n \"--config\",\n configPath,\n \"--output-format\",\n \"json\",\n ...(options?.fix ? [\"--fix\"] : []),\n ...files,\n ];\n\n const run = await runAndClassify(\"ruff\", \"ruff\", args, { cwd: options?.cwd, timeout: options?.timeout });\n if (!run.ok) return { diagnostics: [], exitCode: run.exitCode, failures: [run.failure], skippedRules: [] };\n return { ...readRuffOutput(run.stdout, run.stderr), exitCode: run.exitCode, skippedRules: [] };\n } finally {\n rmSync(tempDir, { recursive: true, force: true });\n }\n}\n","import { generateEslintConfig } from \"../generators/eslint.js\";\nimport { generateRuffConfig } from \"../generators/ruff.js\";\nimport { runEslint } from \"../runners/eslint-runner.js\";\nimport { runRuff } from \"../runners/ruff-runner.js\";\nimport type { RunnerResult } from \"../runners/types.js\";\nimport type {\n OrchestratorOptions,\n OrchestratorResult,\n CheckDiagnostic,\n} from \"./types.js\";\n\nfunction detectLanguage(files: string[]): \"typescript\" | \"python\" | \"mixed\" {\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n if (tsFiles.length > 0 && pyFiles.length === 0) return \"typescript\";\n if (pyFiles.length > 0 && tsFiles.length === 0) return \"python\";\n return \"mixed\";\n}\n\nfunction buildSummary(\n diagnostics: CheckDiagnostic[],\n): OrchestratorResult[\"summary\"] {\n return {\n total: diagnostics.length,\n errors: diagnostics.filter((d) => d.severity === \"error\").length,\n warnings: diagnostics.filter((d) => d.severity === \"warn\").length,\n infos: diagnostics.filter((d) => d.severity === \"info\").length,\n fixed: 0,\n };\n}\n\nasync function runTools(options: OrchestratorOptions): Promise<RunnerResult[]> {\n const { profile, files, fix } = options;\n const language = options.language ?? detectLanguage(files);\n const tsFiles = files.filter((f) => /\\.[tj]sx?$/.test(f));\n const pyFiles = files.filter((f) => /\\.py$/.test(f));\n const results: RunnerResult[] = [];\n\n if ((language === \"typescript\" || language === \"mixed\") && tsFiles.length > 0) {\n const eslintConfig = generateEslintConfig(profile);\n if (eslintConfig.length > 0) results.push(await runEslint(eslintConfig, tsFiles, { fix }));\n }\n\n if ((language === \"python\" || language === \"mixed\") && pyFiles.length > 0) {\n const ruffConfig = generateRuffConfig(profile);\n if (ruffConfig.lint?.select && ruffConfig.lint.select.length > 0) {\n results.push(await runRuff(ruffConfig, pyFiles, { fix }));\n }\n }\n return results;\n}\n\nexport async function orchestrate(\n options: OrchestratorOptions,\n): Promise<OrchestratorResult> {\n const results = await runTools(options);\n const diagnostics = results.flatMap((r) => r.diagnostics);\n return {\n diagnostics,\n failures: results.flatMap((r) => r.failures),\n skippedRules: results.flatMap((r) => r.skippedRules),\n summary: buildSummary(diagnostics),\n };\n}\n","import type { RuffConfig } from \"./ruff.js\";\n\n/** The audit's pinned ruff selection: complexity, size, dead code, and defensive-bloat signals. */\nexport const AUDIT_RUFF_RULES: readonly string[] = [\n \"C901\",\n \"PLR0904\",\n \"PLR0911\",\n \"PLR0912\",\n \"PLR0913\",\n \"PLR0914\",\n \"PLR0915\",\n \"PLR0916\",\n \"PLR0917\",\n \"PLR1702\",\n \"ARG\",\n \"FBT\",\n \"ERA001\",\n \"BLE001\",\n \"S110\",\n \"TRY203\",\n \"SIM105\",\n \"F401\",\n \"F841\",\n \"PIE790\",\n \"RUF100\",\n];\n\n/** Preview is on because ruff 0.16.8 silently skips PLR0904, PLR0914, PLR0916 and PLR1702 without it. */\nexport function generateRuffAuditConfig(): RuffConfig {\n return { preview: true, lint: { select: [...AUDIT_RUFF_RULES] } };\n}\n","import { realpathSync } from \"node:fs\";\nimport { isAbsolute, relative } from \"node:path\";\nimport { generateRuffAuditConfig } from \"../generators/ruff-audit.js\";\nimport { runRuff } from \"./ruff-runner.js\";\nimport type { RunnerOptions, RunnerResult } from \"./types.js\";\n\n/** Runs the pinned audit rule set; diagnostics carry paths relative to `cwd`, since ruff reports absolute ones. */\nexport async function runRuffAudit(files: string[], options?: Omit<RunnerOptions, \"fix\">): Promise<RunnerResult> {\n const result = await runRuff(generateRuffAuditConfig(), files, options);\n const root = realpathSync(options?.cwd ?? process.cwd());\n const diagnostics = result.diagnostics.map((d) => ({ ...d, file: relativeTo(root, d.file) }));\n return { ...result, diagnostics };\n}\n\nfunction relativeTo(root: string, file: string): string {\n return isAbsolute(file) ? relative(root, file) : file;\n}\n","import { PROFILE_CATEGORIES } from \"@titan-design/style-profile\";\nimport type { Profile, ProfileCategory } from \"@titan-design/style-profile\";\nimport type { Observation } from \"@titan-design/style-analyzer\";\n\nexport interface Deviation {\n file: string;\n line: number;\n rule: string;\n expected: string;\n found: string;\n severity: \"error\" | \"warn\" | \"info\";\n}\n\nexport interface DiffResult {\n deviations: Deviation[];\n summary: {\n total: number;\n matching: number;\n deviating: number;\n };\n}\n\nfunction getSeverity(\n confidence: number,\n thresholds: Profile[\"severityThresholds\"],\n): \"error\" | \"warn\" | \"info\" {\n if (confidence >= thresholds.error) return \"error\";\n if (confidence >= thresholds.warn) return \"warn\";\n return \"info\";\n}\n\nfunction resolveProfileRule(\n profile: Profile,\n observationType: string,\n): { convention: unknown; confidence: number } | undefined {\n const [category, rule] = observationType.split(\".\");\n if (!PROFILE_CATEGORIES.includes(category as ProfileCategory)) return undefined;\n const section = profile[category as ProfileCategory];\n if (!section || typeof section !== \"object\") return undefined;\n const ruleObj = rule === undefined ? undefined : section[rule];\n if (!ruleObj || typeof ruleObj !== \"object\") return undefined;\n if (ruleObj.convention === undefined || ruleObj.confidence === undefined) {\n return undefined;\n }\n return { convention: ruleObj.convention, confidence: ruleObj.confidence };\n}\n\nexport function diffAgainstProfile(\n profile: Profile,\n observations: Observation[],\n): DiffResult {\n const deviations: Deviation[] = [];\n let matching = 0;\n\n for (const obs of observations) {\n const profileRule = resolveProfileRule(profile, obs.type);\n if (!profileRule) continue;\n\n const expected = String(profileRule.convention);\n const found = String(obs.value);\n\n if (found === expected) {\n matching++;\n } else {\n deviations.push({\n file: obs.file,\n line: obs.line,\n rule: obs.type,\n expected,\n found,\n severity: getSeverity(\n profileRule.confidence,\n profile.severityThresholds,\n ),\n });\n }\n }\n\n return {\n deviations,\n summary: {\n total: observations.length,\n matching,\n deviating: deviations.length,\n },\n };\n}\n"],"mappings":"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","names":["join","require","join","writeFileSync","mkdtempSync","rmSync","join","tmpdir","mkdtempSync","join","tmpdir","writeFileSync","rmSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titan-design/style-checker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Run external lint tools (ruff, ESLint) against configs generated from a style profile, normalize their output into one diagnostic shape, and diff observations against a profile",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,13 +31,16 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@titan-design/style-analyzer": "^0.
|
|
35
|
-
"@titan-design/style-profile": "^0.
|
|
34
|
+
"@titan-design/style-analyzer": "^0.1.0",
|
|
35
|
+
"@titan-design/style-profile": "^0.1.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"zod": "^4.0.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.46.0",
|
|
42
|
+
"@typescript-eslint/parser": "^8.46.0",
|
|
43
|
+
"eslint": "^9.39.0",
|
|
41
44
|
"zod": "^4.4.3"
|
|
42
45
|
},
|
|
43
46
|
"scripts": {
|