@geonosis/lint-parity 2.2.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/{chunk-JNTU55OA.js → chunk-JPGZG2HA.js} +31 -7
- package/dist/index.d.ts +15 -9
- package/dist/index.js +5 -3
- package/dist/parity-cli.js +11 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# @geonosis/lint-parity
|
|
2
2
|
|
|
3
|
+
Through the front door: `geonosis lint-parity` — the metapackage pins this and every other kit tool
|
|
4
|
+
at ONE version, and passes the exit code through unchanged.
|
|
5
|
+
|
|
3
6
|
A repo does not delete its vendored copy of a lint plugin because the version numbers match. It
|
|
4
7
|
deletes it when the **findings** match: same tree, same rule ids, two configs, and a diff.
|
|
5
8
|
`geonosis-lint-parity` is that diff — and, before it, the answer to whether either config's rules
|
|
@@ -126,6 +126,8 @@ var ParityError = class extends Error {
|
|
|
126
126
|
}
|
|
127
127
|
};
|
|
128
128
|
var normaliseFindings = (output, cwd) => output.replaceAll(ANSI, "").split("\n").map((line) => line.trim().replace(`${cwd}/`, "").replace(/^\.\//, "")).filter((line) => UNIX_FINDING.test(line)).toSorted();
|
|
129
|
+
var REFUSED_CONFIG = /Failed to parse oxlint configuration file|Failed to setup JS plugin options/;
|
|
130
|
+
var refusalIn = (output) => (/^\s*(?:x|Error:)\s*(.+)$/m.exec(output)?.[1] ?? output.trim().split("\n")[0] ?? "").trim();
|
|
129
131
|
var readRun = ({ code, output }, cwd, side) => {
|
|
130
132
|
const findings = normaliseFindings(output, cwd);
|
|
131
133
|
const summary = SUMMARY_COUNT.exec(output);
|
|
@@ -136,6 +138,11 @@ ${output.trim()}`);
|
|
|
136
138
|
if (summary?.[1] !== void 0 && Number(summary[1]) !== findings.length) {
|
|
137
139
|
return refuse(`oxlint says ${summary[1]} problems, ${findings.length} lines parsed as findings`);
|
|
138
140
|
}
|
|
141
|
+
if (REFUSED_CONFIG.test(output)) {
|
|
142
|
+
return refuse(
|
|
143
|
+
`oxlint refused to parse this config and linted nothing \u2014 ${refusalIn(output)}. That is not a run with no findings. For a corpus comparison, config A is the BASE COMMIT's own corpus config against the BASE build, never HEAD's config against an older build: see docs/releasing.md.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
139
146
|
if (findings.length === 0 && code !== 0) {
|
|
140
147
|
return refuse(`oxlint exited ${code} without reporting a single finding`);
|
|
141
148
|
}
|
|
@@ -248,15 +255,22 @@ var formatSummary = (parity) => {
|
|
|
248
255
|
changed
|
|
249
256
|
].join("\n");
|
|
250
257
|
};
|
|
251
|
-
var
|
|
258
|
+
var OFF = /* @__PURE__ */ new Set(["off", 0]);
|
|
259
|
+
var severityOf = (level) => Array.isArray(level) ? level[0] : level;
|
|
260
|
+
var layersOf = (config) => {
|
|
252
261
|
const parsed = JSON.parse(readFileSync2(config, "utf8"));
|
|
253
|
-
return [
|
|
254
|
-
.../* @__PURE__ */ new Set([
|
|
255
|
-
...Object.keys(parsed.rules ?? {}),
|
|
256
|
-
...(parsed.overrides ?? []).flatMap((one) => Object.keys(one.rules ?? {}))
|
|
257
|
-
])
|
|
258
|
-
];
|
|
262
|
+
return [parsed.rules ?? {}, ...(parsed.overrides ?? []).map((one) => one.rules ?? {})];
|
|
259
263
|
};
|
|
264
|
+
var namedBy = (config) => {
|
|
265
|
+
const layers = layersOf(config);
|
|
266
|
+
const named = [...new Set(layers.flatMap((one) => Object.keys(one)))];
|
|
267
|
+
const enabled = named.filter(
|
|
268
|
+
(rule) => layers.some((one) => rule in one && !OFF.has(severityOf(one[rule])))
|
|
269
|
+
);
|
|
270
|
+
return { enabled, off: named.filter((rule) => !enabled.includes(rule)) };
|
|
271
|
+
};
|
|
272
|
+
var rulesNamedBy = (config) => namedBy(config).enabled;
|
|
273
|
+
var rulesTurnedOffBy = (config) => namedBy(config).off;
|
|
260
274
|
var ruleIdOf = (line) => {
|
|
261
275
|
const token = RULE_IN_LINE.exec(line)?.[1];
|
|
262
276
|
if (token === void 0) return UNATTRIBUTED;
|
|
@@ -342,6 +356,7 @@ var corpusOf = ({
|
|
|
342
356
|
rmSync(root, { force: true, recursive: true });
|
|
343
357
|
}
|
|
344
358
|
const configured = [.../* @__PURE__ */ new Set([...rulesNamedBy(configA), ...rulesNamedBy(configB)])].toSorted();
|
|
359
|
+
const off = [.../* @__PURE__ */ new Set([...rulesTurnedOffBy(configA), ...rulesTurnedOffBy(configB)])].filter((rule) => !configured.includes(rule)).toSorted();
|
|
345
360
|
const reach = configured.filter((rule) => exercised.has(rule)).map((rule) => {
|
|
346
361
|
const foundInA = inA.get(rule) ?? 0;
|
|
347
362
|
const foundInB = inB.get(rule) ?? 0;
|
|
@@ -359,6 +374,7 @@ var corpusOf = ({
|
|
|
359
374
|
claims,
|
|
360
375
|
claimsHeld: claims.every((one) => one.verdict === "held"),
|
|
361
376
|
neither: reach.filter((one) => !one.firedInA && !one.firedInB).map((one) => one.rule),
|
|
377
|
+
off,
|
|
362
378
|
outside: configured.filter((rule) => !exercised.has(rule)),
|
|
363
379
|
plugin: manifest.plugin,
|
|
364
380
|
reach
|
|
@@ -396,6 +412,7 @@ var formatCorpus = (report) => {
|
|
|
396
412
|
`- rules in scope of this corpus: **${report.reach.length}**`,
|
|
397
413
|
`- fired under neither config: **${report.neither.length}**`,
|
|
398
414
|
`- outside this corpus: **${report.outside.length}**`,
|
|
415
|
+
`- turned off by a config, so out of scope: **${report.off.length}**${report.off.length === 0 ? "" : ` (${report.off.map((rule) => `\`${rule}\``).join(", ")})`}`,
|
|
399
416
|
"",
|
|
400
417
|
report.neither.length === 0 ? "**PASS** \u2014 every in-scope 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:
|
|
401
418
|
${report.neither.map((rule) => `- \`${rule}\``).join("\n")}`,
|
|
@@ -425,9 +442,14 @@ var parseParityArgs = (argv) => {
|
|
|
425
442
|
const read = {};
|
|
426
443
|
let corpus;
|
|
427
444
|
let expectChanged;
|
|
445
|
+
let json = false;
|
|
428
446
|
for (let index = 0; index < flags.length; index += 1) {
|
|
429
447
|
const flag = flags[index];
|
|
430
448
|
const value = flags[index + 1];
|
|
449
|
+
if (flag === "--json") {
|
|
450
|
+
json = true;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
431
453
|
if (flag === CORPUS_FLAG) {
|
|
432
454
|
if (value === void 0 || value.startsWith("--")) {
|
|
433
455
|
throw new Error(
|
|
@@ -466,6 +488,7 @@ var parseParityArgs = (argv) => {
|
|
|
466
488
|
configB: read.configB,
|
|
467
489
|
...corpus === void 0 ? {} : { corpus },
|
|
468
490
|
...expectChanged === void 0 ? {} : { expectChanged },
|
|
491
|
+
json,
|
|
469
492
|
...read.out === void 0 ? {} : { out: read.out },
|
|
470
493
|
...read.oxlint === void 0 ? {} : { oxlint: read.oxlint },
|
|
471
494
|
paths: paths.length === 0 ? ["."] : paths
|
|
@@ -561,6 +584,7 @@ export {
|
|
|
561
584
|
compareFindings,
|
|
562
585
|
formatSummary,
|
|
563
586
|
rulesNamedBy,
|
|
587
|
+
rulesTurnedOffBy,
|
|
564
588
|
ruleIdOf,
|
|
565
589
|
MANIFEST_FILE,
|
|
566
590
|
manifestOf,
|
package/dist/index.d.ts
CHANGED
|
@@ -47,13 +47,6 @@ type Parity = {
|
|
|
47
47
|
* thread-scheduling detail, not a finding.
|
|
48
48
|
*/
|
|
49
49
|
declare const normaliseFindings: (output: string, cwd: string) => string[];
|
|
50
|
-
/**
|
|
51
|
-
* The findings of one run, or a refusal to guess. oxlint exits 1 both when it reported errors and
|
|
52
|
-
* when it never linted a file at all — a rule name it does not know, options a rule has no schema
|
|
53
|
-
* for, a plugin it could not load. All three print a paragraph and no findings, and "0 findings" on
|
|
54
|
-
* one side of a parity run reads as agreement with the other. So: a summary counting findings no
|
|
55
|
-
* line accounts for, or a non-zero exit with nothing readable on it, is an error, not a count.
|
|
56
|
-
*/
|
|
57
50
|
declare const readRun: ({ code, output }: {
|
|
58
51
|
code: number;
|
|
59
52
|
output: string;
|
|
@@ -100,20 +93,31 @@ type CorpusReport = {
|
|
|
100
93
|
* about anybody else's rule, and a verdict on one would be a guess.
|
|
101
94
|
*/
|
|
102
95
|
outside: string[];
|
|
96
|
+
/**
|
|
97
|
+
* Rules a config names and turns off. Out of scope — a rule that does not run cannot be shown to
|
|
98
|
+
* reach anything — and counted, so "in scope" is never a number with a silent subtraction in it.
|
|
99
|
+
*/
|
|
100
|
+
off: string[];
|
|
103
101
|
/** The plugin the corpus declared itself to speak for. */
|
|
104
102
|
plugin: string;
|
|
105
103
|
reach: RuleReach[];
|
|
106
104
|
};
|
|
107
105
|
/**
|
|
108
|
-
* The rule ids an oxlint config
|
|
106
|
+
* The rule ids an oxlint config ENABLES, exactly as it wrote them — plugin prefix included, and
|
|
109
107
|
* from `overrides[]` as well as from `rules`.
|
|
110
108
|
*
|
|
111
109
|
* A config's base block is only its first layer: an overrides entry may name a rule the base block
|
|
112
110
|
* never mentions, and oxlint runs it on every file that entry's globs claim. Reading `rules` alone
|
|
113
111
|
* left such a rule out of the reach scope entirely — it fired, and the report about which rules
|
|
114
112
|
* reach anything did not have a row for it.
|
|
113
|
+
*
|
|
114
|
+
* Severity is read (#16). A rule set to `"off"` does not run, so a corpus that never fires it has
|
|
115
|
+
* shown nothing about it: three rules read as "fire nowhere" under a consumer's config where the
|
|
116
|
+
* doctor, which does read severity, said one.
|
|
115
117
|
*/
|
|
116
118
|
declare const rulesNamedBy: (config: string) => string[];
|
|
119
|
+
/** The rules a config names and turns off — out of the reach scope, and counted rather than hidden. */
|
|
120
|
+
declare const rulesTurnedOffBy: (config: string) => string[];
|
|
117
121
|
/**
|
|
118
122
|
* The plugin-qualified id behind a finding's `plugin(rule)` token, in the `plugin/rule` shape a
|
|
119
123
|
* config names it by. Prefix included on purpose: `typescript(no-unused-vars)` is a different rule
|
|
@@ -176,6 +180,8 @@ type ParityArgs = {
|
|
|
176
180
|
* an empty list is not expressible, because a claim about no rules is not a claim.
|
|
177
181
|
*/
|
|
178
182
|
expectChanged?: string[];
|
|
183
|
+
/** Print the report as a document rather than as a summary a caller has to read by regex. */
|
|
184
|
+
json: boolean;
|
|
179
185
|
out?: string;
|
|
180
186
|
oxlint?: string;
|
|
181
187
|
paths: string[];
|
|
@@ -219,4 +225,4 @@ declare const parityOf: ({ configA, configB, cwd, out, oxlint, paths, }: {
|
|
|
219
225
|
paths: string[];
|
|
220
226
|
}) => Parity;
|
|
221
227
|
|
|
222
|
-
export { type CorpusClaim, type CorpusManifest, type CorpusReport, type FileCensus, MANIFEST_FILE, type MessageChange, type Parity, type ParityArgs, ParityError, type RuleReach, type RuleTally, compareFindings, corpusOf, fileCensus, filesLintedBy, formatCorpus, formatSummary, manifestOf, normaliseFindings, parityOf, parseParityArgs, readManifest, readRun, resolveOxlint, ruleIdOf, rulesNamedBy };
|
|
228
|
+
export { type CorpusClaim, type CorpusManifest, type CorpusReport, type FileCensus, MANIFEST_FILE, type MessageChange, type Parity, type ParityArgs, ParityError, type RuleReach, type RuleTally, compareFindings, corpusOf, fileCensus, filesLintedBy, formatCorpus, formatSummary, manifestOf, normaliseFindings, parityOf, parseParityArgs, readManifest, readRun, resolveOxlint, ruleIdOf, rulesNamedBy, rulesTurnedOffBy };
|
package/dist/index.js
CHANGED
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
readRun,
|
|
16
16
|
resolveOxlint,
|
|
17
17
|
ruleIdOf,
|
|
18
|
-
rulesNamedBy
|
|
19
|
-
|
|
18
|
+
rulesNamedBy,
|
|
19
|
+
rulesTurnedOffBy
|
|
20
|
+
} from "./chunk-JPGZG2HA.js";
|
|
20
21
|
export {
|
|
21
22
|
MANIFEST_FILE,
|
|
22
23
|
ParityError,
|
|
@@ -34,5 +35,6 @@ export {
|
|
|
34
35
|
readRun,
|
|
35
36
|
resolveOxlint,
|
|
36
37
|
ruleIdOf,
|
|
37
|
-
rulesNamedBy
|
|
38
|
+
rulesNamedBy,
|
|
39
|
+
rulesTurnedOffBy
|
|
38
40
|
};
|
package/dist/parity-cli.js
CHANGED
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
parseParityArgs,
|
|
10
10
|
resolveOxlint,
|
|
11
11
|
writeEnvelope
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JPGZG2HA.js";
|
|
13
13
|
|
|
14
14
|
// src/parity-cli.ts
|
|
15
15
|
import { mkdirSync, writeFileSync } from "fs";
|
|
16
16
|
import { join, resolve } from "path";
|
|
17
|
-
var USAGE = `geonosis-lint-parity --a <config-a.json> --b <config-b.json> [--oxlint <path>] [--out <dir>] -- <paths\u2026>
|
|
18
|
-
geonosis-lint-parity --corpus <dir> --a <config-a.json> --b <config-b.json> [--out <dir>]
|
|
17
|
+
var USAGE = `geonosis-lint-parity --a <config-a.json> --b <config-b.json> [--oxlint <path>] [--out <dir>] [--json] -- <paths\u2026>
|
|
18
|
+
geonosis-lint-parity --corpus <dir> --a <config-a.json> --b <config-b.json> [--out <dir>] [--json]
|
|
19
19
|
[--expect-changed <rule,rule,\u2026>]
|
|
20
20
|
|
|
21
21
|
Runs oxlint twice over the same paths under two configs and diffs the findings. Exits 1 when a
|
|
@@ -71,7 +71,8 @@ and it reads them as oxlint does rather than interpreting them itself.
|
|
|
71
71
|
oxlint
|
|
72
72
|
});
|
|
73
73
|
const text = formatCorpus(report);
|
|
74
|
-
process.stdout.write(`${
|
|
74
|
+
process.stdout.write(args.json ? `${JSON.stringify(report, void 0, 2)}
|
|
75
|
+
` : `${text}
|
|
75
76
|
`);
|
|
76
77
|
if (args.out !== void 0) {
|
|
77
78
|
const out = resolve(cwd, args.out);
|
|
@@ -89,7 +90,7 @@ and it reads them as oxlint does rather than interpreting them itself.
|
|
|
89
90
|
oxlint,
|
|
90
91
|
paths: args.paths
|
|
91
92
|
});
|
|
92
|
-
process.stdout.write(`${formatSummary(parity)}
|
|
93
|
+
if (!args.json) process.stdout.write(`${formatSummary(parity)}
|
|
93
94
|
`);
|
|
94
95
|
const at = writeEnvelope({
|
|
95
96
|
envelope: parityEnvelope(
|
|
@@ -100,8 +101,11 @@ and it reads them as oxlint does rather than interpreting them itself.
|
|
|
100
101
|
next: PARITY_NEXT,
|
|
101
102
|
root: cwd
|
|
102
103
|
});
|
|
103
|
-
process.stdout.write(
|
|
104
|
-
|
|
104
|
+
process.stdout.write(
|
|
105
|
+
args.json ? `${JSON.stringify(parity, void 0, 2)}
|
|
106
|
+
` : `envelope: ${at}
|
|
107
|
+
`
|
|
108
|
+
);
|
|
105
109
|
return parity.lost ? 1 : 0;
|
|
106
110
|
};
|
|
107
111
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/lint-parity",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"types": "./dist/index.d.ts",
|
|
5
5
|
"description": "Findings parity and rule reach over any two oxlint configs — the diff a fork is deleted on.",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@geonosis/ratchet": "2.
|
|
47
|
+
"@geonosis/ratchet": "2.3.1"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsup",
|