@geonosis/oxlint-plugin-biological-architecture 0.1.2 → 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 +39 -61
- package/corpus/.oxlintrc.json +33 -0
- package/corpus/layers/core/bad-reaches-up.ts +3 -0
- package/corpus/layers/core/thing.ts +1 -0
- package/corpus/layers/feature/bad-relative.ts +3 -0
- package/corpus/layers/feature/good-downward.ts +3 -0
- package/corpus/layers/feature/good-same-layer.ts +3 -0
- package/corpus/layers/feature/good-type-only.ts +3 -0
- package/corpus/layers/feature/sibling.ts +1 -0
- package/corpus/layers/shell/boot.ts +3 -0
- package/corpus/manifest.json +1 -0
- package/dist/{chunk-74VWOEPL.js → chunk-HXBZSOIU.js} +3 -1
- package/dist/discover-atoms.d.ts +39 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +159 -24
- package/dist/presets.d.ts +36 -0
- package/dist/presets.js +1 -1
- package/package.json +5 -8
- package/bin/geonosis-lint-parity.mjs +0 -4
- package/dist/chunk-2VSZAP5M.js +0 -240
- package/dist/chunk-BFFWNFJM.js +0 -6
- package/dist/chunk-BGX2XYEL.js +0 -153
- package/dist/chunk-FGUOGKA5.js +0 -228
- package/dist/chunk-FJ3J7B5J.js +0 -155
- package/dist/chunk-KZ5XEK6F.js +0 -83
- package/dist/chunk-NZVMV3Y5.js +0 -339
- package/dist/chunk-RNY5NDH5.js +0 -155
- package/dist/chunk-SUTNA6YD.js +0 -268
- package/dist/chunk-UM6UIUH3.js +0 -250
- package/dist/chunk-YIAUS4GG.js +0 -298
- package/dist/parity-cli.js +0 -84
- package/dist/parity.js +0 -32
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
type Preset = {
|
|
2
|
+
/** Rule ids that belong to this preset but are not ours — enabled alongside, never instead. */
|
|
3
|
+
rules: Record<string, string>;
|
|
4
|
+
};
|
|
5
|
+
declare const PRESETS: {
|
|
6
|
+
'backend-d1': {
|
|
7
|
+
rules: Record<string, "error">;
|
|
8
|
+
};
|
|
9
|
+
'backend-medusa': {
|
|
10
|
+
rules: Record<string, "error">;
|
|
11
|
+
};
|
|
12
|
+
'backend-sagaflow-cf': {
|
|
13
|
+
rules: Record<string, "error">;
|
|
14
|
+
};
|
|
15
|
+
'backend-workflows': {
|
|
16
|
+
rules: Record<string, "error">;
|
|
17
|
+
};
|
|
18
|
+
'comment-ceiling': Preset;
|
|
19
|
+
frontend: {
|
|
20
|
+
rules: Record<string, "error">;
|
|
21
|
+
};
|
|
22
|
+
recommended: {
|
|
23
|
+
rules: Record<string, "error">;
|
|
24
|
+
};
|
|
25
|
+
'tenant-scoping': {
|
|
26
|
+
rules: Record<string, "error">;
|
|
27
|
+
};
|
|
28
|
+
tooling: {
|
|
29
|
+
rules: Record<string, "error">;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
type PresetName = keyof typeof PRESETS;
|
|
33
|
+
/** The rule names a preset carries, stripped of the plugin namespace. */
|
|
34
|
+
declare const rulesOfPreset: (preset: PresetName) => string[];
|
|
35
|
+
|
|
36
|
+
export { PRESETS, type Preset, type PresetName, rulesOfPreset };
|
package/dist/presets.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/oxlint-plugin-biological-architecture",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Biological tier architecture as lint — the union of the dielime and during.day rule sets, shipped as presets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"oxlint",
|
|
@@ -20,23 +20,21 @@
|
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"type": "module",
|
|
22
22
|
"main": "dist/index.js",
|
|
23
|
-
"bin": {
|
|
24
|
-
"geonosis-lint-parity": "bin/geonosis-lint-parity.mjs"
|
|
25
|
-
},
|
|
26
23
|
"exports": {
|
|
27
24
|
".": "./dist/index.js",
|
|
28
25
|
"./discover-atoms": "./dist/discover-atoms.js",
|
|
29
|
-
"./parity": "./dist/parity.js",
|
|
30
26
|
"./presets": "./dist/presets.js"
|
|
31
27
|
},
|
|
32
28
|
"files": [
|
|
33
|
-
"bin",
|
|
34
29
|
"corpus",
|
|
35
30
|
"dist"
|
|
36
31
|
],
|
|
37
32
|
"peerDependencies": {
|
|
38
33
|
"oxlint": ">=1.77"
|
|
39
34
|
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@geonosis/lint-parity": "0.2.0"
|
|
37
|
+
},
|
|
40
38
|
"engines": {
|
|
41
39
|
"node": ">=22"
|
|
42
40
|
},
|
|
@@ -44,8 +42,7 @@
|
|
|
44
42
|
"access": "public"
|
|
45
43
|
},
|
|
46
44
|
"scripts": {
|
|
47
|
-
"build": "tsup
|
|
48
|
-
"generate:skill": "tsx scripts/generate-skill.ts",
|
|
45
|
+
"build": "tsup && node scripts/build-corpus.mjs",
|
|
49
46
|
"typecheck": "tsc --noEmit"
|
|
50
47
|
}
|
|
51
48
|
}
|
package/dist/chunk-2VSZAP5M.js
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
// src/parity.ts
|
|
2
|
-
import { spawnSync } from "child_process";
|
|
3
|
-
import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
4
|
-
import { tmpdir } from "os";
|
|
5
|
-
import { join } from "path";
|
|
6
|
-
var ANSI = /\[[0-9;]*m/g;
|
|
7
|
-
var SUMMARY_COUNT = /^(\d+) problems?$/m;
|
|
8
|
-
var RULE_IN_LINE = /\[(?:Error|Warning)\/([^\]]+)\]$/;
|
|
9
|
-
var UNIX_FINDING = /^\S[^\n]*:\d+:\d+: .*\[(?:Error|Warning)\/[^\]\n]+\]$/;
|
|
10
|
-
var ParityError = class extends Error {
|
|
11
|
-
constructor(message) {
|
|
12
|
-
super(message);
|
|
13
|
-
this.name = "ParityError";
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
var normaliseFindings = (output, cwd) => output.replaceAll(ANSI, "").split("\n").map((line) => line.trim().replace(`${cwd}/`, "").replace(/^\.\//, "")).filter((line) => UNIX_FINDING.test(line)).toSorted();
|
|
17
|
-
var readRun = ({ code, output }, cwd, side) => {
|
|
18
|
-
const findings = normaliseFindings(output, cwd);
|
|
19
|
-
const summary = SUMMARY_COUNT.exec(output);
|
|
20
|
-
const refuse = (why) => {
|
|
21
|
-
throw new ParityError(`config ${side}: ${why}
|
|
22
|
-
${output.trim()}`);
|
|
23
|
-
};
|
|
24
|
-
if (summary?.[1] !== void 0 && Number(summary[1]) !== findings.length) {
|
|
25
|
-
return refuse(`oxlint says ${summary[1]} problems, ${findings.length} lines parsed as findings`);
|
|
26
|
-
}
|
|
27
|
-
if (findings.length === 0 && code !== 0) {
|
|
28
|
-
return refuse(`oxlint exited ${code} without reporting a single finding`);
|
|
29
|
-
}
|
|
30
|
-
return findings;
|
|
31
|
-
};
|
|
32
|
-
var ruleOf = (line) => RULE_IN_LINE.exec(line)?.[1] ?? "(unattributed)";
|
|
33
|
-
var asFile = (lines) => lines.length === 0 ? "" : `${lines.join("\n")}
|
|
34
|
-
`;
|
|
35
|
-
var countByRule = (lines) => {
|
|
36
|
-
const counts = /* @__PURE__ */ new Map();
|
|
37
|
-
for (const line of lines) counts.set(ruleOf(line), (counts.get(ruleOf(line)) ?? 0) + 1);
|
|
38
|
-
return counts;
|
|
39
|
-
};
|
|
40
|
-
var missingFrom = (lines, other) => {
|
|
41
|
-
const remaining = /* @__PURE__ */ new Map();
|
|
42
|
-
for (const line of other) remaining.set(line, (remaining.get(line) ?? 0) + 1);
|
|
43
|
-
return lines.filter((line) => {
|
|
44
|
-
const left = remaining.get(line) ?? 0;
|
|
45
|
-
if (left === 0) return true;
|
|
46
|
-
remaining.set(line, left - 1);
|
|
47
|
-
return false;
|
|
48
|
-
});
|
|
49
|
-
};
|
|
50
|
-
var compareFindings = ({ a, b }) => {
|
|
51
|
-
const inA = countByRule(a);
|
|
52
|
-
const inB = countByRule(b);
|
|
53
|
-
const onlyInA = missingFrom(a, b);
|
|
54
|
-
return {
|
|
55
|
-
a,
|
|
56
|
-
b,
|
|
57
|
-
lost: onlyInA.length > 0,
|
|
58
|
-
onlyInA,
|
|
59
|
-
onlyInB: missingFrom(b, a),
|
|
60
|
-
tally: [.../* @__PURE__ */ new Set([...inA.keys(), ...inB.keys()])].toSorted().map((rule) => ({
|
|
61
|
-
a: inA.get(rule) ?? 0,
|
|
62
|
-
b: inB.get(rule) ?? 0,
|
|
63
|
-
rule
|
|
64
|
-
}))
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
|
-
var formatSummary = (parity) => {
|
|
68
|
-
const rows = parity.tally.map(
|
|
69
|
-
(one) => `| \`${one.rule}\` | ${one.a} | ${one.b} | ${one.b - one.a >= 0 ? "+" : ""}${one.b - one.a} |`
|
|
70
|
-
);
|
|
71
|
-
const lost = parity.onlyInA.length === 0 ? "None. Every finding A reported, B reported too.\n" : `${parity.onlyInA.map((line) => `- \`${line}\``).join("\n")}
|
|
72
|
-
`;
|
|
73
|
-
const gained = parity.onlyInB.length === 0 ? "None.\n" : `${parity.onlyInB.map((line) => `- \`${line}\``).join("\n")}
|
|
74
|
-
`;
|
|
75
|
-
return [
|
|
76
|
-
"# findings parity",
|
|
77
|
-
"",
|
|
78
|
-
`- findings A: **${parity.a.length}**`,
|
|
79
|
-
`- findings B: **${parity.b.length}**`,
|
|
80
|
-
`- only in A (lost): **${parity.onlyInA.length}**`,
|
|
81
|
-
`- only in B (gained): **${parity.onlyInB.length}**`,
|
|
82
|
-
"",
|
|
83
|
-
`**${parity.lost ? "FAIL" : "PASS"}** \u2014 ${parity.lost ? "B lost findings A reported. A rule that fired under A and fires nowhere under B stops the adoption." : "B reported everything A did."}`,
|
|
84
|
-
"",
|
|
85
|
-
"## per rule",
|
|
86
|
-
"",
|
|
87
|
-
"| rule | A | B | delta |",
|
|
88
|
-
"|---|---:|---:|---:|",
|
|
89
|
-
...rows,
|
|
90
|
-
"",
|
|
91
|
-
"## only in A (lost)",
|
|
92
|
-
"",
|
|
93
|
-
lost,
|
|
94
|
-
"## only in B (gained)",
|
|
95
|
-
"",
|
|
96
|
-
gained
|
|
97
|
-
].join("\n");
|
|
98
|
-
};
|
|
99
|
-
var rulesNamedBy = (config) => {
|
|
100
|
-
const parsed = JSON.parse(readFileSync(config, "utf8"));
|
|
101
|
-
return Object.keys(parsed.rules ?? {}).map((id) => id.slice(id.indexOf("/") + 1));
|
|
102
|
-
};
|
|
103
|
-
var nameOf = (rule) => rule.replace(/^.*[(/]/, "").replace(")", "");
|
|
104
|
-
var corpusOf = ({
|
|
105
|
-
configA,
|
|
106
|
-
configB,
|
|
107
|
-
corpus,
|
|
108
|
-
oxlint
|
|
109
|
-
}) => {
|
|
110
|
-
const root = mkdtempSync(join(tmpdir(), "geonosis-corpus-"));
|
|
111
|
-
const here = join(root, "corpus");
|
|
112
|
-
const firedIn = (config, side) => new Set(
|
|
113
|
-
readRun(
|
|
114
|
-
lint({ config, cwd: root, extra: ["--no-ignore", "--disable-nested-config"], oxlint, paths: [here] }),
|
|
115
|
-
root,
|
|
116
|
-
side
|
|
117
|
-
).map((line) => nameOf(ruleOf(line)))
|
|
118
|
-
);
|
|
119
|
-
let inA = /* @__PURE__ */ new Set();
|
|
120
|
-
let inB = /* @__PURE__ */ new Set();
|
|
121
|
-
try {
|
|
122
|
-
cpSync(corpus, here, { recursive: true });
|
|
123
|
-
inA = firedIn(configA, "A");
|
|
124
|
-
inB = firedIn(configB, "B");
|
|
125
|
-
} finally {
|
|
126
|
-
rmSync(root, { force: true, recursive: true });
|
|
127
|
-
}
|
|
128
|
-
const configured = [.../* @__PURE__ */ new Set([...rulesNamedBy(configA), ...rulesNamedBy(configB)])].toSorted();
|
|
129
|
-
const reach = configured.map((rule) => ({
|
|
130
|
-
firedInA: inA.has(rule),
|
|
131
|
-
firedInB: inB.has(rule),
|
|
132
|
-
rule
|
|
133
|
-
}));
|
|
134
|
-
return { neither: reach.filter((one) => !one.firedInA && !one.firedInB).map((one) => one.rule), reach };
|
|
135
|
-
};
|
|
136
|
-
var formatCorpus = (report) => {
|
|
137
|
-
const rows = report.reach.map(
|
|
138
|
-
(one) => `| \`${one.rule}\` | ${one.firedInA ? "yes" : "NO"} | ${one.firedInB ? "yes" : "NO"} |`
|
|
139
|
-
);
|
|
140
|
-
return [
|
|
141
|
-
"# corpus reach",
|
|
142
|
-
"",
|
|
143
|
-
`- rules configured: **${report.reach.length}**`,
|
|
144
|
-
`- fired under neither config: **${report.neither.length}**`,
|
|
145
|
-
"",
|
|
146
|
-
report.neither.length === 0 ? "**PASS** \u2014 every configured rule has at least one corpus file that fires it." : `**FAIL** \u2014 these rules fire nowhere in the corpus, so nothing shows they reach anything:
|
|
147
|
-
${report.neither.map((rule) => `- \`${rule}\``).join("\n")}`,
|
|
148
|
-
"",
|
|
149
|
-
"## per rule",
|
|
150
|
-
"",
|
|
151
|
-
"| rule | fired under A | fired under B |",
|
|
152
|
-
"|---|---|---|",
|
|
153
|
-
...rows,
|
|
154
|
-
""
|
|
155
|
-
].join("\n");
|
|
156
|
-
};
|
|
157
|
-
var FLAGS = { "--a": "configA", "--b": "configB", "--out": "out", "--oxlint": "oxlint" };
|
|
158
|
-
var CORPUS_FLAG = "--corpus";
|
|
159
|
-
var parseParityArgs = (argv) => {
|
|
160
|
-
const at = argv.indexOf("--");
|
|
161
|
-
const flags = (at === -1 ? argv : argv.slice(0, at)).filter((one) => one !== CORPUS_FLAG);
|
|
162
|
-
const paths = at === -1 ? [] : argv.slice(at + 1);
|
|
163
|
-
const corpus = (at === -1 ? argv : argv.slice(0, at)).includes(CORPUS_FLAG);
|
|
164
|
-
const read = {};
|
|
165
|
-
for (let index = 0; index < flags.length; index += 1) {
|
|
166
|
-
const flag = flags[index];
|
|
167
|
-
const name = FLAGS[flag];
|
|
168
|
-
if (name === void 0) throw new Error(`geonosis-lint-parity: unknown argument "${flag}"`);
|
|
169
|
-
const value = flags[index + 1];
|
|
170
|
-
if (value === void 0 || value.startsWith("--")) {
|
|
171
|
-
throw new Error(`geonosis-lint-parity: ${flag} needs a value`);
|
|
172
|
-
}
|
|
173
|
-
read[name] = value;
|
|
174
|
-
index += 1;
|
|
175
|
-
}
|
|
176
|
-
if (read.configA === void 0 || read.configB === void 0) {
|
|
177
|
-
throw new Error(
|
|
178
|
-
"geonosis-lint-parity: both --a and --b are required \u2014 one config compares to nothing"
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
return {
|
|
182
|
-
configA: read.configA,
|
|
183
|
-
configB: read.configB,
|
|
184
|
-
corpus,
|
|
185
|
-
...read.out === void 0 ? {} : { out: read.out },
|
|
186
|
-
...read.oxlint === void 0 ? {} : { oxlint: read.oxlint },
|
|
187
|
-
paths: paths.length === 0 ? ["."] : paths
|
|
188
|
-
};
|
|
189
|
-
};
|
|
190
|
-
var lint = ({
|
|
191
|
-
config,
|
|
192
|
-
cwd,
|
|
193
|
-
extra = [],
|
|
194
|
-
oxlint,
|
|
195
|
-
paths
|
|
196
|
-
}) => {
|
|
197
|
-
const run = spawnSync(oxlint, ["--format=unix", ...extra, "--config", config, ...paths], {
|
|
198
|
-
cwd,
|
|
199
|
-
encoding: "utf8",
|
|
200
|
-
env: { ...process.env, NO_COLOR: "1" },
|
|
201
|
-
maxBuffer: 64 * 1024 * 1024
|
|
202
|
-
});
|
|
203
|
-
if (run.error !== void 0) throw new ParityError(`could not run ${oxlint}: ${run.error.message}`);
|
|
204
|
-
return { code: run.status ?? -1, output: `${run.stdout ?? ""}${run.stderr ?? ""}` };
|
|
205
|
-
};
|
|
206
|
-
var parityOf = ({
|
|
207
|
-
configA,
|
|
208
|
-
configB,
|
|
209
|
-
cwd,
|
|
210
|
-
out,
|
|
211
|
-
oxlint,
|
|
212
|
-
paths
|
|
213
|
-
}) => {
|
|
214
|
-
const parity = compareFindings({
|
|
215
|
-
a: readRun(lint({ config: configA, cwd, oxlint, paths }), cwd, "A"),
|
|
216
|
-
b: readRun(lint({ config: configB, cwd, oxlint, paths }), cwd, "B")
|
|
217
|
-
});
|
|
218
|
-
if (out !== void 0) {
|
|
219
|
-
mkdirSync(out, { recursive: true });
|
|
220
|
-
writeFileSync(join(out, "a.txt"), asFile(parity.a));
|
|
221
|
-
writeFileSync(join(out, "b.txt"), asFile(parity.b));
|
|
222
|
-
writeFileSync(join(out, "only-in-a.txt"), asFile(parity.onlyInA));
|
|
223
|
-
writeFileSync(join(out, "only-in-b.txt"), asFile(parity.onlyInB));
|
|
224
|
-
writeFileSync(join(out, "SUMMARY.md"), formatSummary(parity));
|
|
225
|
-
}
|
|
226
|
-
return parity;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
export {
|
|
230
|
-
ParityError,
|
|
231
|
-
normaliseFindings,
|
|
232
|
-
readRun,
|
|
233
|
-
compareFindings,
|
|
234
|
-
formatSummary,
|
|
235
|
-
rulesNamedBy,
|
|
236
|
-
corpusOf,
|
|
237
|
-
formatCorpus,
|
|
238
|
-
parseParityArgs,
|
|
239
|
-
parityOf
|
|
240
|
-
};
|
package/dist/chunk-BFFWNFJM.js
DELETED
package/dist/chunk-BGX2XYEL.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
// src/parity.ts
|
|
2
|
-
import { execFileSync } from "child_process";
|
|
3
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
var ANSI = /\[[0-9;]*m/g;
|
|
6
|
-
var SUMMARY_LINE = /^\d+ problems?$/;
|
|
7
|
-
var RULE_IN_LINE = /\[(?:Error|Warning)\/([^\]]+)\]$/;
|
|
8
|
-
var normaliseFindings = (output, cwd) => output.replaceAll(ANSI, "").split("\n").map((line) => line.trim()).filter((line) => line !== "" && !SUMMARY_LINE.test(line)).map((line) => line.replace(`${cwd}/`, "").replace(/^\.\//, "")).toSorted();
|
|
9
|
-
var ruleOf = (line) => RULE_IN_LINE.exec(line)?.[1] ?? "(unattributed)";
|
|
10
|
-
var countByRule = (lines) => {
|
|
11
|
-
const counts = /* @__PURE__ */ new Map();
|
|
12
|
-
for (const line of lines) counts.set(ruleOf(line), (counts.get(ruleOf(line)) ?? 0) + 1);
|
|
13
|
-
return counts;
|
|
14
|
-
};
|
|
15
|
-
var missingFrom = (lines, other) => {
|
|
16
|
-
const remaining = /* @__PURE__ */ new Map();
|
|
17
|
-
for (const line of other) remaining.set(line, (remaining.get(line) ?? 0) + 1);
|
|
18
|
-
return lines.filter((line) => {
|
|
19
|
-
const left = remaining.get(line) ?? 0;
|
|
20
|
-
if (left === 0) return true;
|
|
21
|
-
remaining.set(line, left - 1);
|
|
22
|
-
return false;
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
var compareFindings = ({ a, b }) => {
|
|
26
|
-
const inA = countByRule(a);
|
|
27
|
-
const inB = countByRule(b);
|
|
28
|
-
const onlyInA = missingFrom(a, b);
|
|
29
|
-
return {
|
|
30
|
-
a,
|
|
31
|
-
b,
|
|
32
|
-
lost: onlyInA.length > 0,
|
|
33
|
-
onlyInA,
|
|
34
|
-
onlyInB: missingFrom(b, a),
|
|
35
|
-
tally: [.../* @__PURE__ */ new Set([...inA.keys(), ...inB.keys()])].toSorted().map((rule) => ({
|
|
36
|
-
a: inA.get(rule) ?? 0,
|
|
37
|
-
b: inB.get(rule) ?? 0,
|
|
38
|
-
rule
|
|
39
|
-
}))
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
var formatSummary = (parity) => {
|
|
43
|
-
const rows = parity.tally.map(
|
|
44
|
-
(one) => `| \`${one.rule}\` | ${one.a} | ${one.b} | ${one.b - one.a >= 0 ? "+" : ""}${one.b - one.a} |`
|
|
45
|
-
);
|
|
46
|
-
const lost = parity.onlyInA.length === 0 ? "None. Every finding A reported, B reported too.\n" : `${parity.onlyInA.map((line) => `- \`${line}\``).join("\n")}
|
|
47
|
-
`;
|
|
48
|
-
const gained = parity.onlyInB.length === 0 ? "None.\n" : `${parity.onlyInB.map((line) => `- \`${line}\``).join("\n")}
|
|
49
|
-
`;
|
|
50
|
-
return [
|
|
51
|
-
"# findings parity",
|
|
52
|
-
"",
|
|
53
|
-
`- findings A: **${parity.a.length}**`,
|
|
54
|
-
`- findings B: **${parity.b.length}**`,
|
|
55
|
-
`- only in A (lost): **${parity.onlyInA.length}**`,
|
|
56
|
-
`- only in B (gained): **${parity.onlyInB.length}**`,
|
|
57
|
-
"",
|
|
58
|
-
`**${parity.lost ? "FAIL" : "PASS"}** \u2014 ${parity.lost ? "B lost findings A reported. A rule that fired under A and fires nowhere under B stops the adoption." : "B reported everything A did."}`,
|
|
59
|
-
"",
|
|
60
|
-
"## per rule",
|
|
61
|
-
"",
|
|
62
|
-
"| rule | A | B | delta |",
|
|
63
|
-
"|---|---:|---:|---:|",
|
|
64
|
-
...rows,
|
|
65
|
-
"",
|
|
66
|
-
"## only in A (lost)",
|
|
67
|
-
"",
|
|
68
|
-
lost,
|
|
69
|
-
"## only in B (gained)",
|
|
70
|
-
"",
|
|
71
|
-
gained
|
|
72
|
-
].join("\n");
|
|
73
|
-
};
|
|
74
|
-
var FLAGS = { "--a": "configA", "--b": "configB", "--out": "out", "--oxlint": "oxlint" };
|
|
75
|
-
var parseParityArgs = (argv) => {
|
|
76
|
-
const at = argv.indexOf("--");
|
|
77
|
-
const flags = at === -1 ? argv : argv.slice(0, at);
|
|
78
|
-
const paths = at === -1 ? [] : argv.slice(at + 1);
|
|
79
|
-
const read = {};
|
|
80
|
-
for (let index = 0; index < flags.length; index += 1) {
|
|
81
|
-
const flag = flags[index];
|
|
82
|
-
const name = FLAGS[flag];
|
|
83
|
-
if (name === void 0) throw new Error(`geonosis-lint-parity: unknown argument "${flag}"`);
|
|
84
|
-
const value = flags[index + 1];
|
|
85
|
-
if (value === void 0 || value.startsWith("--")) {
|
|
86
|
-
throw new Error(`geonosis-lint-parity: ${flag} needs a value`);
|
|
87
|
-
}
|
|
88
|
-
read[name] = value;
|
|
89
|
-
index += 1;
|
|
90
|
-
}
|
|
91
|
-
if (read.configA === void 0 || read.configB === void 0) {
|
|
92
|
-
throw new Error("geonosis-lint-parity: both --a and --b are required \u2014 one config compares to nothing");
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
configA: read.configA,
|
|
96
|
-
configB: read.configB,
|
|
97
|
-
...read.out === void 0 ? {} : { out: read.out },
|
|
98
|
-
...read.oxlint === void 0 ? {} : { oxlint: read.oxlint },
|
|
99
|
-
paths: paths.length === 0 ? ["."] : paths
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
var lint = ({
|
|
103
|
-
config,
|
|
104
|
-
cwd,
|
|
105
|
-
oxlint,
|
|
106
|
-
paths
|
|
107
|
-
}) => {
|
|
108
|
-
try {
|
|
109
|
-
return execFileSync(oxlint, ["--format=unix", "--config", config, ...paths], {
|
|
110
|
-
cwd,
|
|
111
|
-
encoding: "utf8",
|
|
112
|
-
env: { ...process.env, NO_COLOR: "1" },
|
|
113
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
114
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
115
|
-
});
|
|
116
|
-
} catch (error) {
|
|
117
|
-
const failed = error;
|
|
118
|
-
if (failed.stdout === void 0) throw error;
|
|
119
|
-
return failed.stdout;
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
var parityOf = ({
|
|
123
|
-
configA,
|
|
124
|
-
configB,
|
|
125
|
-
cwd,
|
|
126
|
-
out,
|
|
127
|
-
oxlint,
|
|
128
|
-
paths
|
|
129
|
-
}) => {
|
|
130
|
-
const parity = compareFindings({
|
|
131
|
-
a: normaliseFindings(lint({ config: configA, cwd, oxlint, paths }), cwd),
|
|
132
|
-
b: normaliseFindings(lint({ config: configB, cwd, oxlint, paths }), cwd)
|
|
133
|
-
});
|
|
134
|
-
if (out !== void 0) {
|
|
135
|
-
mkdirSync(out, { recursive: true });
|
|
136
|
-
const asFile = (lines) => lines.length === 0 ? "" : `${lines.join("\n")}
|
|
137
|
-
`;
|
|
138
|
-
writeFileSync(join(out, "a.txt"), asFile(parity.a));
|
|
139
|
-
writeFileSync(join(out, "b.txt"), asFile(parity.b));
|
|
140
|
-
writeFileSync(join(out, "only-in-a.txt"), asFile(parity.onlyInA));
|
|
141
|
-
writeFileSync(join(out, "only-in-b.txt"), asFile(parity.onlyInB));
|
|
142
|
-
writeFileSync(join(out, "SUMMARY.md"), formatSummary(parity));
|
|
143
|
-
}
|
|
144
|
-
return parity;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export {
|
|
148
|
-
normaliseFindings,
|
|
149
|
-
compareFindings,
|
|
150
|
-
formatSummary,
|
|
151
|
-
parseParityArgs,
|
|
152
|
-
parityOf
|
|
153
|
-
};
|