@goodbones/oxlint 0.1.0-beta.1 → 0.1.0-beta.10
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 +4 -0
- package/build/dts/campaigns-rule.d.ts.map +1 -0
- package/build/dts/config-loader.d.ts +0 -1
- package/build/dts/config-loader.d.ts.map +1 -1
- package/build/dts/exports-rule.d.ts.map +1 -1
- package/build/dts/imports-rule.d.ts.map +1 -1
- package/build/dts/members-rule.d.ts.map +1 -1
- package/build/dts/oxlint-api.d.ts +4 -33
- package/build/dts/oxlint-api.d.ts.map +1 -1
- package/build/dts/plugin.d.ts +1 -0
- package/build/dts/plugin.d.ts.map +1 -1
- package/build/dts/surface-rule.d.ts.map +1 -1
- package/build/esm/campaigns-rule.js +102 -0
- package/build/esm/campaigns-rule.js.map +1 -0
- package/build/esm/config-loader.js +56 -7
- package/build/esm/config-loader.js.map +1 -1
- package/build/esm/exports-rule.js +15 -71
- package/build/esm/exports-rule.js.map +1 -1
- package/build/esm/imports-rule.js +14 -26
- package/build/esm/imports-rule.js.map +1 -1
- package/build/esm/members-rule.js +10 -112
- package/build/esm/members-rule.js.map +1 -1
- package/build/esm/oxlint-api.js +22 -20
- package/build/esm/oxlint-api.js.map +1 -1
- package/build/esm/plugin.js +4 -2
- package/build/esm/plugin.js.map +1 -1
- package/build/esm/surface-rule.js +11 -127
- package/build/esm/surface-rule.js.map +1 -1
- package/package.json +4 -3
- package/src/campaigns-rule.ts +129 -0
- package/src/config-loader.ts +60 -7
- package/src/exports-rule.ts +19 -106
- package/src/imports-rule.ts +16 -35
- package/src/members-rule.ts +11 -165
- package/src/oxlint-api.ts +31 -47
- package/src/plugin.ts +5 -2
- package/src/surface-rule.ts +15 -189
|
@@ -0,0 +1 @@
|
|
|
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,5 +1,4 @@
|
|
|
1
1
|
import { type LoadedPolicy } from "@goodbones/core";
|
|
2
2
|
export type { LoadedPolicy } from "@goodbones/core";
|
|
3
|
-
export { DEFAULT_CONFIG_FILENAME } from "@goodbones/core";
|
|
4
3
|
export declare const loadPolicyFromFile: (repoRoot: string, configFilename?: string) => Promise<LoadedPolicy>;
|
|
5
4
|
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"
|
|
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,CAgDtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exports-rule.d.ts","sourceRoot":"","sources":["../../src/exports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"exports-rule.d.ts","sourceRoot":"","sources":["../../src/exports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAIL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AAYzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UA6ErD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imports-rule.d.ts","sourceRoot":"","sources":["../../src/imports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAGlB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"imports-rule.d.ts","sourceRoot":"","sources":["../../src/imports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAGlB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAGL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AAOzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UAoDrD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"members-rule.d.ts","sourceRoot":"","sources":["../../src/members-rule.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"members-rule.d.ts","sourceRoot":"","sources":["../../src/members-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAGL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UAiCrD,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReadFacts, type SyntaxNode } from "@goodbones/typescript";
|
|
1
2
|
import type { RuleTester } from "oxlint/plugins-dev";
|
|
2
3
|
export type OxlintRule = Parameters<RuleTester["run"]>[1];
|
|
3
4
|
export type RuleContext = Parameters<Extract<OxlintRule, {
|
|
@@ -8,39 +9,9 @@ export type ReportableNode = Extract<Diagnostic, {
|
|
|
8
9
|
node: unknown;
|
|
9
10
|
}>["node"];
|
|
10
11
|
export type Fixer = Parameters<NonNullable<Diagnostic["fix"]>>[0];
|
|
11
|
-
export type
|
|
12
|
-
readonly source?: {
|
|
13
|
-
readonly value?: unknown;
|
|
14
|
-
} | null;
|
|
15
|
-
};
|
|
12
|
+
export type Program = RuleContext["sourceCode"]["ast"];
|
|
16
13
|
export declare const toRepoRelative: (repoRoot: string, filename: string) => string;
|
|
17
|
-
export declare const
|
|
18
|
-
export
|
|
19
|
-
readonly callee?: {
|
|
20
|
-
readonly type: string;
|
|
21
|
-
readonly name?: unknown;
|
|
22
|
-
} | null;
|
|
23
|
-
readonly arguments?: ReadonlyArray<{
|
|
24
|
-
readonly type: string;
|
|
25
|
-
readonly value?: unknown;
|
|
26
|
-
}> | null;
|
|
27
|
-
};
|
|
28
|
-
export type ImportExpressionNode = ReportableNode & {
|
|
29
|
-
readonly source?: {
|
|
30
|
-
readonly type?: string;
|
|
31
|
-
readonly value?: unknown;
|
|
32
|
-
} | null;
|
|
33
|
-
};
|
|
34
|
-
export type ImportEqualsNode = ReportableNode & {
|
|
35
|
-
readonly moduleReference?: {
|
|
36
|
-
readonly type: string;
|
|
37
|
-
readonly expression?: {
|
|
38
|
-
readonly value?: unknown;
|
|
39
|
-
} | null;
|
|
40
|
-
} | null;
|
|
41
|
-
};
|
|
42
|
-
export declare const requireSpecifierOf: (node: CallNode) => string | null;
|
|
43
|
-
export declare const importExpressionSpecifierOf: (node: ImportExpressionNode) => string | null;
|
|
44
|
-
export declare const importEqualsSpecifierOf: (node: ImportEqualsNode) => string | null;
|
|
14
|
+
export declare const at: (node: SyntaxNode) => ReportableNode;
|
|
15
|
+
export declare const factsOfProgram: (file: string, program: Program) => ReadFacts;
|
|
45
16
|
export {};
|
|
46
17
|
//# sourceMappingURL=oxlint-api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oxlint-api.d.ts","sourceRoot":"","sources":["../../src/oxlint-api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpG,KAAK,UAAU,GAAG,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,MAAM,CAAC,CAAC;AAC5E,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"oxlint-api.d.ts","sourceRoot":"","sources":["../../src/oxlint-api.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpG,KAAK,UAAU,GAAG,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,MAAM,CAAC,CAAC;AAC5E,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAIlE,MAAM,MAAM,OAAO,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC;AAEvD,eAAO,MAAM,cAAc,aAAc,MAAM,YAAY,MAAM,KAAG,MACP,CAAC;AAI9D,eAAO,MAAM,EAAE,SAAU,UAAU,KAAG,cAEpC,CAAC;AAOH,eAAO,MAAM,cAAc,SAAU,MAAM,WAAW,OAAO,KAAG,SAW/D,CAAC"}
|
package/build/dts/plugin.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAoBlD,eAAO,MAAM,KAAK,EAAE;IAClB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;CAQhC,CAAC;AAEF,QAAA,MAAM,MAAM,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAA;CAGrF,CAAC;eAEa,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface-rule.d.ts","sourceRoot":"","sources":["../../src/surface-rule.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"surface-rule.d.ts","sourceRoot":"","sources":["../../src/surface-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UA4CrD,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { campaignsSelecting, evaluateCampaigns, formatMessage, leafTermsOf, reconcile, ReportUnavailable, } from "@goodbones/core";
|
|
2
|
+
import { sourceFactsOf } from "@goodbones/typescript";
|
|
3
|
+
import { factsOfProgram, toRepoRelative, } from "./oxlint-api.js";
|
|
4
|
+
// The campaigns family, one file at a time: every hit of a campaign selecting
|
|
5
|
+
// the file that its ledger does not carry, reported at the match's position
|
|
6
|
+
// with the campaign's `how` as the message. The facts come from oxlint's tree
|
|
7
|
+
// through the pack's reader, as the other rules read them; the `syntax` term
|
|
8
|
+
// is answered by the same matcher the CLI uses, over `sourceCode.text` —
|
|
9
|
+
// "one engine" is this family's parity contract, rather than a corpus.
|
|
10
|
+
// Whether a hit is carried by the ledger, judged against the file's hits
|
|
11
|
+
// alone: entries are per file, so a file's reconciliation is the whole
|
|
12
|
+
// campaign's restricted to it.
|
|
13
|
+
const unledgered = (policy, selected, hits) => {
|
|
14
|
+
const carried = new Set();
|
|
15
|
+
for (const rule of selected) {
|
|
16
|
+
const ledger = policy.ledgers.get(rule.id);
|
|
17
|
+
if (ledger === undefined)
|
|
18
|
+
continue;
|
|
19
|
+
const own = hits.filter((hit) => hit.campaign === rule.id).map((hit) => hit.violation);
|
|
20
|
+
for (const one of reconcile(ledger, own, rule.unit).ledgered)
|
|
21
|
+
carried.add(one);
|
|
22
|
+
}
|
|
23
|
+
return hits.filter((hit) => !carried.has(hit.violation));
|
|
24
|
+
};
|
|
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",
|
|
43
|
+
},
|
|
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
|
+
};
|
|
102
|
+
//# sourceMappingURL=campaigns-rule.js.map
|
|
@@ -0,0 +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,EACT,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EACL,cAAc,EAId,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yEAAyE;AACzE,uEAAuE;AAEvE,yEAAyE;AACzE,uEAAuE;AACvE,+BAA+B;AAC/B,MAAM,UAAU,GAAG,CACjB,MAAoB,EACpB,QAAyC,EACzC,IAAgC,EACJ,EAAE;IAC9B,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvF,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;AAC3D,CAAC,CAAC;AAEF,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,2EAA2E;AAC3E,0EAA0E;AAC1E,0EAA0E;AAC1E,2EAA2E;AAC3E,MAAM,KAAK,GAAG,CAAC,KAAwB,EAAU,EAAE,CACjD,0FAA0F;IAC1F,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;AAE1B,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAoB,EAAc,EAAE;IACpE,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,SAAkB;YACxB,IAAI,EAAE;gBACJ,WAAW,EACT,yHAAyH;aAC5H;YACD,MAAM,EAAE,EAAE;SACX;QAED,UAAU,CAAC,OAAoB;YAC7B,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,IAAI,QAAQ,GAAoC,EAAE,CAAC;YAEnD,OAAO;gBACL,MAAM;oBACJ,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;oBACzD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;wBAAE,OAAO,KAAK,CAAC;oBACxC,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;oBAC1D,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;gBAC7B,CAAC;gBAED,OAAO,CAAC,IAAa;oBACnB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;oBACrC,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CACzC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAC3B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAClE,CACF,CAAC;oBACF,MAAM,KAAK,GAAG;wBACZ,IAAI;wBACJ,IAAI;wBACJ,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;wBAChD,QAAQ,EAAE,MAAM,CAAC,QAAQ;wBACzB,UAAU,EAAE,MAAM,CAAC,UAAU;wBAC7B,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;wBAC5D,SAAS,EAAE,MAAM,CAAC,SAAS;wBAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;qBACxB,CAAC;oBACF,oEAAoE;oBACpE,gEAAgE;oBAChE,MAAM,IAAI,GAAuB,EAAE,CAAC;oBACpC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;wBAC5B,IAAI,CAAC;4BACH,KAAK,MAAM,GAAG,IAAI,iBAAiB,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;gCAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;wBACrE,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACf,IAAI,CAAC,CAAC,KAAK,YAAY,iBAAiB,CAAC;gCAAE,MAAM,KAAK,CAAC;4BACvD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;4BAC7B,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;gCAAE,SAAS;4BAChC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;4BAClB,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;wBAC3D,CAAC;oBACH,CAAC;oBAED,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;wBACrD,mEAAmE;wBACnE,6DAA6D;wBAC7D,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC1D,OAAO,CAAC,MAAM,CAAC;4BACb,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC;4BACrC,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE;yBAC1D,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -1,24 +1,73 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { astGrepMatcher } from "@goodbones/ast-grep";
|
|
3
|
+
import { findManifestFile, loadCampaignFunctions, loadPolicy, makeFileSystemLive, makeReportSourceLive, readManifestFile, reportSpecsOf, ReportUnavailable, } from "@goodbones/core";
|
|
3
4
|
import { typescriptLanguage } from "@goodbones/typescript";
|
|
4
5
|
import * as Result from "effect/Result";
|
|
5
|
-
|
|
6
|
+
// The clock the campaigns are judged by; `ARCHITECTURE_NOW` pins it, as it
|
|
7
|
+
// does for the CLI.
|
|
8
|
+
const hostNow = () => {
|
|
9
|
+
const pinned = process.env.ARCHITECTURE_NOW;
|
|
10
|
+
if (pinned === undefined || pinned === "")
|
|
11
|
+
return Date.now();
|
|
12
|
+
const parsed = /^\d+$/.test(pinned) ? Number(pinned) : Date.parse(pinned);
|
|
13
|
+
if (Number.isNaN(parsed)) {
|
|
14
|
+
throw new Error(`ARCHITECTURE_NOW is ${JSON.stringify(pinned)}, which is not a date.`);
|
|
15
|
+
}
|
|
16
|
+
return parsed;
|
|
17
|
+
};
|
|
6
18
|
// The plugin's composition root: read the manifest file, construct the
|
|
7
19
|
// language packs and the live file system, and hand them to the loader. A load
|
|
8
20
|
// failure throws out of here and out of oxlint's import of the plugin — a
|
|
9
21
|
// plugin that came up with no policy would report nothing and be
|
|
10
22
|
// indistinguishable from a clean codebase.
|
|
11
|
-
export const loadPolicyFromFile = async (repoRoot, configFilename
|
|
12
|
-
|
|
23
|
+
export const loadPolicyFromFile = async (repoRoot, configFilename) => {
|
|
24
|
+
// Named, or discovered: architecture.yaml, .yml, .json, or .config.mjs,
|
|
25
|
+
// exactly one of which may be present.
|
|
26
|
+
const configPath = configFilename === undefined
|
|
27
|
+
? findManifestFile(repoRoot)
|
|
28
|
+
: path.resolve(repoRoot, configFilename);
|
|
29
|
+
const read = await readManifestFile(configPath);
|
|
30
|
+
// The `fn` terms are imported here, before the policy loads, as the CLI
|
|
31
|
+
// does; the pack is composed with ast-grep for the campaigns family's
|
|
32
|
+
// `syntax` term, and the plugin parses with it for that family only.
|
|
33
|
+
const { functions, manifest } = await loadCampaignFunctions(configPath, read.manifest);
|
|
13
34
|
const loaded = loadPolicy({
|
|
14
35
|
repoRoot,
|
|
15
36
|
configPath,
|
|
16
|
-
manifest
|
|
17
|
-
|
|
37
|
+
manifest,
|
|
38
|
+
locate: read.locate,
|
|
39
|
+
languages: [typescriptLanguage({ syntax: astGrepMatcher() })],
|
|
18
40
|
fileSystem: makeFileSystemLive(repoRoot),
|
|
41
|
+
functions,
|
|
42
|
+
// A `report` command runs once per process — once per editor session
|
|
43
|
+
// for oxlint's language server, which then sees that report until it
|
|
44
|
+
// restarts. A report the build writes to a file is the predictable form.
|
|
45
|
+
reports: makeReportSourceLive(repoRoot),
|
|
46
|
+
now: hostNow(),
|
|
19
47
|
});
|
|
20
48
|
if (Result.isFailure(loaded))
|
|
21
49
|
throw loaded.failure;
|
|
22
|
-
|
|
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 keeps the answer,
|
|
56
|
+
// and a failure, so the rules read what was read here — a term's several
|
|
57
|
+
// commands run at once. A report that cannot be read is not a load
|
|
58
|
+
// failure — the campaigns rule reports it once, on the first file a
|
|
59
|
+
// campaign naming it selects; one that does not parse is.
|
|
60
|
+
await Promise.all(reportSpecsOf(policy.campaignRules).map(async (spec) => {
|
|
61
|
+
if (policy.reports.read !== undefined)
|
|
62
|
+
return policy.reports.read(spec);
|
|
63
|
+
try {
|
|
64
|
+
policy.reports.diagnosticsOf(spec, "");
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
if (!(cause instanceof ReportUnavailable))
|
|
68
|
+
throw cause;
|
|
69
|
+
}
|
|
70
|
+
}));
|
|
71
|
+
return policy;
|
|
23
72
|
};
|
|
24
73
|
//# 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,EACL,
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,49 +1,6 @@
|
|
|
1
1
|
import { evaluateSelectedBindings, exportRulesSelecting, formatMessage, } from "@goodbones/core";
|
|
2
2
|
import * as Result from "effect/Result";
|
|
3
|
-
import {
|
|
4
|
-
const nameOf = (node) => {
|
|
5
|
-
if (node === null || node === undefined)
|
|
6
|
-
return null;
|
|
7
|
-
if (typeof node.name === "string")
|
|
8
|
-
return node.name;
|
|
9
|
-
// `import { "a-b" as ab }` — a string-literal export name.
|
|
10
|
-
return typeof node.value === "string" ? node.value : null;
|
|
11
|
-
};
|
|
12
|
-
// The whole module, as one binding. `export * from "m"`, `export * as ns from
|
|
13
|
-
// "m"`, `import x = require("m")`, `import("m")` and `require("m")` all carry
|
|
14
|
-
// every export of `m` at once, exactly as `import * as ns` does — and are the
|
|
15
|
-
// same way around a rule about a name. A side-effect import carries nothing.
|
|
16
|
-
const wholeModule = (node, local = "") => ({
|
|
17
|
-
symbol: "*",
|
|
18
|
-
kind: "namespace",
|
|
19
|
-
node,
|
|
20
|
-
local,
|
|
21
|
-
});
|
|
22
|
-
const boundOf = (specifier) => {
|
|
23
|
-
const local = nameOf(specifier.local) ?? "";
|
|
24
|
-
switch (specifier.type) {
|
|
25
|
-
case "ImportSpecifier": {
|
|
26
|
-
const symbol = nameOf(specifier.imported);
|
|
27
|
-
return symbol === null ? null : { symbol, kind: "named", node: specifier, local };
|
|
28
|
-
}
|
|
29
|
-
case "ImportDefaultSpecifier":
|
|
30
|
-
return { symbol: "default", kind: "default", node: specifier, local };
|
|
31
|
-
case "ImportNamespaceSpecifier":
|
|
32
|
-
return { symbol: "*", kind: "namespace", node: specifier, local };
|
|
33
|
-
// `export { a } from "…"` — `local` is the name in the source module.
|
|
34
|
-
case "ExportSpecifier": {
|
|
35
|
-
const symbol = nameOf(specifier.local);
|
|
36
|
-
return symbol === null ? null : { symbol, kind: "named", node: specifier, local: symbol };
|
|
37
|
-
}
|
|
38
|
-
default:
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
const specifierOf = (node) => {
|
|
43
|
-
const value = node.source?.value;
|
|
44
|
-
return typeof value === "string" ? value : null;
|
|
45
|
-
};
|
|
46
|
-
const boundSpecifiers = (node) => (node.specifiers ?? []).map(boundOf).filter((one) => one !== null);
|
|
3
|
+
import { at, factsOfProgram, toRepoRelative, } from "./oxlint-api.js";
|
|
47
4
|
// `import { A, B as C } from "pkg"` becomes `import * as A from "pkg/A"` and
|
|
48
5
|
// `import * as C from "pkg/B"`. Only whole-declaration rewrites are offered: a
|
|
49
6
|
// declaration mixing restricted named imports with a default or namespace one
|
|
@@ -64,15 +21,16 @@ export const makeExportsRule = (policy) => ({
|
|
|
64
21
|
createOnce(context) {
|
|
65
22
|
let importer = "";
|
|
66
23
|
let selected = [];
|
|
67
|
-
// `
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
const check = (
|
|
71
|
-
|
|
24
|
+
// Only an `import` declaration can be rewritten into subpath namespace
|
|
25
|
+
// imports. `export *` and the whole-module forms are reported and left
|
|
26
|
+
// for the author.
|
|
27
|
+
const check = (edge) => {
|
|
28
|
+
const { bindings: bound, node, specifier } = edge;
|
|
29
|
+
if (bound.length === 0)
|
|
72
30
|
return;
|
|
73
31
|
const outcome = evaluateSelectedBindings(selected, policy.resolver, {
|
|
74
32
|
importer,
|
|
75
|
-
specifier
|
|
33
|
+
specifier,
|
|
76
34
|
bindings: bound,
|
|
77
35
|
});
|
|
78
36
|
if (Result.isFailure(outcome)) {
|
|
@@ -87,20 +45,20 @@ export const makeExportsRule = (policy) => ({
|
|
|
87
45
|
// The rewrite is `import * as X from "pkg/<name>"`, which only means
|
|
88
46
|
// something for a named binding — a namespace one has no name to put
|
|
89
47
|
// in the subpath.
|
|
90
|
-
const rewritable =
|
|
48
|
+
const rewritable = edge.form === "import" &&
|
|
91
49
|
rule.fix === "subpath-namespace-import" &&
|
|
92
50
|
offending.length === bound.length &&
|
|
93
51
|
offending.every((one) => one.kind === "named");
|
|
94
52
|
if (rewritable) {
|
|
95
53
|
context.report({
|
|
96
|
-
node,
|
|
54
|
+
node: at(node),
|
|
97
55
|
message: formatMessage(violation),
|
|
98
|
-
fix: (fixer) => fixer.replaceText(node, subpathNamespaceImport(
|
|
56
|
+
fix: (fixer) => fixer.replaceText(at(node), subpathNamespaceImport(specifier, offending)),
|
|
99
57
|
});
|
|
100
58
|
continue;
|
|
101
59
|
}
|
|
102
60
|
context.report({
|
|
103
|
-
node: offending[0]?.node ?? node,
|
|
61
|
+
node: at(offending[0]?.node ?? node),
|
|
104
62
|
message: formatMessage(violation),
|
|
105
63
|
});
|
|
106
64
|
}
|
|
@@ -113,23 +71,9 @@ export const makeExportsRule = (policy) => ({
|
|
|
113
71
|
selected = exportRulesSelecting(policy.exportRules, importer);
|
|
114
72
|
return selected.length > 0;
|
|
115
73
|
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
ExportNamedDeclaration(node) {
|
|
120
|
-
check(node, specifierOf(node), boundSpecifiers(node), false);
|
|
121
|
-
},
|
|
122
|
-
ExportAllDeclaration(node) {
|
|
123
|
-
check(node, specifierOf(node), [wholeModule(node, nameOf(node.exported) ?? "")], false);
|
|
124
|
-
},
|
|
125
|
-
ImportExpression(node) {
|
|
126
|
-
check(node, importExpressionSpecifierOf(node), [wholeModule(node)], false);
|
|
127
|
-
},
|
|
128
|
-
CallExpression(node) {
|
|
129
|
-
check(node, requireSpecifierOf(node), [wholeModule(node)], false);
|
|
130
|
-
},
|
|
131
|
-
TSImportEqualsDeclaration(node) {
|
|
132
|
-
check(node, importEqualsSpecifierOf(node), [wholeModule(node)], false);
|
|
74
|
+
Program(node) {
|
|
75
|
+
for (const edge of factsOfProgram(importer, node).edges)
|
|
76
|
+
check(edge);
|
|
133
77
|
},
|
|
134
78
|
};
|
|
135
79
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exports-rule.js","sourceRoot":"","sources":["../../src/exports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"exports-rule.js","sourceRoot":"","sources":["../../src/exports-rule.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { evaluateSelectedEdge, formatMessage, rulesSelecting, } from "@goodbones/core";
|
|
2
2
|
import * as Result from "effect/Result";
|
|
3
|
-
import {
|
|
3
|
+
import { at, factsOfProgram, toRepoRelative, } from "./oxlint-api.js";
|
|
4
4
|
const unresolvedMessage = (specifier, detail) => `[unresolved-import] "${specifier}" could not be resolved, so every import rule about it ` +
|
|
5
5
|
`enforces nothing. Fix the resolve scope in the architecture config, or list the specifier ` +
|
|
6
6
|
`in resolve.ignoreUnresolved. (${detail})`;
|
|
@@ -18,31 +18,29 @@ export const makeImportsRule = (policy) => ({
|
|
|
18
18
|
createOnce(context) {
|
|
19
19
|
let importer = "";
|
|
20
20
|
let selected = [];
|
|
21
|
-
const check = (
|
|
22
|
-
|
|
23
|
-
return;
|
|
21
|
+
const check = (edge) => {
|
|
22
|
+
const { specifier } = edge;
|
|
24
23
|
const outcome = evaluateSelectedEdge(selected, policy.resolver, { importer, specifier });
|
|
25
24
|
if (Result.isFailure(outcome)) {
|
|
26
25
|
if (policy.config.resolve.unresolved === "off")
|
|
27
26
|
return;
|
|
28
27
|
if (policy.ignoreUnresolved.some((pattern) => pattern.test(specifier)))
|
|
29
28
|
return;
|
|
30
|
-
context.report({
|
|
29
|
+
context.report({
|
|
30
|
+
node: at(edge.node),
|
|
31
|
+
message: unresolvedMessage(specifier, outcome.failure.detail),
|
|
32
|
+
});
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
33
35
|
for (const violation of outcome.success) {
|
|
34
36
|
if (policy.baseline.isBaselined(violation))
|
|
35
37
|
continue;
|
|
36
|
-
context.report({ node, message: formatMessage(violation) });
|
|
38
|
+
context.report({ node: at(edge.node), message: formatMessage(violation) });
|
|
37
39
|
}
|
|
38
40
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Every form that names a module is an edge. The CLI adapter reads the same
|
|
43
|
-
// five out of TypeScript's tree, and the parity suite holds the two to it: a
|
|
44
|
-
// `require` the plugin skipped would be a rule that enforces nothing under
|
|
45
|
-
// `oxlint` while failing under `architecture check`.
|
|
41
|
+
// Every form that names a module is an edge, and the pack's reader is
|
|
42
|
+
// what says which forms those are — the same reader the CLI reads a file
|
|
43
|
+
// through, so a form one host sees the other sees too.
|
|
46
44
|
return {
|
|
47
45
|
before() {
|
|
48
46
|
importer = toRepoRelative(policy.repoRoot, context.filename);
|
|
@@ -51,19 +49,9 @@ export const makeImportsRule = (policy) => ({
|
|
|
51
49
|
selected = rulesSelecting(policy.importRules, importer);
|
|
52
50
|
return selected.length > 0;
|
|
53
51
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// `import("m")` with a literal argument. A computed one is not a fact a
|
|
58
|
-
// static policy can speak about, in either adapter.
|
|
59
|
-
ImportExpression(node) {
|
|
60
|
-
check(node, importExpressionSpecifierOf(node));
|
|
61
|
-
},
|
|
62
|
-
CallExpression(node) {
|
|
63
|
-
check(node, requireSpecifierOf(node));
|
|
64
|
-
},
|
|
65
|
-
TSImportEqualsDeclaration(node) {
|
|
66
|
-
check(node, importEqualsSpecifierOf(node));
|
|
52
|
+
Program(node) {
|
|
53
|
+
for (const edge of factsOfProgram(importer, node).edges)
|
|
54
|
+
check(edge);
|
|
67
55
|
},
|
|
68
56
|
};
|
|
69
57
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imports-rule.js","sourceRoot":"","sources":["../../src/imports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,aAAa,EAEb,cAAc,GAEf,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"imports-rule.js","sourceRoot":"","sources":["../../src/imports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,aAAa,EAEb,cAAc,GAEf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EACL,EAAE,EACF,cAAc,EAId,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,MAAM,iBAAiB,GAAG,CAAC,SAAiB,EAAE,MAAc,EAAU,EAAE,CACtE,wBAAwB,SAAS,yDAAyD;IAC1F,4FAA4F;IAC5F,iCAAiC,MAAM,GAAG,CAAC;AAE7C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAoB,EAAc,EAAE,CAAC,CAAC;IACpE,IAAI,EAAE;QACJ,IAAI,EAAE,SAAkB;QACxB,IAAI,EAAE;YACJ,WAAW,EACT,6FAA6F;SAChG;QACD,MAAM,EAAE,EAAE;KACX;IAED,yEAAyE;IACzE,4EAA4E;IAC5E,4EAA4E;IAC5E,UAAU,CAAC,OAAoB;QAC7B,IAAI,QAAQ,GAAG,EAAE,CAAC;QAClB,IAAI,QAAQ,GAAgC,EAAE,CAAC;QAE/C,MAAM,KAAK,GAAG,CAAC,IAAc,EAAQ,EAAE;YACrC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;YAC3B,MAAM,OAAO,GAAG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;YAEzF,IAAI,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC9B,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,KAAK,KAAK;oBAAE,OAAO;gBACvD,IAAI,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBAAE,OAAO;gBAC/E,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;iBAC9D,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACxC,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC;oBAAE,SAAS;gBACrD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC,CAAC;QAEF,sEAAsE;QACtE,yEAAyE;QACzE,uDAAuD;QACvD,OAAO;YACL,MAAM;gBACJ,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC7D,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,OAAO,KAAK,CAAC;gBAC5C,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;gBACxD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAC7B,CAAC;YACD,OAAO,CAAC,IAAa;gBACnB,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,KAAK;oBAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACvE,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|