@brighterly/lib-fe-codestyle 0.5.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -178
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +181 -0
- package/dist/config/define-config.d.ts +11 -0
- package/dist/config/define-config.js +33 -0
- package/dist/config/load-config.d.ts +15 -0
- package/dist/config/load-config.js +52 -0
- package/dist/config/schema.d.ts +58 -0
- package/dist/config/schema.js +139 -0
- package/dist/{invariants → engine}/ast.d.ts +1 -6
- package/dist/{invariants → engine}/ast.js +18 -1
- package/dist/engine/docs.d.ts +14 -0
- package/dist/engine/docs.js +72 -0
- package/dist/engine/meta.d.ts +32 -0
- package/dist/engine/meta.js +47 -0
- package/dist/engine/migrate.d.ts +22 -0
- package/dist/engine/migrate.js +175 -0
- package/dist/engine/overrides.d.ts +36 -0
- package/dist/engine/overrides.js +92 -0
- package/dist/engine/registry.d.ts +35 -0
- package/dist/engine/registry.js +100 -0
- package/dist/engine/report.d.ts +23 -0
- package/dist/engine/report.js +87 -0
- package/dist/engine/run.d.ts +57 -0
- package/dist/engine/run.js +353 -0
- package/dist/engine/self-test.d.ts +33 -0
- package/dist/engine/self-test.js +141 -0
- package/dist/engine/types.d.ts +232 -0
- package/dist/{invariants → engine}/types.js +2 -1
- package/dist/{invariants → engine}/util.d.ts +31 -39
- package/dist/{invariants → engine}/util.js +59 -86
- package/dist/index.d.ts +16 -0
- package/dist/index.js +14 -0
- package/dist/rules/convention.d.ts +20 -0
- package/dist/{invariants/rules → rules}/convention.js +89 -15
- package/dist/rules/index.d.ts +28 -0
- package/dist/{invariants/rules → rules}/index.js +40 -32
- package/dist/{invariants/rules → rules}/regression.d.ts +1 -1
- package/dist/{invariants/rules → rules}/regression.js +26 -6
- package/dist/{invariants/rules → rules}/shared.d.ts +1 -1
- package/dist/validate/index.d.ts +1 -1
- package/dist/validate/index.js +1 -1
- package/package.json +8 -13
- package/dist/invariants/index.d.ts +0 -7
- package/dist/invariants/index.js +0 -5
- package/dist/invariants/rules/convention.d.ts +0 -18
- package/dist/invariants/rules/fixtures.d.ts +0 -17
- package/dist/invariants/rules/fixtures.js +0 -89
- package/dist/invariants/rules/index.d.ts +0 -55
- package/dist/invariants/rules/linter.d.ts +0 -10
- package/dist/invariants/rules/linter.js +0 -9
- package/dist/invariants/run.d.ts +0 -53
- package/dist/invariants/run.js +0 -746
- package/dist/invariants/self-test.d.ts +0 -46
- package/dist/invariants/self-test.js +0 -84
- package/dist/invariants/types.d.ts +0 -222
- /package/dist/{invariants/rules → rules}/shared.js +0 -0
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The generic half of the self-test: prove every rule still fires on a violating fixture
|
|
3
|
-
* and stays quiet on a clean one.
|
|
4
|
-
*
|
|
5
|
-
* This exists because a rule that has silently stopped matching finds nothing — which is
|
|
6
|
-
* indistinguishable from a clean codebase. The scan alone can print PASS while a rule is
|
|
7
|
-
* dead.
|
|
8
|
-
*
|
|
9
|
-
* Only the harness lives here. The FIXTURES map and any repo-specific assertions stay with
|
|
10
|
-
* the app that owns them and arrive through `fixtures` and `extraChecks` — the original
|
|
11
|
-
* imported the app's funnel queries directly, which is exactly the coupling the package
|
|
12
|
-
* boundary removes.
|
|
13
|
-
*/
|
|
14
|
-
import type { Rule } from './types.js';
|
|
15
|
-
/**
|
|
16
|
-
* One fixture case. `bad` must produce >=1 finding, `good` must produce none.
|
|
17
|
-
* `excluded: true` asserts the FILE is matched by the rule's own `exclude` list —
|
|
18
|
-
* the case never runs the check; it proves the exclusion glob is right.
|
|
19
|
-
*/
|
|
20
|
-
export interface FixtureCase {
|
|
21
|
-
file: string;
|
|
22
|
-
bad?: string;
|
|
23
|
-
good?: string;
|
|
24
|
-
excluded?: true;
|
|
25
|
-
}
|
|
26
|
-
/** A rule may declare one case or an array (multi-check rules need one `bad` per check). */
|
|
27
|
-
export type Fixture = FixtureCase | FixtureCase[];
|
|
28
|
-
export interface SelfTestInput {
|
|
29
|
-
rules: Rule[];
|
|
30
|
-
fixtures: Record<string, Fixture>;
|
|
31
|
-
/**
|
|
32
|
-
* Rule ids exempt from the fixture requirement — registry and graph rules that read real
|
|
33
|
-
* repo files rather than a synthetic source, so a fixture cannot express them.
|
|
34
|
-
*/
|
|
35
|
-
skip?: string[];
|
|
36
|
-
/** Repo-specific assertions; each returns failure messages. May be async — the
|
|
37
|
-
* domain cases they express usually need to await a rule's own check(). */
|
|
38
|
-
extraChecks?: (() => string[] | Promise<string[]>)[];
|
|
39
|
-
}
|
|
40
|
-
export interface SelfTestResult {
|
|
41
|
-
ok: boolean;
|
|
42
|
-
failures: string[];
|
|
43
|
-
/** Rule ids that were actually exercised against a fixture. */
|
|
44
|
-
checked: string[];
|
|
45
|
-
}
|
|
46
|
-
export declare const runSelfTest: (input: SelfTestInput) => Promise<SelfTestResult>;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The generic half of the self-test: prove every rule still fires on a violating fixture
|
|
3
|
-
* and stays quiet on a clean one.
|
|
4
|
-
*
|
|
5
|
-
* This exists because a rule that has silently stopped matching finds nothing — which is
|
|
6
|
-
* indistinguishable from a clean codebase. The scan alone can print PASS while a rule is
|
|
7
|
-
* dead.
|
|
8
|
-
*
|
|
9
|
-
* Only the harness lives here. The FIXTURES map and any repo-specific assertions stay with
|
|
10
|
-
* the app that owns them and arrive through `fixtures` and `extraChecks` — the original
|
|
11
|
-
* imported the app's funnel queries directly, which is exactly the coupling the package
|
|
12
|
-
* boundary removes.
|
|
13
|
-
*/
|
|
14
|
-
import { matchesAny, stripCodeComments } from './util.js';
|
|
15
|
-
/** Run one rule against a synthetic single-file context. */
|
|
16
|
-
const runRule = async (rule, file, source, extra = {}) => {
|
|
17
|
-
const ctx = {
|
|
18
|
-
file,
|
|
19
|
-
source,
|
|
20
|
-
code: stripCodeComments(source, file),
|
|
21
|
-
read: () => null,
|
|
22
|
-
allFiles: [],
|
|
23
|
-
settings: {},
|
|
24
|
-
...extra,
|
|
25
|
-
};
|
|
26
|
-
return (await rule.check(ctx)) ?? [];
|
|
27
|
-
};
|
|
28
|
-
export const runSelfTest = async (input) => {
|
|
29
|
-
const { rules, fixtures, skip = [], extraChecks = [] } = input;
|
|
30
|
-
const failures = [];
|
|
31
|
-
const checked = [];
|
|
32
|
-
for (const rule of rules) {
|
|
33
|
-
if (skip.includes(rule.id))
|
|
34
|
-
continue;
|
|
35
|
-
const declared = fixtures[rule.id];
|
|
36
|
-
if (!declared) {
|
|
37
|
-
failures.push(`${rule.id}: no self-test fixture — every rule needs one`);
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
const cases = Array.isArray(declared) ? declared : [declared];
|
|
41
|
-
// Declared, not fired: a dead snippet reports once, on its own line. `misplaced`
|
|
42
|
-
// mutes the two floors — an out-of-scope path already has its own message and
|
|
43
|
-
// the floors would only tell the author to add cases they already declared.
|
|
44
|
-
let declaredBad = false;
|
|
45
|
-
let declaredGood = false;
|
|
46
|
-
let misplaced = false;
|
|
47
|
-
for (const fx of cases) {
|
|
48
|
-
if (fx.excluded) {
|
|
49
|
-
if (!matchesAny(fx.file, rule.scope))
|
|
50
|
-
failures.push(`${rule.id}: ${fx.file} is declared excluded but is not in the rule's scope (${rule.scope.join(', ')}) — the exclusion is doing no work`);
|
|
51
|
-
if (!rule.exclude || !matchesAny(fx.file, rule.exclude))
|
|
52
|
-
failures.push(`${rule.id}: ${fx.file} is declared excluded but the rule's exclude list (${(rule.exclude ?? []).join(', ') || 'none'}) does not match it`);
|
|
53
|
-
if (fx.bad !== undefined)
|
|
54
|
-
failures.push(`${rule.id}: ${fx.file} is excluded — the runner never checks it, so a 'bad' snippet proves nothing; drop it`);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (!matchesAny(fx.file, rule.scope)) {
|
|
58
|
-
failures.push(`${rule.id}: fixture path ${fx.file} is outside the rule's own scope (${rule.scope.join(', ')})`);
|
|
59
|
-
misplaced = true;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (fx.bad !== undefined) {
|
|
63
|
-
declaredBad = true;
|
|
64
|
-
const bad = await runRule(rule, fx.file, fx.bad);
|
|
65
|
-
if (bad.length === 0)
|
|
66
|
-
failures.push(`${rule.id}: violating fixture at ${fx.file} produced no finding — the check is a no-op:\n${fx.bad}`);
|
|
67
|
-
}
|
|
68
|
-
if (fx.good !== undefined) {
|
|
69
|
-
declaredGood = true;
|
|
70
|
-
const good = await runRule(rule, fx.file, fx.good);
|
|
71
|
-
if (good.length > 0)
|
|
72
|
-
failures.push(`${rule.id}: clean fixture at ${fx.file} produced ${good.length} false positive(s): ${good.map(g => g.message).join('; ')}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (!misplaced && !declaredGood)
|
|
76
|
-
failures.push(`${rule.id}: no case proves the rule stays quiet — at least one 'good' snippet is required`);
|
|
77
|
-
if (!misplaced && !declaredBad)
|
|
78
|
-
failures.push(`${rule.id}: no case declares a 'bad' snippet — an 'excluded' case never runs the check, and a 'good'-only fixture cannot prove the rule still matches`);
|
|
79
|
-
checked.push(rule.id);
|
|
80
|
-
}
|
|
81
|
-
for (const check of extraChecks)
|
|
82
|
-
failures.push(...(await check()));
|
|
83
|
-
return { ok: failures.length === 0, failures, checked };
|
|
84
|
-
};
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The contracts every rule and every config entry must satisfy.
|
|
3
|
-
*
|
|
4
|
-
* These exist so that extending this directory fails at typecheck instead of
|
|
5
|
-
* failing silently: a rule with a typo'd field, a manifest entry naming a
|
|
6
|
-
* mechanism that has no resolver, a level that is not a real level. Those were
|
|
7
|
-
* all silently ignored while this was plain JavaScript.
|
|
8
|
-
*/
|
|
9
|
-
/** `error` counts toward the exit code · `warn` reports · `off` skips entirely. */
|
|
10
|
-
export type Level = 'error' | 'warn' | 'off';
|
|
11
|
-
/**
|
|
12
|
-
* Rule ID: the family letter says where the rule came from.
|
|
13
|
-
* R — regression: a CLAUDE.md "What NOT to do" scar
|
|
14
|
-
* B — business: the funnel-data contract, confirmed by PMM
|
|
15
|
-
* C — convention: a preference we chose, advisory by default
|
|
16
|
-
* L — linter: covered by a built-in ESLint rule, configured elsewhere
|
|
17
|
-
* E — e2e: end-to-end test authoring standards
|
|
18
|
-
* IDs are load-bearing (`--rule=`, `invariant-ignore:`, allowlists, levels) —
|
|
19
|
-
* retire one, never recycle it.
|
|
20
|
-
*/
|
|
21
|
-
/** App-local ids are bare; shared-pack ids carry the `core/` prefix (`core/R1`). */
|
|
22
|
-
export type RuleId = `${'' | 'core/'}${'R' | 'B' | 'C' | 'L' | 'E'}${number}`;
|
|
23
|
-
/**
|
|
24
|
-
* Meta rules. They police the escape hatch rather than the app, so they have no
|
|
25
|
-
* family letter — but they are addressable exactly like any other rule
|
|
26
|
-
* (`allowlists`, `levels`, `invariant-ignore`), so they belong in the ID space.
|
|
27
|
-
* IG0 — an `invariant-ignore` with no reason
|
|
28
|
-
* IG1 — a reference to a rule ID that no longer exists
|
|
29
|
-
* IG2 — an `invariant-ignore` naming a rule declared `noIgnore`
|
|
30
|
-
*/
|
|
31
|
-
export type MetaRuleId = 'IG0' | 'IG1' | 'IG2';
|
|
32
|
-
/** Anything addressable in config or a suppression comment. */
|
|
33
|
-
export type AnyRuleId = RuleId | MetaRuleId;
|
|
34
|
-
/** One violation. `line` is 1-based. */
|
|
35
|
-
export interface Finding {
|
|
36
|
-
file: string;
|
|
37
|
-
line: number;
|
|
38
|
-
message: string;
|
|
39
|
-
/**
|
|
40
|
-
* Severity for THIS finding, when it differs from the rest of the rule's.
|
|
41
|
-
* Omit to inherit the rule's. A `levels` override in config.mts is a ceiling
|
|
42
|
-
* over this — see resolveFindingLevel in util.mts.
|
|
43
|
-
*/
|
|
44
|
-
level?: Level;
|
|
45
|
-
}
|
|
46
|
-
/** A finding after the runner has attributed it to a rule and a level. */
|
|
47
|
-
export interface ReportedFinding extends Finding {
|
|
48
|
-
ruleId: AnyRuleId;
|
|
49
|
-
level: Level;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Every settings key a shared rule reads, documented for hover. Annotate the app's
|
|
53
|
-
* `settings` const with this to get completion and the per-key contract in the IDE:
|
|
54
|
-
*
|
|
55
|
-
* export const settings: SharedRuleSettings & Record<string, string> = { ... }
|
|
56
|
-
*
|
|
57
|
-
* All keys are optional: a missing one degrades the rule's MESSAGE (or falls back to
|
|
58
|
-
* the documented default), never its firing.
|
|
59
|
-
*/
|
|
60
|
-
export interface SharedRuleSettings {
|
|
61
|
-
/** `core/R1`/`core/R3` fix messages: how this app names its safe storage wrapper, e.g. 'safeLocalStorage from @helpers/safe-local-storage'. */
|
|
62
|
-
safeStorageModule?: string;
|
|
63
|
-
/** `core/R1`: repo-relative path of the wrapper FILE itself — the one place bare storage access belongs, auto-exempted so no app has to allowlist its own wrapper. E.g. 'app/utils/helpers/safe-local-storage.ts'. */
|
|
64
|
-
safeStorageFile?: string;
|
|
65
|
-
/** `core/R3` fix message: the guarded parse helper, e.g. 'parseStoredJson() from @helpers/safe-local-storage'. */
|
|
66
|
-
safeJsonParse?: string;
|
|
67
|
-
/** `core/C3`: the palette file that legitimately DEFINES colour literals, e.g. 'app/assets/scss/_colors.scss'. */
|
|
68
|
-
scssTokensFile?: string;
|
|
69
|
-
/** `core/C2`: root of the UI-kit tree whose component names must not be duplicated. Default 'app/components/UI'. */
|
|
70
|
-
uiKitRoot?: string;
|
|
71
|
-
/** `core/C4` fix message: the shared date-format enum, e.g. 'DateFormat from @ts-types/date/enums'. */
|
|
72
|
-
dateFormatModule?: string;
|
|
73
|
-
/** `core/C5`: where entity type folders live. Default 'app/types'. */
|
|
74
|
-
typesRoot?: string;
|
|
75
|
-
/** `core/C6` fix message: this app's typography source — a mixin in some apps ('the font() mixin from @scss/_mixins.scss'), a component in others ('the AppText component'). */
|
|
76
|
-
typographySource?: string;
|
|
77
|
-
/** `core/C7`: which `<script>` attribute order is canonical in this app. Default 'setup-first' (`<script setup lang="ts">`). */
|
|
78
|
-
scriptAttrOrder?: 'setup-first' | 'lang-first';
|
|
79
|
-
/** The consumer file whose `invariant-ignore:` strings are self-test FIXTURE DATA, not real suppressions — the engine skips ignore-parsing it entirely, replacing per-file IG0/IG1 allowlist boilerplate. E.g. 'invariants/self-test.mts'. */
|
|
80
|
-
selfTestFile?: string;
|
|
81
|
-
}
|
|
82
|
-
/** What a rule's `check` receives. */
|
|
83
|
-
export interface RuleContext {
|
|
84
|
-
/** Repo-relative path of the file under inspection. */
|
|
85
|
-
file: string;
|
|
86
|
-
/** Raw file contents. */
|
|
87
|
-
source: string;
|
|
88
|
-
/** Same contents with comment bodies blanked, line numbers preserved. */
|
|
89
|
-
code: string;
|
|
90
|
-
/** Read any other repo-relative file (cached). Returns null if unreadable. */
|
|
91
|
-
read: (rel: string) => string | null;
|
|
92
|
-
/** Every scanned file in the repo minus the `unmaintained` paths — for rules that resolve references. */
|
|
93
|
-
allFiles: string[];
|
|
94
|
-
/**
|
|
95
|
-
* Every scanned file INCLUDING the `unmaintained` paths. Undefined unless the rule declares
|
|
96
|
-
* `seesUnmaintained` — the exclusion is meant to be invisible to a rule, and this is the one
|
|
97
|
-
* declared way through it.
|
|
98
|
-
*/
|
|
99
|
-
scanned?: string[];
|
|
100
|
-
/**
|
|
101
|
-
* App-specific paths a SHARED rule needs, supplied by `config.settings`. This is how a
|
|
102
|
-
* portable rule learns where this app keeps its SCSS tokens or its UI-kit root, without
|
|
103
|
-
* the rule becoming a factory. The known keys are documented on `SharedRuleSettings`.
|
|
104
|
-
*/
|
|
105
|
-
settings: Readonly<SharedRuleSettings & Record<string, string>>;
|
|
106
|
-
}
|
|
107
|
-
/** A `once: true` rule reads its own inputs, so it gets no per-file fields. */
|
|
108
|
-
export type OnceRuleContext = Pick<RuleContext, 'read' | 'allFiles' | 'scanned'> & Partial<RuleContext>;
|
|
109
|
-
interface BaseRule {
|
|
110
|
-
id: RuleId;
|
|
111
|
-
/** Short description — appears in the report and the README inventory. */
|
|
112
|
-
title: string;
|
|
113
|
-
level: Level;
|
|
114
|
-
/** The silent-failure mode. A rule without a why gets deleted by future-you. */
|
|
115
|
-
why: string;
|
|
116
|
-
/** What to do instead. */
|
|
117
|
-
fix: string;
|
|
118
|
-
/** Glob patterns this rule applies to. */
|
|
119
|
-
scope: string[];
|
|
120
|
-
/** Glob patterns to skip within scope. */
|
|
121
|
-
exclude?: string[];
|
|
122
|
-
/**
|
|
123
|
-
* The rule cannot be suppressed with `invariant-ignore`; a comment naming it is
|
|
124
|
-
* itself a finding (IG2). For rules whose author is an AI: exceptions are made by
|
|
125
|
-
* a human in the rulebook (rule + fixture), never by the agent in the file under
|
|
126
|
-
* check — an AI writes a plausible reason for free.
|
|
127
|
-
* Honoured by the runner: the suppression lookup skips such rules, and the IG2
|
|
128
|
-
* block reports the attempt.
|
|
129
|
-
*/
|
|
130
|
-
noIgnore?: boolean;
|
|
131
|
-
/** Set when `check` returns a promise, so the runner awaits it. */
|
|
132
|
-
async?: boolean;
|
|
133
|
-
/**
|
|
134
|
-
* Hand this rule `scanned` — every file, the `unmaintained` paths included — where every other
|
|
135
|
-
* rule gets only `allFiles`. The exclusion exists so rules that model the component contract
|
|
136
|
-
* never meet a tree that does not follow it; a rule asking a different question may opt out,
|
|
137
|
-
* and says so here.
|
|
138
|
-
*/
|
|
139
|
-
seesUnmaintained?: boolean;
|
|
140
|
-
}
|
|
141
|
-
/** Runs once per matching file. */
|
|
142
|
-
export interface FileRule extends BaseRule {
|
|
143
|
-
once?: false;
|
|
144
|
-
check: (ctx: RuleContext) => Finding[] | Promise<Finding[]>;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Runs once per invocation if any file in `scope` is in the candidate set, for
|
|
148
|
-
* rules that read several files themselves (registry and graph checks).
|
|
149
|
-
*/
|
|
150
|
-
export interface OnceRule extends BaseRule {
|
|
151
|
-
once: true;
|
|
152
|
-
check: (ctx: OnceRuleContext) => Finding[] | Promise<Finding[]>;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* The two shapes are separate so a per-file rule cannot accidentally be handed a
|
|
156
|
-
* context without `source`, and a `once` rule cannot claim to receive one.
|
|
157
|
-
*/
|
|
158
|
-
export type Rule = FileRule | OnceRule;
|
|
159
|
-
/** Per-rule, per-file exemptions. The value is the reason — required. */
|
|
160
|
-
export type Allowlists = Partial<Record<AnyRuleId, Record<string, string>>>;
|
|
161
|
-
/**
|
|
162
|
-
* Severity overrides, keyed by rule ID. The reason is MANDATORY: once four repos share a
|
|
163
|
-
* rule pack, a bare `levels.X = 'off'` is how a shared rule gets silenced invisibly.
|
|
164
|
-
* Read it through `levelOverrideOf` rather than indexing directly.
|
|
165
|
-
*/
|
|
166
|
-
export type Levels = Partial<Record<AnyRuleId, {
|
|
167
|
-
level: Level;
|
|
168
|
-
reason: string;
|
|
169
|
-
}>>;
|
|
170
|
-
/**
|
|
171
|
-
* Retired rule IDs and why. A numeric gap records only that *something* existed;
|
|
172
|
-
* this records what, so a stale reference gets a useful message instead of
|
|
173
|
-
* "unknown rule". Never delete an entry — that is the point of it.
|
|
174
|
-
*/
|
|
175
|
-
export type RetiredRules = Record<string, string>;
|
|
176
|
-
/** Everything rule IG0/IG1 needs, passed in so the check stays pure and testable. */
|
|
177
|
-
export interface DanglingRefInput {
|
|
178
|
-
/** IDs of rules currently loaded. */
|
|
179
|
-
live: Set<string>;
|
|
180
|
-
retired: RetiredRules;
|
|
181
|
-
allowlists: Allowlists;
|
|
182
|
-
levels: Levels;
|
|
183
|
-
/** Every `invariant-ignore` comment found in the scanned set. */
|
|
184
|
-
ignores: {
|
|
185
|
-
file: string;
|
|
186
|
-
line: number;
|
|
187
|
-
ids: string[];
|
|
188
|
-
}[];
|
|
189
|
-
/** Where allowlists/levels live, for the finding location. */
|
|
190
|
-
configFile?: string;
|
|
191
|
-
/** Contents of `configFile`, so a stale key can be reported on its own line. */
|
|
192
|
-
configSource?: string;
|
|
193
|
-
/** Answers "does this repo-relative file exist?" — enables the allowlist-path staleness check. */
|
|
194
|
-
pathExists?: (path: string) => boolean;
|
|
195
|
-
}
|
|
196
|
-
/** An invariant a built-in ESLint rule already covers. */
|
|
197
|
-
export interface LinterOwnedRule {
|
|
198
|
-
id: RuleId;
|
|
199
|
-
eslintRule: string;
|
|
200
|
-
level: Level;
|
|
201
|
-
title: string;
|
|
202
|
-
why: string;
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Areas the validator deliberately does not look at.
|
|
206
|
-
*
|
|
207
|
-
* Not an allowlist: an allowlist exempts one FILE from one RULE, and the file is
|
|
208
|
-
* still part of the funnel model. This removes a whole subtree from the scanned
|
|
209
|
-
* set and a whole funnel from `query.funnels()`, so no rule — present or future —
|
|
210
|
-
* can see it. That is the right shape for a design system in flux, and the wrong
|
|
211
|
-
* shape for anything we intend to keep correct.
|
|
212
|
-
*
|
|
213
|
-
* Every entry carries a reason, and the CLI prints the totals on every run: an
|
|
214
|
-
* exclusion nobody can see is how a validator quietly stops covering the repo.
|
|
215
|
-
*/
|
|
216
|
-
export interface Unmaintained {
|
|
217
|
-
/** Glob patterns dropped from the scanned file set → reason. */
|
|
218
|
-
paths: Record<string, string>;
|
|
219
|
-
/** WRAP_STEPS keys hidden from `query.funnels()` → reason. */
|
|
220
|
-
funnels: Record<string, string>;
|
|
221
|
-
}
|
|
222
|
-
export {};
|
|
File without changes
|