@goodbones/oxlint 0.1.0-beta.6 → 0.1.0-beta.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"members-rule.js","sourceRoot":"","sources":["../../src/members-rule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"members-rule.js","sourceRoot":"","sources":["../../src/members-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kBAAkB,EAClB,aAAa,EAEb,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,EAAE,EACF,cAAc,EAId,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,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,UAAU,CAAC,OAAoB;QAC7B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,QAAQ,GAAsC,EAAE,CAAC;QAErD,2EAA2E;QAC3E,+CAA+C;QAC/C,OAAO;YACL,MAAM;gBACJ,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACzD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,OAAO,KAAK,CAAC;gBACxC,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;gBAC1D,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAC7B,CAAC;YACD,OAAO,CAAC,IAAa;gBACnB,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;oBAC1D,KAAK,MAAM,SAAS,IAAI,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;wBAC3D,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC;4BAAE,SAAS;wBACrD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;oBAC7E,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -1,24 +1,26 @@
1
1
  import * as path from "node:path";
2
+ import { readProgram, } from "@goodbones/typescript";
2
3
  export const toRepoRelative = (repoRoot, filename) => path.relative(repoRoot, filename).replaceAll(path.sep, "/");
3
- export const specifierOf = (node) => {
4
- const value = node.source?.value;
5
- return typeof value === "string" ? value : null;
6
- };
7
- export const requireSpecifierOf = (node) => {
8
- if (node.callee?.type !== "Identifier" || node.callee.name !== "require")
9
- return null;
10
- const [first] = node.arguments ?? [];
11
- return first?.type === "Literal" && typeof first.value === "string" ? first.value : null;
12
- };
13
- export const importExpressionSpecifierOf = (node) => {
14
- const source = node.source;
15
- return source?.type === "Literal" && typeof source.value === "string" ? source.value : null;
16
- };
17
- export const importEqualsSpecifierOf = (node) => {
18
- const reference = node.moduleReference;
19
- if (reference?.type !== "TSExternalModuleReference")
20
- return null;
21
- const value = reference.expression?.value;
22
- return typeof value === "string" ? value : null;
4
+ // A diagnostic is placed by the node's range, which every node of oxlint's
5
+ // tree carries; a node from another parse carries `start` and `end`.
6
+ export const at = (node) => ({
7
+ range: node.range ?? [node.start, node.end],
8
+ });
9
+ // The facts of one file, read once and shared by every rule that runs on it:
10
+ // oxlint deserialises a file's tree once and hands the same `Program` to each
11
+ // rule, so the tree is the key.
12
+ const read = new WeakMap();
13
+ export const factsOfProgram = (file, program) => {
14
+ const found = read.get(program);
15
+ if (found !== undefined)
16
+ return found;
17
+ // oxlint's node types are `@oxc-project/types`' with `parent` required on
18
+ // every node — the same tree, generated from the same source. Each node
19
+ // type assigns on its own; the compiler gives up relating the two
20
+ // ~190-member recursive unions as wholes, so the boundary is asserted once,
21
+ // here. The parity suite is what proves the trees are one.
22
+ const facts = readProgram(file, program);
23
+ read.set(program, facts);
24
+ return facts;
23
25
  };
24
26
  //# sourceMappingURL=oxlint-api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"oxlint-api.js","sourceRoot":"","sources":["../../src/oxlint-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAkBlC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAE,QAAgB,EAAU,EAAE,CAC3E,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAE9D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAgB,EAAiB,EAAE;IAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC,CAAC;AAyBF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAc,EAAiB,EAAE;IAClE,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtF,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;IACrC,OAAO,KAAK,EAAE,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3F,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,IAA0B,EAAiB,EAAE;IACvF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,OAAO,MAAM,EAAE,IAAI,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9F,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,IAAsB,EAAiB,EAAE;IAC/E,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC;IACvC,IAAI,SAAS,EAAE,IAAI,KAAK,2BAA2B;QAAE,OAAO,IAAI,CAAC;IACjE,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC,CAAC"}
1
+ {"version":3,"file":"oxlint-api.js","sourceRoot":"","sources":["../../src/oxlint-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAGL,WAAW,GAEZ,MAAM,uBAAuB,CAAC;AAiB/B,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAE,QAAgB,EAAU,EAAE,CAC3E,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAE9D,2EAA2E;AAC3E,qEAAqE;AACrE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAgB,EAAkB,EAAE,CAAC,CAAC;IACvD,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC;CAC5C,CAAC,CAAC;AAEH,6EAA6E;AAC7E,8EAA8E;AAC9E,gCAAgC;AAChC,MAAM,IAAI,GAAG,IAAI,OAAO,EAAsB,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,OAAgB,EAAa,EAAE;IAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,0EAA0E;IAC1E,wEAAwE;IACxE,kEAAkE;IAClE,4EAA4E;IAC5E,2DAA2D;IAC3D,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,OAAsB,CAAC,CAAC;IACxD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzB,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { makeCampaignsRule } from "./campaigns-rule.js";
1
2
  import { loadPolicyFromFile } from "./config-loader.js";
2
3
  import { makeExportsRule } from "./exports-rule.js";
3
4
  import { makeImportsRule } from "./imports-rule.js";
@@ -21,6 +22,7 @@ export const rules = {
21
22
  members: makeMembersRule(policy),
22
23
  structure: makeStructureRule(policy),
23
24
  surface: makeSurfaceRule(policy),
25
+ campaigns: makeCampaignsRule(policy),
24
26
  };
25
27
  const plugin = {
26
28
  meta: { name: "architecture" },
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,+EAA+E;AAC/E,YAAY;AACZ,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,EAAE,EAC9C,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAChC,CAAC;AAEF,6EAA6E;AAC7E,mCAAmC;AACnC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;IACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,MAAM,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAMd;IACF,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;IAChC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;IAChC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;IAChC,SAAS,EAAE,iBAAiB,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,GAA+E;IACzF,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;IAC9B,KAAK;CACN,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,+EAA+E;AAC/E,YAAY;AACZ,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,EAAE,EAC9C,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAChC,CAAC;AAEF,6EAA6E;AAC7E,mCAAmC;AACnC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;IACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,MAAM,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAOd;IACF,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;IAChC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;IAChC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;IAChC,SAAS,EAAE,iBAAiB,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC;IAChC,SAAS,EAAE,iBAAiB,CAAC,MAAM,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,GAA+E;IACzF,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;IAC9B,KAAK;CACN,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,122 +1,5 @@
1
1
  import { evaluateSurface, formatMessage, surfaceRulesSelecting, } from "@goodbones/core";
2
- import { toRepoRelative, } from "./oxlint-api.js";
3
- const isStatementList = (value) => Array.isArray(value);
4
- const nameOf = (node) => {
5
- if (node === null || node === undefined)
6
- return null;
7
- if (typeof node.name === "string")
8
- return node.name;
9
- return typeof node.value === "string" ? node.value : null;
10
- };
11
- // The identifiers a binding pattern introduces.
12
- const patternNames = (pattern) => {
13
- if (pattern === null || pattern === undefined)
14
- return [];
15
- switch (pattern.type) {
16
- case "Identifier":
17
- return typeof pattern.name === "string" ? [pattern.name] : [];
18
- case "ObjectPattern":
19
- return (pattern.properties ?? []).flatMap((property) => patternNames(property.value ?? property.argument));
20
- case "ArrayPattern":
21
- return (pattern.elements ?? []).flatMap(patternNames);
22
- case "AssignmentPattern":
23
- return patternNames(pattern.left);
24
- case "RestElement":
25
- return patternNames(pattern.argument);
26
- default:
27
- return [];
28
- }
29
- };
30
- // The names a declaration statement introduces, with what it declares them as.
31
- const declaredNamesOf = (statement) => {
32
- const named = (declares) => {
33
- const name = nameOf(statement.id);
34
- return name === null ? [] : [[name, declares]];
35
- };
36
- switch (statement.type) {
37
- case "FunctionDeclaration":
38
- return named("function");
39
- case "ClassDeclaration":
40
- return named("class");
41
- case "VariableDeclaration":
42
- return (statement.declarations ?? []).flatMap((declaration) => patternNames(declaration.id).map((name) => [name, "variable"]));
43
- case "TSTypeAliasDeclaration":
44
- return named("type");
45
- case "TSInterfaceDeclaration":
46
- return named("interface");
47
- case "TSEnumDeclaration":
48
- return named("enum");
49
- case "TSModuleDeclaration":
50
- return named("other");
51
- default:
52
- return [];
53
- }
54
- };
55
- // A file's surface, read off the program body rather than by visiting export
56
- // nodes: an `export` inside a namespace body is that namespace's, not the
57
- // module's, and reading the top level directly is what says so without a
58
- // parent pointer. Mirrors the CLI's `exportSitesOf`.
59
- const surfaceOf = (file, body) => {
60
- const locals = new Map();
61
- for (const statement of body) {
62
- const declaration = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration"
63
- ? statement.declaration
64
- : statement;
65
- if (declaration === null || declaration === undefined)
66
- continue;
67
- for (const [name, declares] of declaredNamesOf(declaration))
68
- locals.set(name, declares);
69
- }
70
- const found = [];
71
- const site = (node, name, kind, declares, reexport) => {
72
- found.push({ node, site: { file, name, kind, declares, reexport } });
73
- };
74
- for (const statement of body) {
75
- switch (statement.type) {
76
- case "ExportNamedDeclaration": {
77
- const declaration = statement.declaration;
78
- if (declaration !== null && declaration !== undefined) {
79
- for (const [name, declares] of declaredNamesOf(declaration)) {
80
- site(declaration, name, "named", declares, false);
81
- }
82
- break;
83
- }
84
- const reexport = typeof statement.source?.value === "string";
85
- for (const specifier of statement.specifiers ?? []) {
86
- const name = nameOf(specifier.exported);
87
- if (name === null)
88
- continue;
89
- const local = nameOf(specifier.local) ?? name;
90
- const declares = reexport ? "other" : (locals.get(local) ?? "other");
91
- site(specifier, name, name === "default" ? "default" : "named", declares, reexport);
92
- }
93
- break;
94
- }
95
- case "ExportDefaultDeclaration": {
96
- const declaration = statement.declaration;
97
- const declares = declaration === null || declaration === undefined
98
- ? "expression"
99
- : declaration.type === "FunctionDeclaration"
100
- ? "function"
101
- : declaration.type === "ClassDeclaration"
102
- ? "class"
103
- : declaration.type === "TSInterfaceDeclaration"
104
- ? "interface"
105
- : declaration.type === "Identifier"
106
- ? (locals.get(nameOf(declaration) ?? "") ?? "expression")
107
- : "expression";
108
- site(statement, "default", "default", declares, false);
109
- break;
110
- }
111
- case "ExportAllDeclaration":
112
- site(statement, nameOf(statement.exported) ?? "*", "namespace", "other", true);
113
- break;
114
- default:
115
- break;
116
- }
117
- }
118
- return found;
119
- };
2
+ import { at, factsOfProgram, toRepoRelative, } from "./oxlint-api.js";
120
3
  export const makeSurfaceRule = (policy) => ({
121
4
  meta: {
122
5
  type: "problem",
@@ -137,22 +20,23 @@ export const makeSurfaceRule = (policy) => ({
137
20
  return selected.length > 0;
138
21
  },
139
22
  Program(node) {
140
- if (!isStatementList(node.body))
141
- return;
142
- const found = surfaceOf(file, node.body);
143
- const violations = evaluateSurface(selected, file, found.map((one) => one.site));
23
+ const sites = factsOfProgram(file, node).exportSites;
24
+ const violations = evaluateSurface(selected, file, sites);
144
25
  // A per-site violation lands on the site's own node; a `count` one, on
145
26
  // the program — it is about the file.
146
27
  const used = new Set();
147
28
  for (const violation of violations) {
148
29
  if (policy.baseline.isBaselined(violation))
149
30
  continue;
150
- const at = violation.subject === null
31
+ const site = violation.subject === null
151
32
  ? undefined
152
- : found.find((one) => one.site.name === violation.subject && !used.has(one));
153
- if (at !== undefined)
154
- used.add(at);
155
- context.report({ node: at?.node ?? node, message: formatMessage(violation) });
33
+ : sites.find((one) => one.name === violation.subject && !used.has(one));
34
+ if (site !== undefined)
35
+ used.add(site);
36
+ context.report({
37
+ node: site === undefined ? node : at(site.node),
38
+ message: formatMessage(violation),
39
+ });
156
40
  }
157
41
  },
158
42
  };
@@ -1 +1 @@
1
- {"version":3,"file":"surface-rule.js","sourceRoot":"","sources":["../../src/surface-rule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"surface-rule.js","sourceRoot":"","sources":["../../src/surface-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EACf,aAAa,EAEb,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,EAAE,EACF,cAAc,EAId,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAoB,EAAc,EAAE,CAAC,CAAC;IACpE,IAAI,EAAE;QACJ,IAAI,EAAE,SAAkB;QACxB,IAAI,EAAE;YACJ,WAAW,EACT,iHAAiH;SACpH;QACD,MAAM,EAAE,EAAE;KACX;IAED,UAAU,CAAC,OAAoB;QAC7B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,QAAQ,GAAuC,EAAE,CAAC;QAEtD,OAAO;YACL,MAAM;gBACJ,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACzD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,OAAO,KAAK,CAAC;gBACxC,QAAQ,GAAG,qBAAqB,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;gBAC5D,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAC7B,CAAC;YAED,OAAO,CAAC,IAAa;gBACnB,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,WAAW,CAAC;gBACrD,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;gBAE1D,uEAAuE;gBACvE,sCAAsC;gBACtC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;gBACvC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;oBACnC,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC;wBAAE,SAAS;oBACrD,MAAM,IAAI,GACR,SAAS,CAAC,OAAO,KAAK,IAAI;wBACxB,CAAC,CAAC,SAAS;wBACX,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC5E,IAAI,IAAI,KAAK,SAAS;wBAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACvC,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;wBAC/C,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC;qBAClC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodbones/oxlint",
3
- "version": "0.1.0-beta.6",
3
+ "version": "0.1.0-beta.8",
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,8 +44,9 @@
44
44
  ],
45
45
  "dependencies": {
46
46
  "effect": "4.0.0-beta.94",
47
- "@goodbones/core": "0.1.0-beta.6",
48
- "@goodbones/typescript": "0.1.0-beta.6"
47
+ "@goodbones/ast-grep": "0.1.0",
48
+ "@goodbones/typescript": "0.1.0-beta.8",
49
+ "@goodbones/core": "0.1.0-beta.8"
49
50
  },
50
51
  "peerDependencies": {
51
52
  "oxlint": ">=1.77.0"
@@ -0,0 +1,98 @@
1
+ import {
2
+ type CampaignHit,
3
+ campaignsSelecting,
4
+ type CompiledCampaign,
5
+ evaluateCampaigns,
6
+ formatMessage,
7
+ leafTermsOf,
8
+ type LoadedPolicy,
9
+ reconcile,
10
+ } from "@goodbones/core";
11
+ import { sourceFactsOf } from "@goodbones/typescript";
12
+
13
+ import {
14
+ factsOfProgram,
15
+ type OxlintRule,
16
+ type Program,
17
+ type RuleContext,
18
+ toRepoRelative,
19
+ } from "./oxlint-api.js";
20
+
21
+ // The campaigns family, one file at a time: every hit of a campaign selecting
22
+ // the file that its ledger does not carry, reported at the match's position
23
+ // with the campaign's `how` as the message. The facts come from oxlint's tree
24
+ // through the pack's reader, as the other rules read them; the `syntax` term
25
+ // is answered by the same matcher the CLI uses, over `sourceCode.text` —
26
+ // "one engine" is this family's parity contract, rather than a corpus.
27
+
28
+ // Whether a hit is carried by the ledger, judged against the file's hits
29
+ // alone: entries are per file, so a file's reconciliation is the whole
30
+ // campaign's restricted to it.
31
+ const unledgered = (
32
+ policy: LoadedPolicy,
33
+ selected: ReadonlyArray<CompiledCampaign>,
34
+ hits: ReadonlyArray<CampaignHit>,
35
+ ): ReadonlyArray<CampaignHit> => {
36
+ const carried = new Set<CampaignHit["violation"]>();
37
+ for (const rule of selected) {
38
+ const ledger = policy.ledgers.get(rule.id);
39
+ if (ledger === undefined) continue;
40
+ const own = hits.filter((hit) => hit.campaign === rule.id).map((hit) => hit.violation);
41
+ for (const one of reconcile(ledger, own, rule.unit).ledgered) carried.add(one);
42
+ }
43
+ return hits.filter((hit) => !carried.has(hit.violation));
44
+ };
45
+
46
+ export const makeCampaignsRule = (policy: LoadedPolicy): OxlintRule => ({
47
+ meta: {
48
+ type: "problem" as const,
49
+ docs: {
50
+ description:
51
+ "the campaigns: every place a pattern the repository is migrating away from still occurs, that its ledger does not carry",
52
+ },
53
+ schema: [],
54
+ },
55
+
56
+ createOnce(context: RuleContext) {
57
+ let file = "";
58
+ let selected: ReadonlyArray<CompiledCampaign> = [];
59
+
60
+ return {
61
+ before() {
62
+ file = toRepoRelative(policy.repoRoot, context.filename);
63
+ if (file.startsWith("..")) return false;
64
+ selected = campaignsSelecting(policy.campaignRules, file);
65
+ return selected.length > 0;
66
+ },
67
+
68
+ Program(node: Program) {
69
+ const text = context.sourceCode.text;
70
+ const needsSyntax = selected.some((rule) =>
71
+ leafTermsOf(rule.detect).some(
72
+ (leaf) => leaf === "syntax" || leaf === "report" || leaf === "fn",
73
+ ),
74
+ );
75
+ const hits = evaluateCampaigns(selected, {
76
+ file,
77
+ text,
78
+ facts: sourceFactsOf(factsOfProgram(file, node)),
79
+ resolver: policy.resolver,
80
+ fileSystem: policy.fileSystem,
81
+ syntax: needsSyntax ? policy.syntax.parse(file, text) : null,
82
+ functions: policy.functions,
83
+ reports: policy.reports,
84
+ });
85
+
86
+ for (const hit of unledgered(policy, selected, hits)) {
87
+ // A match lands where it was found; a file-unit hit, on line 1, as
88
+ // the structure rule places a finding about the file itself.
89
+ const at = hit.range ?? { start: { line: 0, column: 0 } };
90
+ context.report({
91
+ message: formatMessage(hit.violation),
92
+ loc: { line: at.start.line + 1, column: at.start.column },
93
+ });
94
+ }
95
+ },
96
+ };
97
+ },
98
+ });
@@ -1,10 +1,13 @@
1
1
  import * as path from "node:path";
2
2
 
3
+ import { astGrepMatcher } from "@goodbones/ast-grep";
3
4
  import {
4
5
  findManifestFile,
6
+ loadCampaignFunctions,
5
7
  type LoadedPolicy,
6
8
  loadPolicy,
7
9
  makeFileSystemLive,
10
+ makeReportSourceLive,
8
11
  readManifestFile,
9
12
  } from "@goodbones/core";
10
13
  import { typescriptLanguage } from "@goodbones/typescript";
@@ -12,6 +15,18 @@ import * as Result from "effect/Result";
12
15
 
13
16
  export type { LoadedPolicy } from "@goodbones/core";
14
17
 
18
+ // The clock the campaigns are judged by; `ARCHITECTURE_NOW` pins it, as it
19
+ // does for the CLI.
20
+ const hostNow = (): number => {
21
+ const pinned = process.env.ARCHITECTURE_NOW;
22
+ if (pinned === undefined || pinned === "") return Date.now();
23
+ const parsed = /^\d+$/.test(pinned) ? Number(pinned) : Date.parse(pinned);
24
+ if (Number.isNaN(parsed)) {
25
+ throw new Error(`ARCHITECTURE_NOW is ${JSON.stringify(pinned)}, which is not a date.`);
26
+ }
27
+ return parsed;
28
+ };
29
+
15
30
  // The plugin's composition root: read the manifest file, construct the
16
31
  // language packs and the live file system, and hand them to the loader. A load
17
32
  // failure throws out of here and out of oxlint's import of the plugin — a
@@ -28,13 +43,23 @@ export const loadPolicyFromFile = async (
28
43
  ? findManifestFile(repoRoot)
29
44
  : path.resolve(repoRoot, configFilename);
30
45
  const read = await readManifestFile(configPath);
46
+ // The `fn` terms are imported here, before the policy loads, as the CLI
47
+ // does; the pack is composed with ast-grep for the campaigns family's
48
+ // `syntax` term, and the plugin parses with it for that family only.
49
+ const { functions, manifest } = await loadCampaignFunctions(configPath, read.manifest);
31
50
  const loaded = loadPolicy({
32
51
  repoRoot,
33
52
  configPath,
34
- manifest: read.manifest,
53
+ manifest,
35
54
  locate: read.locate,
36
- languages: [typescriptLanguage()],
55
+ languages: [typescriptLanguage({ syntax: astGrepMatcher() })],
37
56
  fileSystem: makeFileSystemLive(repoRoot),
57
+ functions,
58
+ // A `report` command runs once per process — once per editor session
59
+ // for oxlint's language server, which then sees that report until it
60
+ // restarts. A report the build writes to a file is the predictable form.
61
+ reports: makeReportSourceLive(repoRoot),
62
+ now: hostNow(),
38
63
  });
39
64
  if (Result.isFailure(loaded)) throw loaded.failure;
40
65
  return loaded.success;
@@ -1,97 +1,29 @@
1
1
  import {
2
- type Binding,
3
2
  evaluateSelectedBindings,
4
3
  exportRulesSelecting,
5
4
  formatMessage,
6
5
  type LoadedPolicy,
7
6
  type SelectedExportRule,
8
7
  } from "@goodbones/core";
8
+ import type { ReadBinding, ReadEdge } from "@goodbones/typescript";
9
9
  import * as Result from "effect/Result";
10
10
 
11
11
  import {
12
- type CallNode,
12
+ at,
13
+ factsOfProgram,
13
14
  type Fixer,
14
- type ImportEqualsNode,
15
- importEqualsSpecifierOf,
16
- type ImportExpressionNode,
17
- importExpressionSpecifierOf,
18
15
  type OxlintRule,
19
- type ReportableNode,
20
- requireSpecifierOf,
16
+ type Program,
21
17
  type RuleContext,
22
18
  toRepoRelative,
23
19
  } from "./oxlint-api.js";
24
20
 
25
- type NamedNode = ReportableNode & { readonly name?: unknown; readonly value?: unknown };
26
-
27
- type SpecifierNode = ReportableNode & {
28
- readonly type: string;
29
- readonly imported?: NamedNode | null;
30
- readonly local?: NamedNode | null;
31
- };
32
-
33
- type DeclarationNode = ReportableNode & {
34
- readonly source?: { readonly value?: unknown } | null;
35
- readonly specifiers?: ReadonlyArray<SpecifierNode> | null;
36
- };
37
-
38
- const nameOf = (node: NamedNode | null | undefined): string | null => {
39
- if (node === null || node === undefined) return null;
40
- if (typeof node.name === "string") return node.name;
41
- // `import { "a-b" as ab }` — a string-literal export name.
42
- return typeof node.value === "string" ? node.value : null;
43
- };
44
-
45
- type Bound = Binding & { readonly node: ReportableNode; readonly local: string };
46
-
47
- // The whole module, as one binding. `export * from "m"`, `export * as ns from
48
- // "m"`, `import x = require("m")`, `import("m")` and `require("m")` all carry
49
- // every export of `m` at once, exactly as `import * as ns` does — and are the
50
- // same way around a rule about a name. A side-effect import carries nothing.
51
- const wholeModule = (node: ReportableNode, local = ""): Bound => ({
52
- symbol: "*",
53
- kind: "namespace",
54
- node,
55
- local,
56
- });
57
-
58
- type ExportAllNode = DeclarationNode & { readonly exported?: NamedNode | null };
59
-
60
- const boundOf = (specifier: SpecifierNode): Bound | null => {
61
- const local = nameOf(specifier.local) ?? "";
62
- switch (specifier.type) {
63
- case "ImportSpecifier": {
64
- const symbol = nameOf(specifier.imported);
65
- return symbol === null ? null : { symbol, kind: "named", node: specifier, local };
66
- }
67
- case "ImportDefaultSpecifier":
68
- return { symbol: "default", kind: "default", node: specifier, local };
69
- case "ImportNamespaceSpecifier":
70
- return { symbol: "*", kind: "namespace", node: specifier, local };
71
- // `export { a } from "…"` — `local` is the name in the source module.
72
- case "ExportSpecifier": {
73
- const symbol = nameOf(specifier.local);
74
- return symbol === null ? null : { symbol, kind: "named", node: specifier, local: symbol };
75
- }
76
- default:
77
- return null;
78
- }
79
- };
80
-
81
- const specifierOf = (node: DeclarationNode): string | null => {
82
- const value = node.source?.value;
83
- return typeof value === "string" ? value : null;
84
- };
85
-
86
- const boundSpecifiers = (node: DeclarationNode): ReadonlyArray<Bound> =>
87
- (node.specifiers ?? []).map(boundOf).filter((one): one is Bound => one !== null);
88
-
89
21
  // `import { A, B as C } from "pkg"` becomes `import * as A from "pkg/A"` and
90
22
  // `import * as C from "pkg/B"`. Only whole-declaration rewrites are offered: a
91
23
  // declaration mixing restricted named imports with a default or namespace one
92
24
  // would need comma surgery inside the braces, and a fix that is subtly wrong is
93
25
  // worse than a diagnostic the author resolves by hand.
94
- const subpathNamespaceImport = (specifier: string, bound: ReadonlyArray<Bound>): string =>
26
+ const subpathNamespaceImport = (specifier: string, bound: ReadonlyArray<ReadBinding>): string =>
95
27
  bound
96
28
  .map((binding) => `import * as ${binding.local} from "${specifier}/${binding.symbol}";`)
97
29
  .join("\n");
@@ -111,20 +43,16 @@ export const makeExportsRule = (policy: LoadedPolicy): OxlintRule => ({
111
43
  let importer = "";
112
44
  let selected: ReadonlyArray<SelectedExportRule> = [];
113
45
 
114
- // `fixable` is whether a rewrite could apply: only an `import` declaration
115
- // can be rewritten into subpath namespace imports. `export *` and the
116
- // whole-module forms are reported and left for the author.
117
- const check = (
118
- node: ReportableNode,
119
- specifierValue: string | null,
120
- bound: ReadonlyArray<Bound>,
121
- fixable: boolean,
122
- ): void => {
123
- if (specifierValue === null || bound.length === 0) return;
46
+ // Only an `import` declaration can be rewritten into subpath namespace
47
+ // imports. `export *` and the whole-module forms are reported and left
48
+ // for the author.
49
+ const check = (edge: ReadEdge): void => {
50
+ const { bindings: bound, node, specifier } = edge;
51
+ if (bound.length === 0) return;
124
52
 
125
53
  const outcome = evaluateSelectedBindings(selected, policy.resolver, {
126
54
  importer,
127
- specifier: specifierValue,
55
+ specifier,
128
56
  bindings: bound,
129
57
  });
130
58
 
@@ -136,30 +64,30 @@ export const makeExportsRule = (policy: LoadedPolicy): OxlintRule => ({
136
64
 
137
65
  for (const { bindings, rule, violation } of outcome.success) {
138
66
  if (policy.baseline.isBaselined(violation)) continue;
139
- const offending = bound.filter((one: Bound) =>
67
+ const offending = bound.filter((one) =>
140
68
  bindings.some((binding) => binding.symbol === one.symbol && binding.kind === one.kind),
141
69
  );
142
70
  // The rewrite is `import * as X from "pkg/<name>"`, which only means
143
71
  // something for a named binding — a namespace one has no name to put
144
72
  // in the subpath.
145
73
  const rewritable =
146
- fixable &&
74
+ edge.form === "import" &&
147
75
  rule.fix === "subpath-namespace-import" &&
148
76
  offending.length === bound.length &&
149
77
  offending.every((one) => one.kind === "named");
150
78
 
151
79
  if (rewritable) {
152
80
  context.report({
153
- node,
81
+ node: at(node),
154
82
  message: formatMessage(violation),
155
83
  fix: (fixer: Fixer) =>
156
- fixer.replaceText(node, subpathNamespaceImport(specifierValue, offending)),
84
+ fixer.replaceText(at(node), subpathNamespaceImport(specifier, offending)),
157
85
  });
158
86
  continue;
159
87
  }
160
88
 
161
89
  context.report({
162
- node: offending[0]?.node ?? node,
90
+ node: at(offending[0]?.node ?? node),
163
91
  message: formatMessage(violation),
164
92
  });
165
93
  }
@@ -172,23 +100,8 @@ export const makeExportsRule = (policy: LoadedPolicy): OxlintRule => ({
172
100
  selected = exportRulesSelecting(policy.exportRules, importer);
173
101
  return selected.length > 0;
174
102
  },
175
- ImportDeclaration(node: DeclarationNode) {
176
- check(node, specifierOf(node), boundSpecifiers(node), true);
177
- },
178
- ExportNamedDeclaration(node: DeclarationNode) {
179
- check(node, specifierOf(node), boundSpecifiers(node), false);
180
- },
181
- ExportAllDeclaration(node: ExportAllNode) {
182
- check(node, specifierOf(node), [wholeModule(node, nameOf(node.exported) ?? "")], false);
183
- },
184
- ImportExpression(node: ImportExpressionNode) {
185
- check(node, importExpressionSpecifierOf(node), [wholeModule(node)], false);
186
- },
187
- CallExpression(node: CallNode) {
188
- check(node, requireSpecifierOf(node), [wholeModule(node)], false);
189
- },
190
- TSImportEqualsDeclaration(node: ImportEqualsNode) {
191
- check(node, importEqualsSpecifierOf(node), [wholeModule(node)], false);
103
+ Program(node: Program) {
104
+ for (const edge of factsOfProgram(importer, node).edges) check(edge);
192
105
  },
193
106
  };
194
107
  },