@titan-design/style-checker 0.0.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/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +397 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Henry Jewkes
|
|
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,64 @@
|
|
|
1
|
+
# @titan-design/style-checker
|
|
2
|
+
|
|
3
|
+
Runs external lint tools against configs generated from a style profile, and normalizes
|
|
4
|
+
their output into one diagnostic shape. A profile goes in; a ruff config and an ESLint flat
|
|
5
|
+
config come out; ruff and ESLint run over the files; their JSON output comes back as
|
|
6
|
+
`CheckDiagnostic` records with a file, line, column, severity, category and rule. It also
|
|
7
|
+
diffs a file's style observations against a profile, without any external tool.
|
|
8
|
+
|
|
9
|
+
Tier 2 of the titan-platform DAG (TP-135). Depends on `@titan-design/style-profile` and
|
|
10
|
+
`@titan-design/style-analyzer` (both tier 2; the analyzer only for the `Observation` type).
|
|
11
|
+
`zod` v4 is a peer because style-profile needs it at runtime. Ported unchanged from
|
|
12
|
+
codewatch's `@codewatch/checker`, plus `diffAgainstProfile` from codewatch's CLI.
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @titan-design/style-checker zod
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { generateRuffConfig, orchestrate } from "@titan-design/style-checker";
|
|
20
|
+
|
|
21
|
+
generateRuffConfig(profile);
|
|
22
|
+
// { lint: { select: ["N", "D", "C90"], pydocstyle: { convention: "google" }, mccabe: { "max-complexity": 30 } }, "line-length": 100 }
|
|
23
|
+
|
|
24
|
+
const { diagnostics, summary } = await orchestrate({ profile, files: ["src/app.ts", "app.py"] });
|
|
25
|
+
// diagnostics: [{ file: "app.py", line: 5, column: 4, severity: "warn", category: "naming", rule: "N806", ... }]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## API
|
|
29
|
+
|
|
30
|
+
- `generateRuffConfig(profile)` returns a `RuffConfig`. It selects `N`, `I`, `D` and `C90`
|
|
31
|
+
for naming, import order, function docs and function length that clear the profile's
|
|
32
|
+
`info` threshold, and carries `line-length`, isort section order, pydocstyle convention
|
|
33
|
+
and mccabe max complexity.
|
|
34
|
+
- `generateEslintConfig(profile)` returns an `EslintFlatConfigEntry[]`: one entry for
|
|
35
|
+
`**/*.ts` and `**/*.tsx` holding the rules style-profile's rule builders produce, or an
|
|
36
|
+
empty array when there are none.
|
|
37
|
+
- `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`, and
|
|
39
|
+
returns `{ diagnostics, summary }`. A tool is skipped when its generated config is empty.
|
|
40
|
+
`language` restricts it to one tool; otherwise it is detected from the file list.
|
|
41
|
+
- `parseEslintJsonOutput(json)` and `parseRuffJsonOutput(json)` turn each tool's
|
|
42
|
+
`--format json` output into `CheckDiagnostic[]`. Both throw on output that is not JSON.
|
|
43
|
+
- `formatDiagnostic(d)` prints `file:line:column severity message [category.rule]`.
|
|
44
|
+
- `diffAgainstProfile(profile, observations)` compares each observation's value with the
|
|
45
|
+
profile's convention for its `type` and returns `{ deviations, summary }`. Observations
|
|
46
|
+
whose type has no profile rule count toward `total` but neither match nor deviate.
|
|
47
|
+
- Types: `CheckDiagnostic`, `CheckResult`, `OrchestratorOptions`, `OrchestratorResult`,
|
|
48
|
+
`RuffConfig`, `EslintFlatConfigEntry`, `Deviation`, `DiffResult`, and `Severity`
|
|
49
|
+
(re-exported from style-profile).
|
|
50
|
+
|
|
51
|
+
Which runners fire, with which configs, is product policy. The runner seam is one
|
|
52
|
+
function per tool (`runEslint`, `runRuff`) over a shared `runTool` spawn helper; new
|
|
53
|
+
runners are added beside them.
|
|
54
|
+
|
|
55
|
+
## Gotchas
|
|
56
|
+
|
|
57
|
+
- ESLint 9 rejects the generated flat config: it names plugin rules
|
|
58
|
+
(`@typescript-eslint/…`, `perfectionist/…`, `unicorn/…`, `jsdoc/…`) but loads no plugins.
|
|
59
|
+
The runner reads only stdout, so the failure surfaces as zero diagnostics, not an error.
|
|
60
|
+
- ruff's `max-complexity` is set from the profile's `functionMaxLines`, a line count, not a
|
|
61
|
+
cyclomatic complexity.
|
|
62
|
+
- Every ruff diagnostic has severity `warn`. ESLint severity 2 maps to `error`, anything
|
|
63
|
+
else to `warn`. Nothing produces `info`.
|
|
64
|
+
- `summary.fixed` is always 0, even with `fix: true`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Severity, Profile } from '@titan-design/style-profile';
|
|
2
|
+
export { Severity } from '@titan-design/style-profile';
|
|
3
|
+
import { Observation } from '@titan-design/style-analyzer';
|
|
4
|
+
|
|
5
|
+
interface CheckDiagnostic {
|
|
6
|
+
file: string;
|
|
7
|
+
line: number;
|
|
8
|
+
column: number;
|
|
9
|
+
severity: Exclude<Severity, "off">;
|
|
10
|
+
message: string;
|
|
11
|
+
category: string;
|
|
12
|
+
rule: string;
|
|
13
|
+
fixable: boolean;
|
|
14
|
+
fix?: {
|
|
15
|
+
range: [number, number];
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
interface CheckResult {
|
|
20
|
+
diagnostics: CheckDiagnostic[];
|
|
21
|
+
tool: "eslint" | "ruff";
|
|
22
|
+
exitCode: number;
|
|
23
|
+
}
|
|
24
|
+
interface OrchestratorOptions {
|
|
25
|
+
profile: Profile;
|
|
26
|
+
files: string[];
|
|
27
|
+
fix?: boolean;
|
|
28
|
+
language?: "typescript" | "python";
|
|
29
|
+
}
|
|
30
|
+
interface OrchestratorResult {
|
|
31
|
+
diagnostics: CheckDiagnostic[];
|
|
32
|
+
summary: {
|
|
33
|
+
total: number;
|
|
34
|
+
errors: number;
|
|
35
|
+
warnings: number;
|
|
36
|
+
infos: number;
|
|
37
|
+
fixed: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare function orchestrate(options: OrchestratorOptions): Promise<OrchestratorResult>;
|
|
42
|
+
|
|
43
|
+
interface EslintFlatConfigEntry {
|
|
44
|
+
plugins?: Record<string, unknown>;
|
|
45
|
+
rules?: Record<string, unknown>;
|
|
46
|
+
files?: string[];
|
|
47
|
+
}
|
|
48
|
+
declare function generateEslintConfig(profile: Profile): EslintFlatConfigEntry[];
|
|
49
|
+
|
|
50
|
+
interface RuffConfig {
|
|
51
|
+
lint?: {
|
|
52
|
+
select?: string[];
|
|
53
|
+
ignore?: string[];
|
|
54
|
+
fixable?: string[];
|
|
55
|
+
"per-file-ignores"?: Record<string, string[]>;
|
|
56
|
+
mccabe?: {
|
|
57
|
+
"max-complexity"?: number;
|
|
58
|
+
};
|
|
59
|
+
pydocstyle?: {
|
|
60
|
+
convention?: string;
|
|
61
|
+
};
|
|
62
|
+
isort?: {
|
|
63
|
+
"known-first-party"?: string[];
|
|
64
|
+
"section-order"?: string[];
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
"line-length"?: number;
|
|
68
|
+
}
|
|
69
|
+
declare function generateRuffConfig(profile: Profile): RuffConfig;
|
|
70
|
+
|
|
71
|
+
declare function parseEslintJsonOutput(jsonStr: string): CheckDiagnostic[];
|
|
72
|
+
declare function parseRuffJsonOutput(jsonStr: string): CheckDiagnostic[];
|
|
73
|
+
declare function formatDiagnostic(d: CheckDiagnostic): string;
|
|
74
|
+
|
|
75
|
+
interface Deviation {
|
|
76
|
+
file: string;
|
|
77
|
+
line: number;
|
|
78
|
+
rule: string;
|
|
79
|
+
expected: string;
|
|
80
|
+
found: string;
|
|
81
|
+
severity: "error" | "warn" | "info";
|
|
82
|
+
}
|
|
83
|
+
interface DiffResult {
|
|
84
|
+
deviations: Deviation[];
|
|
85
|
+
summary: {
|
|
86
|
+
total: number;
|
|
87
|
+
matching: number;
|
|
88
|
+
deviating: number;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
declare function diffAgainstProfile(profile: Profile, observations: Observation[]): DiffResult;
|
|
92
|
+
|
|
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 };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// src/generators/eslint.ts
|
|
2
|
+
import {
|
|
3
|
+
buildNamingConventionRule,
|
|
4
|
+
buildImportOrderRule,
|
|
5
|
+
buildFunctionLengthRule,
|
|
6
|
+
buildFileNamingRule,
|
|
7
|
+
buildJsdocRules
|
|
8
|
+
} from "@titan-design/style-profile";
|
|
9
|
+
function generateEslintConfig(profile) {
|
|
10
|
+
const entries = [];
|
|
11
|
+
const rules = {};
|
|
12
|
+
const namingRule = buildNamingConventionRule(profile);
|
|
13
|
+
if (namingRule) rules[namingRule[0]] = namingRule[1];
|
|
14
|
+
const importRule = buildImportOrderRule(profile);
|
|
15
|
+
if (importRule) rules[importRule[0]] = importRule[1];
|
|
16
|
+
const fnLengthRule = buildFunctionLengthRule(profile);
|
|
17
|
+
if (fnLengthRule) rules[fnLengthRule[0]] = fnLengthRule[1];
|
|
18
|
+
const fileNamingRule = buildFileNamingRule(profile);
|
|
19
|
+
if (fileNamingRule) rules[fileNamingRule[0]] = fileNamingRule[1];
|
|
20
|
+
const jsdocRules = buildJsdocRules(profile);
|
|
21
|
+
for (const [name, value] of jsdocRules) {
|
|
22
|
+
rules[name] = value;
|
|
23
|
+
}
|
|
24
|
+
if (Object.keys(rules).length > 0) {
|
|
25
|
+
entries.push({
|
|
26
|
+
files: ["**/*.ts", "**/*.tsx"],
|
|
27
|
+
rules
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return entries;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/generators/ruff.ts
|
|
34
|
+
function addNaming(profile, lint) {
|
|
35
|
+
if (profile.naming) {
|
|
36
|
+
const hasNamingRules = Object.values(profile.naming).some(
|
|
37
|
+
(rule) => rule.confidence >= profile.severityThresholds.info
|
|
38
|
+
);
|
|
39
|
+
if (hasNamingRules) lint.select.push("N");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function addImportOrder(profile, lint) {
|
|
43
|
+
const importOrder = profile.structure?.importOrder;
|
|
44
|
+
if (importOrder && importOrder.confidence >= profile.severityThresholds.info) {
|
|
45
|
+
lint.select.push("I");
|
|
46
|
+
if (Array.isArray(importOrder.convention)) {
|
|
47
|
+
lint.isort = {
|
|
48
|
+
"section-order": importOrder.convention
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function addFunctionDocs(profile, lint) {
|
|
54
|
+
const functionDocs = profile.documentation?.functionDocs;
|
|
55
|
+
if (functionDocs && functionDocs.confidence >= profile.severityThresholds.info) {
|
|
56
|
+
lint.select.push("D");
|
|
57
|
+
const conv = functionDocs.convention;
|
|
58
|
+
if (typeof conv === "string" && conv !== "jsdoc-selective" && conv !== "jsdoc-all") {
|
|
59
|
+
lint.pydocstyle = { convention: conv };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function addComplexity(profile, lint) {
|
|
64
|
+
const maxLines = profile.structure?.functionMaxLines;
|
|
65
|
+
if (maxLines && maxLines.confidence >= profile.severityThresholds.info && typeof maxLines.convention === "number") {
|
|
66
|
+
lint.select.push("C90");
|
|
67
|
+
lint.mccabe = { "max-complexity": maxLines.convention };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function generateRuffConfig(profile) {
|
|
71
|
+
const lint = { select: [] };
|
|
72
|
+
const config = { lint };
|
|
73
|
+
addNaming(profile, lint);
|
|
74
|
+
addImportOrder(profile, lint);
|
|
75
|
+
addFunctionDocs(profile, lint);
|
|
76
|
+
addComplexity(profile, lint);
|
|
77
|
+
const lineLength = profile.formatting?.lineLength;
|
|
78
|
+
if (lineLength && typeof lineLength.convention === "number") {
|
|
79
|
+
config["line-length"] = lineLength.convention;
|
|
80
|
+
}
|
|
81
|
+
return config;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/runners/eslint-runner.ts
|
|
85
|
+
import { writeFileSync, mkdtempSync, rmSync } from "fs";
|
|
86
|
+
import { join } from "path";
|
|
87
|
+
import { tmpdir } from "os";
|
|
88
|
+
|
|
89
|
+
// src/runners/tool-runner.ts
|
|
90
|
+
import { spawn } from "child_process";
|
|
91
|
+
function runTool(command, args, options) {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const child = spawn(command, args, {
|
|
94
|
+
cwd: options?.cwd,
|
|
95
|
+
timeout: options?.timeout ?? 6e4,
|
|
96
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
97
|
+
});
|
|
98
|
+
const chunks = [];
|
|
99
|
+
const errChunks = [];
|
|
100
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
101
|
+
child.stderr.on("data", (chunk) => errChunks.push(chunk));
|
|
102
|
+
child.on("error", (err) => {
|
|
103
|
+
reject(new Error(`Failed to spawn ${command}: ${err.message}`));
|
|
104
|
+
});
|
|
105
|
+
child.on("close", (code) => {
|
|
106
|
+
resolve({
|
|
107
|
+
stdout: Buffer.concat(chunks).toString("utf-8"),
|
|
108
|
+
stderr: Buffer.concat(errChunks).toString("utf-8"),
|
|
109
|
+
exitCode: code ?? 1
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/formatters/unified.ts
|
|
116
|
+
var RUFF_CODE_CATEGORY = {
|
|
117
|
+
N: "naming",
|
|
118
|
+
I: "imports",
|
|
119
|
+
D: "documentation",
|
|
120
|
+
C: "complexity",
|
|
121
|
+
E: "formatting",
|
|
122
|
+
W: "formatting",
|
|
123
|
+
F: "errors"
|
|
124
|
+
};
|
|
125
|
+
var ESLINT_RULE_CATEGORY = {
|
|
126
|
+
"@typescript-eslint/naming-convention": "naming",
|
|
127
|
+
"unicorn/filename-case": "naming",
|
|
128
|
+
"perfectionist/sort-imports": "imports",
|
|
129
|
+
"import/order": "imports",
|
|
130
|
+
"max-lines-per-function": "structure",
|
|
131
|
+
"max-depth": "structure",
|
|
132
|
+
"jsdoc/require-jsdoc": "documentation"
|
|
133
|
+
};
|
|
134
|
+
function eslintSeverityToSeverity(eslintSeverity) {
|
|
135
|
+
return eslintSeverity === 2 ? "error" : "warn";
|
|
136
|
+
}
|
|
137
|
+
function categorizeEslintRule(ruleId) {
|
|
138
|
+
if (ESLINT_RULE_CATEGORY[ruleId]) return ESLINT_RULE_CATEGORY[ruleId];
|
|
139
|
+
if (ruleId.startsWith("@typescript-eslint/")) return "typescript";
|
|
140
|
+
if (ruleId.startsWith("jsdoc/")) return "documentation";
|
|
141
|
+
if (ruleId.startsWith("unicorn/")) return "style";
|
|
142
|
+
if (ruleId.startsWith("import/")) return "imports";
|
|
143
|
+
return "other";
|
|
144
|
+
}
|
|
145
|
+
function categorizeRuffCode(code) {
|
|
146
|
+
const prefix = code.replace(/[0-9]/g, "");
|
|
147
|
+
return RUFF_CODE_CATEGORY[prefix] ?? "other";
|
|
148
|
+
}
|
|
149
|
+
function parseEslintJsonOutput(jsonStr) {
|
|
150
|
+
let entries;
|
|
151
|
+
try {
|
|
152
|
+
entries = JSON.parse(jsonStr);
|
|
153
|
+
} catch {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Failed to parse ESLint JSON output. Raw output:
|
|
156
|
+
${jsonStr.slice(0, 500)}`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
const diagnostics = [];
|
|
160
|
+
for (const entry of entries) {
|
|
161
|
+
for (const msg of entry.messages) {
|
|
162
|
+
if (!msg.ruleId) continue;
|
|
163
|
+
diagnostics.push({
|
|
164
|
+
file: entry.filePath,
|
|
165
|
+
line: msg.line,
|
|
166
|
+
column: msg.column,
|
|
167
|
+
severity: eslintSeverityToSeverity(msg.severity),
|
|
168
|
+
message: msg.message,
|
|
169
|
+
category: categorizeEslintRule(msg.ruleId),
|
|
170
|
+
rule: msg.ruleId,
|
|
171
|
+
fixable: msg.fix != null,
|
|
172
|
+
fix: msg.fix ?? void 0
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return diagnostics;
|
|
177
|
+
}
|
|
178
|
+
function parseRuffJsonOutput(jsonStr) {
|
|
179
|
+
let entries;
|
|
180
|
+
try {
|
|
181
|
+
entries = JSON.parse(jsonStr);
|
|
182
|
+
} catch {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Failed to parse Ruff JSON output. Raw output:
|
|
185
|
+
${jsonStr.slice(0, 500)}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return entries.map((entry) => ({
|
|
189
|
+
file: entry.filename,
|
|
190
|
+
line: entry.location.row,
|
|
191
|
+
column: entry.location.column,
|
|
192
|
+
severity: "warn",
|
|
193
|
+
message: entry.message,
|
|
194
|
+
category: categorizeRuffCode(entry.code),
|
|
195
|
+
rule: entry.code,
|
|
196
|
+
fixable: entry.fix != null
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
function formatDiagnostic(d) {
|
|
200
|
+
return `${d.file}:${d.line}:${d.column} ${d.severity} ${d.message} [${d.category}.${d.rule}]`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/runners/eslint-runner.ts
|
|
204
|
+
async function runEslint(config, files, options) {
|
|
205
|
+
const tempDir = mkdtempSync(join(tmpdir(), "codewatch-eslint-"));
|
|
206
|
+
const configPath = join(tempDir, "eslint.config.js");
|
|
207
|
+
try {
|
|
208
|
+
const configContent = `export default ${JSON.stringify(config, null, 2)};`;
|
|
209
|
+
writeFileSync(configPath, configContent, "utf-8");
|
|
210
|
+
const args = [
|
|
211
|
+
"--config",
|
|
212
|
+
configPath,
|
|
213
|
+
"--format",
|
|
214
|
+
"json",
|
|
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 };
|
|
221
|
+
} finally {
|
|
222
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/runners/ruff-runner.ts
|
|
227
|
+
import { writeFileSync as writeFileSync2, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
|
|
228
|
+
import { join as join2 } from "path";
|
|
229
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
230
|
+
function quoteList(values) {
|
|
231
|
+
return `[${values.map((s) => `"${s}"`).join(", ")}]`;
|
|
232
|
+
}
|
|
233
|
+
function lintSelectionLines(lint) {
|
|
234
|
+
const lines = [];
|
|
235
|
+
if (lint.select) lines.push(`select = ${quoteList(lint.select)}`);
|
|
236
|
+
if (lint.ignore) lines.push(`ignore = ${quoteList(lint.ignore)}`);
|
|
237
|
+
return lines;
|
|
238
|
+
}
|
|
239
|
+
function lintSubsectionLines(lint) {
|
|
240
|
+
const lines = [];
|
|
241
|
+
if (lint.mccabe) {
|
|
242
|
+
lines.push("[lint.mccabe]");
|
|
243
|
+
if (lint.mccabe["max-complexity"]) {
|
|
244
|
+
lines.push(`max-complexity = ${lint.mccabe["max-complexity"]}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (lint.pydocstyle) {
|
|
248
|
+
lines.push("[lint.pydocstyle]");
|
|
249
|
+
if (lint.pydocstyle.convention) {
|
|
250
|
+
lines.push(`convention = "${lint.pydocstyle.convention}"`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (lint.isort) {
|
|
254
|
+
lines.push("[lint.isort]");
|
|
255
|
+
if (lint.isort["section-order"]) {
|
|
256
|
+
lines.push(`section-order = ${quoteList(lint.isort["section-order"])}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return lines;
|
|
260
|
+
}
|
|
261
|
+
function toToml(config) {
|
|
262
|
+
const lines = [];
|
|
263
|
+
if (config["line-length"]) {
|
|
264
|
+
lines.push(`line-length = ${config["line-length"]}`);
|
|
265
|
+
}
|
|
266
|
+
if (config.lint) {
|
|
267
|
+
lines.push("[lint]", ...lintSelectionLines(config.lint), ...lintSubsectionLines(config.lint));
|
|
268
|
+
}
|
|
269
|
+
return lines.join("\n") + "\n";
|
|
270
|
+
}
|
|
271
|
+
async function runRuff(config, files, options) {
|
|
272
|
+
const tempDir = mkdtempSync2(join2(tmpdir2(), "codewatch-ruff-"));
|
|
273
|
+
const configPath = join2(tempDir, "ruff.toml");
|
|
274
|
+
try {
|
|
275
|
+
writeFileSync2(configPath, toToml(config), "utf-8");
|
|
276
|
+
const args = [
|
|
277
|
+
"check",
|
|
278
|
+
"--config",
|
|
279
|
+
configPath,
|
|
280
|
+
"--output-format",
|
|
281
|
+
"json",
|
|
282
|
+
...options?.fix ? ["--fix"] : [],
|
|
283
|
+
...files
|
|
284
|
+
];
|
|
285
|
+
const result = await runTool("ruff", args);
|
|
286
|
+
const diagnostics = result.stdout.trim() ? parseRuffJsonOutput(result.stdout) : [];
|
|
287
|
+
return { diagnostics, exitCode: result.exitCode };
|
|
288
|
+
} finally {
|
|
289
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/orchestrator/orchestrate.ts
|
|
294
|
+
function detectLanguage(files) {
|
|
295
|
+
const tsFiles = files.filter((f) => /\.[tj]sx?$/.test(f));
|
|
296
|
+
const pyFiles = files.filter((f) => /\.py$/.test(f));
|
|
297
|
+
if (tsFiles.length > 0 && pyFiles.length === 0) return "typescript";
|
|
298
|
+
if (pyFiles.length > 0 && tsFiles.length === 0) return "python";
|
|
299
|
+
return "mixed";
|
|
300
|
+
}
|
|
301
|
+
function buildSummary(diagnostics) {
|
|
302
|
+
return {
|
|
303
|
+
total: diagnostics.length,
|
|
304
|
+
errors: diagnostics.filter((d) => d.severity === "error").length,
|
|
305
|
+
warnings: diagnostics.filter((d) => d.severity === "warn").length,
|
|
306
|
+
infos: diagnostics.filter((d) => d.severity === "info").length,
|
|
307
|
+
fixed: 0
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
async function orchestrate(options) {
|
|
311
|
+
const { profile, files, fix } = options;
|
|
312
|
+
const language = options.language ?? detectLanguage(files);
|
|
313
|
+
const allDiagnostics = [];
|
|
314
|
+
const tsFiles = files.filter((f) => /\.[tj]sx?$/.test(f));
|
|
315
|
+
const pyFiles = files.filter((f) => /\.py$/.test(f));
|
|
316
|
+
if ((language === "typescript" || language === "mixed") && tsFiles.length > 0) {
|
|
317
|
+
const eslintConfig = generateEslintConfig(profile);
|
|
318
|
+
if (eslintConfig.length > 0) {
|
|
319
|
+
const result = await runEslint(eslintConfig, tsFiles, { fix });
|
|
320
|
+
allDiagnostics.push(...result.diagnostics);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if ((language === "python" || language === "mixed") && pyFiles.length > 0) {
|
|
324
|
+
const ruffConfig = generateRuffConfig(profile);
|
|
325
|
+
if (ruffConfig.lint?.select && ruffConfig.lint.select.length > 0) {
|
|
326
|
+
const result = await runRuff(ruffConfig, pyFiles, { fix });
|
|
327
|
+
allDiagnostics.push(...result.diagnostics);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
diagnostics: allDiagnostics,
|
|
332
|
+
summary: buildSummary(allDiagnostics)
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/profile-diff/diff-against-profile.ts
|
|
337
|
+
import { PROFILE_CATEGORIES } from "@titan-design/style-profile";
|
|
338
|
+
function getSeverity(confidence, thresholds) {
|
|
339
|
+
if (confidence >= thresholds.error) return "error";
|
|
340
|
+
if (confidence >= thresholds.warn) return "warn";
|
|
341
|
+
return "info";
|
|
342
|
+
}
|
|
343
|
+
function resolveProfileRule(profile, observationType) {
|
|
344
|
+
const [category, rule] = observationType.split(".");
|
|
345
|
+
if (!PROFILE_CATEGORIES.includes(category)) return void 0;
|
|
346
|
+
const section = profile[category];
|
|
347
|
+
if (!section || typeof section !== "object") return void 0;
|
|
348
|
+
const ruleObj = rule === void 0 ? void 0 : section[rule];
|
|
349
|
+
if (!ruleObj || typeof ruleObj !== "object") return void 0;
|
|
350
|
+
if (ruleObj.convention === void 0 || ruleObj.confidence === void 0) {
|
|
351
|
+
return void 0;
|
|
352
|
+
}
|
|
353
|
+
return { convention: ruleObj.convention, confidence: ruleObj.confidence };
|
|
354
|
+
}
|
|
355
|
+
function diffAgainstProfile(profile, observations) {
|
|
356
|
+
const deviations = [];
|
|
357
|
+
let matching = 0;
|
|
358
|
+
for (const obs of observations) {
|
|
359
|
+
const profileRule = resolveProfileRule(profile, obs.type);
|
|
360
|
+
if (!profileRule) continue;
|
|
361
|
+
const expected = String(profileRule.convention);
|
|
362
|
+
const found = String(obs.value);
|
|
363
|
+
if (found === expected) {
|
|
364
|
+
matching++;
|
|
365
|
+
} else {
|
|
366
|
+
deviations.push({
|
|
367
|
+
file: obs.file,
|
|
368
|
+
line: obs.line,
|
|
369
|
+
rule: obs.type,
|
|
370
|
+
expected,
|
|
371
|
+
found,
|
|
372
|
+
severity: getSeverity(
|
|
373
|
+
profileRule.confidence,
|
|
374
|
+
profile.severityThresholds
|
|
375
|
+
)
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
deviations,
|
|
381
|
+
summary: {
|
|
382
|
+
total: observations.length,
|
|
383
|
+
matching,
|
|
384
|
+
deviating: deviations.length
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
export {
|
|
389
|
+
diffAgainstProfile,
|
|
390
|
+
formatDiagnostic,
|
|
391
|
+
generateEslintConfig,
|
|
392
|
+
generateRuffConfig,
|
|
393
|
+
orchestrate,
|
|
394
|
+
parseEslintJsonOutput,
|
|
395
|
+
parseRuffJsonOutput
|
|
396
|
+
};
|
|
397
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@titan-design/style-checker",
|
|
3
|
+
"version": "0.0.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"author": "Henry Jewkes",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"homepage": "https://github.com/HJewkes/titan-platform#readme",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/HJewkes/titan-platform.git",
|
|
24
|
+
"directory": "packages/style-checker"
|
|
25
|
+
},
|
|
26
|
+
"bugs": "https://github.com/HJewkes/titan-platform/issues",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@titan-design/style-analyzer": "^0.0.0",
|
|
35
|
+
"@titan-design/style-profile": "^0.0.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"zod": "^4.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"zod": "^4.4.3"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"lint": "eslint src"
|
|
47
|
+
}
|
|
48
|
+
}
|