@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.
@@ -0,0 +1,4 @@
1
+ import { type LoadedPolicy } from "@goodbones/core";
2
+ import { type OxlintRule } from "./oxlint-api.js";
3
+ export declare const makeCampaignsRule: (policy: LoadedPolicy) => OxlintRule;
4
+ //# sourceMappingURL=campaigns-rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaigns-rule.d.ts","sourceRoot":"","sources":["../../src/campaigns-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAEL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AA2BzB,eAAO,MAAM,iBAAiB,WAAY,YAAY,KAAG,UAoDvD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,iBAAiB,CAAC;AAIzB,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAOpD,eAAO,MAAM,kBAAkB,aACnB,MAAM,mBACC,MAAM,KACtB,OAAO,CAAC,YAAY,CAkBtB,CAAC"}
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/config-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,YAAY,EAKlB,MAAM,iBAAiB,CAAC;AAIzB,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAmBpD,eAAO,MAAM,kBAAkB,aACnB,MAAM,mBACC,MAAM,KACtB,OAAO,CAAC,YAAY,CA4BtB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"exports-rule.d.ts","sourceRoot":"","sources":["../../src/exports-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAOL,KAAK,UAAU,EAKhB,MAAM,iBAAiB,CAAC;AA4EzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UAgGrD,CAAC"}
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;AAGzB,OAAO,EAML,KAAK,UAAU,EAOhB,MAAM,iBAAiB,CAAC;AAOzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UAkErD,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,EAKL,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AAsGzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UAwFrD,CAAC"}
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 SourceNode = ReportableNode & {
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 specifierOf: (node: SourceNode) => string | null;
18
- export type CallNode = ReportableNode & {
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;AAElE,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACvD,CAAC;AAEF,eAAO,MAAM,cAAc,aAAc,MAAM,YAAY,MAAM,KAAG,MACP,CAAC;AAE9D,eAAO,MAAM,WAAW,SAAU,UAAU,KAAG,MAAM,GAAG,IAGvD,CAAC;AAOF,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5E,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;CAChG,CAAC;AAGF,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CAC/E,CAAC;AAGF,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG;IAC9C,QAAQ,CAAC,eAAe,CAAC,EAAE;QACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,UAAU,CAAC,EAAE;YAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC;KAC3D,GAAG,IAAI,CAAC;CACV,CAAC;AAEF,eAAO,MAAM,kBAAkB,SAAU,QAAQ,KAAG,MAAM,GAAG,IAI5D,CAAC;AAEF,eAAO,MAAM,2BAA2B,SAAU,oBAAoB,KAAG,MAAM,GAAG,IAGjF,CAAC;AAEF,eAAO,MAAM,uBAAuB,SAAU,gBAAgB,KAAG,MAAM,GAAG,IAKzE,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"}
@@ -5,6 +5,7 @@ export declare const rules: {
5
5
  readonly members: OxlintRule;
6
6
  readonly structure: OxlintRule;
7
7
  readonly surface: OxlintRule;
8
+ readonly campaigns: OxlintRule;
8
9
  };
9
10
  declare const plugin: {
10
11
  readonly meta: {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAIA,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;CAO9B,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
+ {"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,EAML,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,iBAAiB,CAAC;AA+KzB,eAAO,MAAM,eAAe,WAAY,YAAY,KAAG,UA8CrD,CAAC"}
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,70 @@
1
+ import { campaignsSelecting, evaluateCampaigns, formatMessage, leafTermsOf, reconcile, } 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
+ export const makeCampaignsRule = (policy) => ({
26
+ meta: {
27
+ type: "problem",
28
+ docs: {
29
+ description: "the campaigns: every place a pattern the repository is migrating away from still occurs, that its ledger does not carry",
30
+ },
31
+ schema: [],
32
+ },
33
+ createOnce(context) {
34
+ let file = "";
35
+ let selected = [];
36
+ return {
37
+ before() {
38
+ file = toRepoRelative(policy.repoRoot, context.filename);
39
+ if (file.startsWith(".."))
40
+ return false;
41
+ selected = campaignsSelecting(policy.campaignRules, file);
42
+ return selected.length > 0;
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
+ }
66
+ },
67
+ };
68
+ },
69
+ });
70
+ //# sourceMappingURL=campaigns-rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaigns-rule.js","sourceRoot":"","sources":["../../src/campaigns-rule.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,20 @@
1
1
  import * as path from "node:path";
2
- import { findManifestFile, loadPolicy, makeFileSystemLive, readManifestFile, } from "@goodbones/core";
2
+ import { astGrepMatcher } from "@goodbones/ast-grep";
3
+ import { findManifestFile, loadCampaignFunctions, loadPolicy, makeFileSystemLive, makeReportSourceLive, readManifestFile, } from "@goodbones/core";
3
4
  import { typescriptLanguage } from "@goodbones/typescript";
4
5
  import * as Result from "effect/Result";
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
+ };
5
18
  // The plugin's composition root: read the manifest file, construct the
6
19
  // language packs and the live file system, and hand them to the loader. A load
7
20
  // failure throws out of here and out of oxlint's import of the plugin — a
@@ -14,13 +27,23 @@ export const loadPolicyFromFile = async (repoRoot, configFilename) => {
14
27
  ? findManifestFile(repoRoot)
15
28
  : path.resolve(repoRoot, configFilename);
16
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);
17
34
  const loaded = loadPolicy({
18
35
  repoRoot,
19
36
  configPath,
20
- manifest: read.manifest,
37
+ manifest,
21
38
  locate: read.locate,
22
- languages: [typescriptLanguage()],
39
+ languages: [typescriptLanguage({ syntax: astGrepMatcher() })],
23
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(),
24
47
  });
25
48
  if (Result.isFailure(loaded))
26
49
  throw loaded.failure;
@@ -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,gBAAgB,EAEhB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,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,MAAM,MAAM,GAAG,UAAU,CAAC;QACxB,QAAQ;QACR,UAAU;QACV,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,CAAC,kBAAkB,EAAE,CAAC;QACjC,UAAU,EAAE,kBAAkB,CAAC,QAAQ,CAAC;KACzC,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAAE,MAAM,MAAM,CAAC,OAAO,CAAC;IACnD,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC,CAAC"}
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,GACjB,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,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC,CAAC"}
@@ -1,49 +1,6 @@
1
1
  import { evaluateSelectedBindings, exportRulesSelecting, formatMessage, } from "@goodbones/core";
2
2
  import * as Result from "effect/Result";
3
- import { importEqualsSpecifierOf, importExpressionSpecifierOf, requireSpecifierOf, toRepoRelative, } from "./oxlint-api.js";
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
- // `fixable` is whether a rewrite could apply: only an `import` declaration
68
- // can be rewritten into subpath namespace imports. `export *` and the
69
- // whole-module forms are reported and left for the author.
70
- const check = (node, specifierValue, bound, fixable) => {
71
- if (specifierValue === null || bound.length === 0)
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: specifierValue,
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 = fixable &&
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(specifierValue, offending)),
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
- ImportDeclaration(node) {
117
- check(node, specifierOf(node), boundSpecifiers(node), true);
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":"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"}
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 { importEqualsSpecifierOf, importExpressionSpecifierOf, requireSpecifierOf, specifierOf, toRepoRelative, } from "./oxlint-api.js";
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 = (node, specifier) => {
22
- if (specifier === null)
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({ node, message: unresolvedMessage(specifier, outcome.failure.detail) });
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
- const checkSource = (node) => {
40
- check(node, specifierOf(node));
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
- ImportDeclaration: checkSource,
55
- ExportNamedDeclaration: checkSource,
56
- ExportAllDeclaration: checkSource,
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":"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"}
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"}
@@ -1,59 +1,5 @@
1
1
  import { evaluateMemberSite, formatMessage, memberRulesSelecting, } from "@goodbones/core";
2
- import { toRepoRelative, } from "./oxlint-api.js";
3
- const isMemberList = (value) => Array.isArray(value);
4
- // The member shapes that carry a name a vocabulary rule can speak about: a
5
- // property or method signature in a type, a property, method or accessor in a
6
- // class. Mirrors the CLI's `isNamedMember`.
7
- const DECLARED_MEMBER_TYPES = new Set([
8
- "TSPropertySignature",
9
- "TSMethodSignature",
10
- "PropertyDefinition",
11
- "MethodDefinition",
12
- "TSAbstractPropertyDefinition",
13
- "TSAbstractMethodDefinition",
14
- ]);
15
- // The type literals written in a type, through intersections, unions and
16
- // parentheses. A reference is not followed: its members are declared where it
17
- // is, and reported there under its own name. Mirrors the CLI's `literalsOf`.
18
- const literalsOf = (node) => {
19
- if (node === null || node === undefined)
20
- return [];
21
- switch (node.type) {
22
- case "TSTypeLiteral":
23
- return [node];
24
- case "TSIntersectionType":
25
- case "TSUnionType":
26
- return (node.types ?? []).flatMap(literalsOf);
27
- case "TSParenthesizedType":
28
- return literalsOf(node.typeAnnotation);
29
- default:
30
- return [];
31
- }
32
- };
33
- const nameOf = (node) => {
34
- if (node === null || node === undefined)
35
- return null;
36
- if (typeof node.name === "string")
37
- return node.name;
38
- return typeof node.value === "string" ? node.value : null;
39
- };
40
- // The name a call is made by: `f()` and `x.f()` are both `f`. `x[f]()` and
41
- // `x["f"]()` are not — a computed property is not a name a vocabulary rule can
42
- // speak about — and neither is a private `x.#f()`. The CLI reads the same
43
- // three cases out of TypeScript's tree; the parity suite keeps them agreeing.
44
- const calleeName = (node) => {
45
- const callee = node.callee;
46
- if (callee === null || callee === undefined)
47
- return null;
48
- if (callee.type === "Identifier" && typeof callee.name === "string")
49
- return callee.name;
50
- if (callee.type !== "MemberExpression" || callee.computed === true)
51
- return null;
52
- const property = callee.property;
53
- if (property?.type !== "Identifier")
54
- return null;
55
- return nameOf(property);
56
- };
2
+ import { at, factsOfProgram, toRepoRelative, } from "./oxlint-api.js";
57
3
  export const makeMembersRule = (policy) => ({
58
4
  meta: {
59
5
  type: "problem",
@@ -65,36 +11,8 @@ export const makeMembersRule = (policy) => ({
65
11
  createOnce(context) {
66
12
  let file = "";
67
13
  let selected = [];
68
- const report = (node, name, declaration) => {
69
- const violations = evaluateMemberSite(selected, {
70
- file,
71
- subject: declaration === undefined ? "calls" : "members",
72
- name,
73
- ...(declaration === undefined
74
- ? {}
75
- : { in: declaration.name, declares: declaration.declares }),
76
- });
77
- for (const violation of violations) {
78
- if (policy.baseline.isBaselined(violation))
79
- continue;
80
- context.report({ node, message: formatMessage(violation) });
81
- }
82
- };
83
- // The members written in a declaration, under that declaration's name and
84
- // kind. A computed key is not a name a vocabulary rule can speak about;
85
- // neither is a private `#name`, an index, call or construct signature, or
86
- // a constructor.
87
- const declared = (declaration, declares, members) => {
88
- for (const member of members) {
89
- if (member.computed === true || !DECLARED_MEMBER_TYPES.has(member.type))
90
- continue;
91
- if (member.key?.type === "PrivateIdentifier" || member.kind === "constructor")
92
- continue;
93
- const name = nameOf(member.key);
94
- if (name !== null)
95
- report(member.key ?? member, name, { name: declaration, declares });
96
- }
97
- };
14
+ // A declared member is reported at its key, a call at the call — the nodes
15
+ // the pack's reader hands back with each site.
98
16
  return {
99
17
  before() {
100
18
  file = toRepoRelative(policy.repoRoot, context.filename);
@@ -103,35 +21,15 @@ export const makeMembersRule = (policy) => ({
103
21
  selected = memberRulesSelecting(policy.memberRules, file);
104
22
  return selected.length > 0;
105
23
  },
106
- TSTypeAliasDeclaration(node) {
107
- const declaration = nameOf(node.id);
108
- if (declaration === null)
109
- return;
110
- for (const literal of literalsOf(node.typeAnnotation)) {
111
- declared(declaration, "type", literal.members ?? []);
24
+ Program(node) {
25
+ for (const site of factsOfProgram(file, node).memberSites) {
26
+ for (const violation of evaluateMemberSite(selected, site)) {
27
+ if (policy.baseline.isBaselined(violation))
28
+ continue;
29
+ context.report({ node: at(site.node), message: formatMessage(violation) });
30
+ }
112
31
  }
113
32
  },
114
- TSInterfaceDeclaration(node) {
115
- const declaration = nameOf(node.id);
116
- const members = node.body?.body;
117
- if (declaration === null || !isMemberList(members))
118
- return;
119
- declared(declaration, "interface", members);
120
- },
121
- // A named class only, as the CLI reads it: an anonymous default class has
122
- // no name for `in`, and a class expression is a value.
123
- ClassDeclaration(node) {
124
- const declaration = nameOf(node.id);
125
- const members = node.body?.body;
126
- if (declaration === null || !isMemberList(members))
127
- return;
128
- declared(declaration, "class", members);
129
- },
130
- CallExpression(node) {
131
- const name = calleeName(node);
132
- if (name !== null)
133
- report(node, name);
134
- },
135
33
  };
136
34
  },
137
35
  });