@goodbones/oxlint 0.1.0-beta.8 → 0.1.0-beta.9
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/build/dts/campaigns-rule.d.ts.map +1 -1
- package/build/dts/config-loader.d.ts.map +1 -1
- package/build/esm/campaigns-rule.js +77 -45
- package/build/esm/campaigns-rule.js.map +1 -1
- package/build/esm/config-loader.js +20 -2
- package/build/esm/config-loader.js.map +1 -1
- package/package.json +4 -4
- package/src/campaigns-rule.ts +79 -48
- package/src/config-loader.ts +19 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"campaigns-rule.d.ts","sourceRoot":"","sources":["../../src/campaigns-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"campaigns-rule.d.ts","sourceRoot":"","sources":["../../src/campaigns-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,YAAY,EAGlB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAEL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AAsCzB,eAAO,MAAM,iBAAiB,WAAY,YAAY,KAAG,UAuExD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,YAAY,EAOlB,MAAM,iBAAiB,CAAC;AAIzB,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAmBpD,eAAO,MAAM,kBAAkB,aACnB,MAAM,mBACC,MAAM,KACtB,OAAO,CAAC,YAAY,CA4CtB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { campaignsSelecting, evaluateCampaigns, formatMessage, leafTermsOf, reconcile, } from "@goodbones/core";
|
|
1
|
+
import { campaignsSelecting, evaluateCampaigns, formatMessage, leafTermsOf, reconcile, ReportUnavailable, } from "@goodbones/core";
|
|
2
2
|
import { sourceFactsOf } from "@goodbones/typescript";
|
|
3
3
|
import { factsOfProgram, toRepoRelative, } from "./oxlint-api.js";
|
|
4
4
|
// The campaigns family, one file at a time: every hit of a campaign selecting
|
|
@@ -22,49 +22,81 @@ const unledgered = (policy, selected, hits) => {
|
|
|
22
22
|
}
|
|
23
23
|
return hits.filter((hit) => !carried.has(hit.violation));
|
|
24
24
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
Program(node) {
|
|
45
|
-
const text = context.sourceCode.text;
|
|
46
|
-
const needsSyntax = selected.some((rule) => leafTermsOf(rule.detect).some((leaf) => leaf === "syntax" || leaf === "report" || leaf === "fn"));
|
|
47
|
-
const hits = evaluateCampaigns(selected, {
|
|
48
|
-
file,
|
|
49
|
-
text,
|
|
50
|
-
facts: sourceFactsOf(factsOfProgram(file, node)),
|
|
51
|
-
resolver: policy.resolver,
|
|
52
|
-
fileSystem: policy.fileSystem,
|
|
53
|
-
syntax: needsSyntax ? policy.syntax.parse(file, text) : null,
|
|
54
|
-
functions: policy.functions,
|
|
55
|
-
reports: policy.reports,
|
|
56
|
-
});
|
|
57
|
-
for (const hit of unledgered(policy, selected, hits)) {
|
|
58
|
-
// A match lands where it was found; a file-unit hit, on line 1, as
|
|
59
|
-
// the structure rule places a finding about the file itself.
|
|
60
|
-
const at = hit.range ?? { start: { line: 0, column: 0 } };
|
|
61
|
-
context.report({
|
|
62
|
-
message: formatMessage(hit.violation),
|
|
63
|
-
loc: { line: at.start.line + 1, column: at.start.column },
|
|
64
|
-
});
|
|
65
|
-
}
|
|
25
|
+
// A `report` a campaign names that cannot be read — a command the kernel
|
|
26
|
+
// refused to spawn, a file no step wrote — is one fact about the run, not
|
|
27
|
+
// one about each file the campaign selects. The live source keeps the
|
|
28
|
+
// failure per spec; this keeps which failures have been said, so the first
|
|
29
|
+
// selected file carries the notice and the rest stay quiet. Without it, a
|
|
30
|
+
// lint of eight hundred files is eight hundred copies of oxlint's generic
|
|
31
|
+
// "error running JS plugin", with the cause dropped by the terser formats.
|
|
32
|
+
const aside = (cause) => `A report a campaign names could not be read, so no campaign naming it is judged in this ` +
|
|
33
|
+
`run: ${cause.message}`;
|
|
34
|
+
export const makeCampaignsRule = (policy) => {
|
|
35
|
+
// Per rule instance, which is per plugin load — one process — rather than
|
|
36
|
+
// per `createOnce`, which a host may call more often than that.
|
|
37
|
+
const said = new Set();
|
|
38
|
+
return {
|
|
39
|
+
meta: {
|
|
40
|
+
type: "problem",
|
|
41
|
+
docs: {
|
|
42
|
+
description: "the campaigns: every place a pattern the repository is migrating away from still occurs, that its ledger does not carry",
|
|
66
43
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
44
|
+
schema: [],
|
|
45
|
+
},
|
|
46
|
+
createOnce(context) {
|
|
47
|
+
let file = "";
|
|
48
|
+
let selected = [];
|
|
49
|
+
return {
|
|
50
|
+
before() {
|
|
51
|
+
file = toRepoRelative(policy.repoRoot, context.filename);
|
|
52
|
+
if (file.startsWith(".."))
|
|
53
|
+
return false;
|
|
54
|
+
selected = campaignsSelecting(policy.campaignRules, file);
|
|
55
|
+
return selected.length > 0;
|
|
56
|
+
},
|
|
57
|
+
Program(node) {
|
|
58
|
+
const text = context.sourceCode.text;
|
|
59
|
+
const needsSyntax = selected.some((rule) => leafTermsOf(rule.detect).some((leaf) => leaf === "syntax" || leaf === "report" || leaf === "fn"));
|
|
60
|
+
const input = {
|
|
61
|
+
file,
|
|
62
|
+
text,
|
|
63
|
+
facts: sourceFactsOf(factsOfProgram(file, node)),
|
|
64
|
+
resolver: policy.resolver,
|
|
65
|
+
fileSystem: policy.fileSystem,
|
|
66
|
+
syntax: needsSyntax ? policy.syntax.parse(file, text) : null,
|
|
67
|
+
functions: policy.functions,
|
|
68
|
+
reports: policy.reports,
|
|
69
|
+
};
|
|
70
|
+
// One campaign at a time, so a report one of them cannot read costs
|
|
71
|
+
// that campaign's answer for this file and not its neighbours'.
|
|
72
|
+
const hits = [];
|
|
73
|
+
for (const rule of selected) {
|
|
74
|
+
try {
|
|
75
|
+
for (const hit of evaluateCampaigns([rule], input))
|
|
76
|
+
hits.push(hit);
|
|
77
|
+
}
|
|
78
|
+
catch (cause) {
|
|
79
|
+
if (!(cause instanceof ReportUnavailable))
|
|
80
|
+
throw cause;
|
|
81
|
+
const message = aside(cause);
|
|
82
|
+
if (said.has(message))
|
|
83
|
+
continue;
|
|
84
|
+
said.add(message);
|
|
85
|
+
context.report({ message, loc: { line: 1, column: 0 } });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const hit of unledgered(policy, selected, hits)) {
|
|
89
|
+
// A match lands where it was found; a file-unit hit, on line 1, as
|
|
90
|
+
// the structure rule places a finding about the file itself.
|
|
91
|
+
const at = hit.range ?? { start: { line: 0, column: 0 } };
|
|
92
|
+
context.report({
|
|
93
|
+
message: formatMessage(hit.violation),
|
|
94
|
+
loc: { line: at.start.line + 1, column: at.start.column },
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
};
|
|
70
102
|
//# sourceMappingURL=campaigns-rule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"campaigns-rule.js","sourceRoot":"","sources":["../../src/campaigns-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kBAAkB,EAElB,iBAAiB,EACjB,aAAa,EACb,WAAW,EAEX,SAAS,
|
|
1
|
+
{"version":3,"file":"campaigns-rule.js","sourceRoot":"","sources":["../../src/campaigns-rule.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { astGrepMatcher } from "@goodbones/ast-grep";
|
|
3
|
-
import { findManifestFile, loadCampaignFunctions, loadPolicy, makeFileSystemLive, makeReportSourceLive, readManifestFile, } from "@goodbones/core";
|
|
3
|
+
import { findManifestFile, loadCampaignFunctions, loadPolicy, makeFileSystemLive, makeReportSourceLive, readManifestFile, reportSpecsOf, ReportUnavailable, } from "@goodbones/core";
|
|
4
4
|
import { typescriptLanguage } from "@goodbones/typescript";
|
|
5
5
|
import * as Result from "effect/Result";
|
|
6
6
|
// The clock the campaigns are judged by; `ARCHITECTURE_NOW` pins it, as it
|
|
@@ -47,6 +47,24 @@ export const loadPolicyFromFile = async (repoRoot, configFilename) => {
|
|
|
47
47
|
});
|
|
48
48
|
if (Result.isFailure(loaded))
|
|
49
49
|
throw loaded.failure;
|
|
50
|
-
|
|
50
|
+
const policy = loaded.success;
|
|
51
|
+
// Every `report` a campaign names is read now, while oxlint has linted
|
|
52
|
+
// nothing. A `command` forks this process, and once the linter is running
|
|
53
|
+
// Linux can refuse the fork: the linter's per-thread AST buffers merge
|
|
54
|
+
// into one mapping larger than RAM and swap together, which the default
|
|
55
|
+
// overcommit heuristic refuses to duplicate. The source caches the answer,
|
|
56
|
+
// and a failure, so the rules read what was read here. A report that
|
|
57
|
+
// cannot be read is not a load failure — the campaigns rule reports it
|
|
58
|
+
// once, on the first file a campaign naming it selects.
|
|
59
|
+
for (const spec of reportSpecsOf(policy.campaignRules)) {
|
|
60
|
+
try {
|
|
61
|
+
policy.reports.diagnosticsOf(spec, "");
|
|
62
|
+
}
|
|
63
|
+
catch (cause) {
|
|
64
|
+
if (!(cause instanceof ReportUnavailable))
|
|
65
|
+
throw cause;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return policy;
|
|
51
69
|
};
|
|
52
70
|
//# sourceMappingURL=config-loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EAErB,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EAErB,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,2EAA2E;AAC3E,oBAAoB;AACpB,MAAM,OAAO,GAAG,GAAW,EAAE;IAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC5C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1E,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,uEAAuE;AACvE,+EAA+E;AAC/E,0EAA0E;AAC1E,iEAAiE;AACjE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,QAAgB,EAChB,cAAuB,EACA,EAAE;IACzB,wEAAwE;IACxE,uCAAuC;IACvC,MAAM,UAAU,GACd,cAAc,KAAK,SAAS;QAC1B,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC;QAC5B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAChD,wEAAwE;IACxE,sEAAsE;IACtE,qEAAqE;IACrE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,qBAAqB,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvF,MAAM,MAAM,GAAG,UAAU,CAAC;QACxB,QAAQ;QACR,UAAU;QACV,QAAQ;QACR,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;QAC7D,UAAU,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QACxC,SAAS;QACT,qEAAqE;QACrE,qEAAqE;QACrE,yEAAyE;QACzE,OAAO,EAAE,oBAAoB,CAAC,QAAQ,CAAC;QACvC,GAAG,EAAE,OAAO,EAAE;KACf,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAAE,MAAM,MAAM,CAAC,OAAO,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;IAC9B,uEAAuE;IACvE,0EAA0E;IAC1E,uEAAuE;IACvE,wEAAwE;IACxE,2EAA2E;IAC3E,qEAAqE;IACrE,uEAAuE;IACvE,wDAAwD;IACxD,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,iBAAiB,CAAC;gBAAE,MAAM,KAAK,CAAC;QACzD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodbones/oxlint",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The oxlint plugin for @goodbones/core: the imports, exports, members, structure and surface families as five oxlint rules, evaluated in the editor and in CI from the same manifest the CLI reads.",
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"effect": "4.0.0-beta.94",
|
|
47
|
-
"@goodbones/ast-grep": "0.1.
|
|
48
|
-
"@goodbones/
|
|
49
|
-
"@goodbones/
|
|
47
|
+
"@goodbones/ast-grep": "0.1.1",
|
|
48
|
+
"@goodbones/core": "0.1.0-beta.9",
|
|
49
|
+
"@goodbones/typescript": "0.1.0-beta.9"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"oxlint": ">=1.77.0"
|
package/src/campaigns-rule.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
leafTermsOf,
|
|
8
8
|
type LoadedPolicy,
|
|
9
9
|
reconcile,
|
|
10
|
+
ReportUnavailable,
|
|
10
11
|
} from "@goodbones/core";
|
|
11
12
|
import { sourceFactsOf } from "@goodbones/typescript";
|
|
12
13
|
|
|
@@ -43,56 +44,86 @@ const unledgered = (
|
|
|
43
44
|
return hits.filter((hit) => !carried.has(hit.violation));
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
// A `report` a campaign names that cannot be read — a command the kernel
|
|
48
|
+
// refused to spawn, a file no step wrote — is one fact about the run, not
|
|
49
|
+
// one about each file the campaign selects. The live source keeps the
|
|
50
|
+
// failure per spec; this keeps which failures have been said, so the first
|
|
51
|
+
// selected file carries the notice and the rest stay quiet. Without it, a
|
|
52
|
+
// lint of eight hundred files is eight hundred copies of oxlint's generic
|
|
53
|
+
// "error running JS plugin", with the cause dropped by the terser formats.
|
|
54
|
+
const aside = (cause: ReportUnavailable): string =>
|
|
55
|
+
`A report a campaign names could not be read, so no campaign naming it is judged in this ` +
|
|
56
|
+
`run: ${cause.message}`;
|
|
57
|
+
|
|
58
|
+
export const makeCampaignsRule = (policy: LoadedPolicy): OxlintRule => {
|
|
59
|
+
// Per rule instance, which is per plugin load — one process — rather than
|
|
60
|
+
// per `createOnce`, which a host may call more often than that.
|
|
61
|
+
const said = new Set<string>();
|
|
62
|
+
return {
|
|
63
|
+
meta: {
|
|
64
|
+
type: "problem" as const,
|
|
65
|
+
docs: {
|
|
66
|
+
description:
|
|
67
|
+
"the campaigns: every place a pattern the repository is migrating away from still occurs, that its ledger does not carry",
|
|
68
|
+
},
|
|
69
|
+
schema: [],
|
|
52
70
|
},
|
|
53
|
-
schema: [],
|
|
54
|
-
},
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
createOnce(context: RuleContext) {
|
|
73
|
+
let file = "";
|
|
74
|
+
let selected: ReadonlyArray<CompiledCampaign> = [];
|
|
59
75
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
return {
|
|
77
|
+
before() {
|
|
78
|
+
file = toRepoRelative(policy.repoRoot, context.filename);
|
|
79
|
+
if (file.startsWith("..")) return false;
|
|
80
|
+
selected = campaignsSelecting(policy.campaignRules, file);
|
|
81
|
+
return selected.length > 0;
|
|
82
|
+
},
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
Program(node: Program) {
|
|
85
|
+
const text = context.sourceCode.text;
|
|
86
|
+
const needsSyntax = selected.some((rule) =>
|
|
87
|
+
leafTermsOf(rule.detect).some(
|
|
88
|
+
(leaf) => leaf === "syntax" || leaf === "report" || leaf === "fn",
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
const input = {
|
|
92
|
+
file,
|
|
93
|
+
text,
|
|
94
|
+
facts: sourceFactsOf(factsOfProgram(file, node)),
|
|
95
|
+
resolver: policy.resolver,
|
|
96
|
+
fileSystem: policy.fileSystem,
|
|
97
|
+
syntax: needsSyntax ? policy.syntax.parse(file, text) : null,
|
|
98
|
+
functions: policy.functions,
|
|
99
|
+
reports: policy.reports,
|
|
100
|
+
};
|
|
101
|
+
// One campaign at a time, so a report one of them cannot read costs
|
|
102
|
+
// that campaign's answer for this file and not its neighbours'.
|
|
103
|
+
const hits: Array<CampaignHit> = [];
|
|
104
|
+
for (const rule of selected) {
|
|
105
|
+
try {
|
|
106
|
+
for (const hit of evaluateCampaigns([rule], input)) hits.push(hit);
|
|
107
|
+
} catch (cause) {
|
|
108
|
+
if (!(cause instanceof ReportUnavailable)) throw cause;
|
|
109
|
+
const message = aside(cause);
|
|
110
|
+
if (said.has(message)) continue;
|
|
111
|
+
said.add(message);
|
|
112
|
+
context.report({ message, loc: { line: 1, column: 0 } });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
85
115
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
116
|
+
for (const hit of unledgered(policy, selected, hits)) {
|
|
117
|
+
// A match lands where it was found; a file-unit hit, on line 1, as
|
|
118
|
+
// the structure rule places a finding about the file itself.
|
|
119
|
+
const at = hit.range ?? { start: { line: 0, column: 0 } };
|
|
120
|
+
context.report({
|
|
121
|
+
message: formatMessage(hit.violation),
|
|
122
|
+
loc: { line: at.start.line + 1, column: at.start.column },
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
};
|
package/src/config-loader.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
makeFileSystemLive,
|
|
10
10
|
makeReportSourceLive,
|
|
11
11
|
readManifestFile,
|
|
12
|
+
reportSpecsOf,
|
|
13
|
+
ReportUnavailable,
|
|
12
14
|
} from "@goodbones/core";
|
|
13
15
|
import { typescriptLanguage } from "@goodbones/typescript";
|
|
14
16
|
import * as Result from "effect/Result";
|
|
@@ -62,5 +64,21 @@ export const loadPolicyFromFile = async (
|
|
|
62
64
|
now: hostNow(),
|
|
63
65
|
});
|
|
64
66
|
if (Result.isFailure(loaded)) throw loaded.failure;
|
|
65
|
-
|
|
67
|
+
const policy = loaded.success;
|
|
68
|
+
// Every `report` a campaign names is read now, while oxlint has linted
|
|
69
|
+
// nothing. A `command` forks this process, and once the linter is running
|
|
70
|
+
// Linux can refuse the fork: the linter's per-thread AST buffers merge
|
|
71
|
+
// into one mapping larger than RAM and swap together, which the default
|
|
72
|
+
// overcommit heuristic refuses to duplicate. The source caches the answer,
|
|
73
|
+
// and a failure, so the rules read what was read here. A report that
|
|
74
|
+
// cannot be read is not a load failure — the campaigns rule reports it
|
|
75
|
+
// once, on the first file a campaign naming it selects.
|
|
76
|
+
for (const spec of reportSpecsOf(policy.campaignRules)) {
|
|
77
|
+
try {
|
|
78
|
+
policy.reports.diagnosticsOf(spec, "");
|
|
79
|
+
} catch (cause) {
|
|
80
|
+
if (!(cause instanceof ReportUnavailable)) throw cause;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return policy;
|
|
66
84
|
};
|