@nielspeter/eess 0.2.1 → 0.4.0
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 +3 -3
- package/dist/assertion-advice.d.ts +26 -0
- package/dist/assertion-advice.d.ts.map +1 -0
- package/dist/assertion-advice.js +29 -0
- package/dist/assertion-advice.js.map +1 -0
- package/dist/baseline.d.ts +69 -4
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +119 -13
- package/dist/baseline.js.map +1 -1
- package/dist/cache-registry.d.ts +14 -0
- package/dist/cache-registry.d.ts.map +1 -0
- package/dist/cache-registry.js +37 -0
- package/dist/cache-registry.js.map +1 -0
- package/dist/cardinality.d.ts +35 -0
- package/dist/cardinality.d.ts.map +1 -0
- package/dist/cardinality.js +65 -0
- package/dist/cardinality.js.map +1 -0
- package/dist/check-options.d.ts +14 -0
- package/dist/check-options.d.ts.map +1 -1
- package/dist/comment-suppression.d.ts +60 -0
- package/dist/comment-suppression.d.ts.map +1 -0
- package/dist/comment-suppression.js +89 -0
- package/dist/comment-suppression.js.map +1 -0
- package/dist/condition.d.ts +6 -0
- package/dist/condition.d.ts.map +1 -1
- package/dist/correspondence-core.d.ts +35 -0
- package/dist/correspondence-core.d.ts.map +1 -0
- package/dist/correspondence-core.js +38 -0
- package/dist/correspondence-core.js.map +1 -0
- package/dist/correspondence.d.ts +27 -5
- package/dist/correspondence.d.ts.map +1 -1
- package/dist/correspondence.js +39 -5
- package/dist/correspondence.js.map +1 -1
- package/dist/dedupe-config-findings.d.ts +56 -0
- package/dist/dedupe-config-findings.d.ts.map +1 -0
- package/dist/dedupe-config-findings.js +74 -0
- package/dist/dedupe-config-findings.js.map +1 -0
- package/dist/define.d.ts +44 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +45 -4
- package/dist/define.js.map +1 -1
- package/dist/diff-aware.d.ts +8 -0
- package/dist/diff-aware.d.ts.map +1 -1
- package/dist/diff-aware.js +11 -2
- package/dist/diff-aware.js.map +1 -1
- package/dist/diff-disclosure.d.ts +72 -0
- package/dist/diff-disclosure.d.ts.map +1 -0
- package/dist/diff-disclosure.js +110 -0
- package/dist/diff-disclosure.js.map +1 -0
- package/dist/disk-set.d.ts +27 -0
- package/dist/disk-set.d.ts.map +1 -0
- package/dist/disk-set.js +12 -0
- package/dist/disk-set.js.map +1 -0
- package/dist/edge-coverage.d.ts +96 -0
- package/dist/edge-coverage.d.ts.map +1 -0
- package/dist/edge-coverage.js +108 -0
- package/dist/edge-coverage.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +32 -0
- package/dist/errors.js.map +1 -1
- package/dist/execute-rule.d.ts +11 -5
- package/dist/execute-rule.d.ts.map +1 -1
- package/dist/execute-rule.js +87 -19
- package/dist/execute-rule.js.map +1 -1
- package/dist/format-github.d.ts.map +1 -1
- package/dist/format-github.js +37 -5
- package/dist/format-github.js.map +1 -1
- package/dist/format-json.d.ts +83 -1
- package/dist/format-json.d.ts.map +1 -1
- package/dist/format-json.js +70 -9
- package/dist/format-json.js.map +1 -1
- package/dist/format.d.ts +4 -2
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +40 -8
- package/dist/format.js.map +1 -1
- package/dist/glob-site.d.ts +232 -0
- package/dist/glob-site.d.ts.map +1 -0
- package/dist/glob-site.js +156 -0
- package/dist/glob-site.js.map +1 -0
- package/dist/identity-root.d.ts +55 -0
- package/dist/identity-root.d.ts.map +1 -0
- package/dist/identity-root.js +170 -0
- package/dist/identity-root.js.map +1 -0
- package/dist/index.d.ts +41 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/owns-empty-discovery.d.ts +5 -0
- package/dist/owns-empty-discovery.d.ts.map +1 -0
- package/dist/owns-empty-discovery.js +77 -0
- package/dist/owns-empty-discovery.js.map +1 -0
- package/dist/path-universe.d.ts +44 -0
- package/dist/path-universe.d.ts.map +1 -0
- package/dist/path-universe.js +21 -0
- package/dist/path-universe.js.map +1 -0
- package/dist/predicate.d.ts +33 -0
- package/dist/predicate.d.ts.map +1 -1
- package/dist/preset-dispatch.d.ts +19 -0
- package/dist/preset-dispatch.d.ts.map +1 -1
- package/dist/preset-dispatch.js +43 -3
- package/dist/preset-dispatch.js.map +1 -1
- package/dist/project-relative.d.ts +25 -0
- package/dist/project-relative.d.ts.map +1 -0
- package/dist/project-relative.js +54 -0
- package/dist/project-relative.js.map +1 -0
- package/dist/report.d.ts +11 -5
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +17 -0
- package/dist/report.js.map +1 -1
- package/dist/rule-builder-like.d.ts +11 -0
- package/dist/rule-builder-like.d.ts.map +1 -0
- package/dist/rule-builder-like.js +2 -0
- package/dist/rule-builder-like.js.map +1 -0
- package/dist/rule-builder.d.ts +109 -84
- package/dist/rule-builder.d.ts.map +1 -1
- package/dist/rule-builder.js +202 -158
- package/dist/rule-builder.js.map +1 -1
- package/dist/rule-description.d.ts +33 -1
- package/dist/rule-description.d.ts.map +1 -1
- package/dist/rule-description.js +17 -1
- package/dist/rule-description.js.map +1 -1
- package/dist/rule-metadata.d.ts +20 -3
- package/dist/rule-metadata.d.ts.map +1 -1
- package/dist/selection-memo.d.ts +47 -0
- package/dist/selection-memo.d.ts.map +1 -0
- package/dist/selection-memo.js +68 -0
- package/dist/selection-memo.js.map +1 -0
- package/dist/shallow-clone.d.ts +20 -0
- package/dist/shallow-clone.d.ts.map +1 -0
- package/dist/shallow-clone.js +27 -0
- package/dist/shallow-clone.js.map +1 -0
- package/dist/stderr.d.ts +16 -0
- package/dist/stderr.d.ts.map +1 -0
- package/dist/stderr.js +73 -0
- package/dist/stderr.js.map +1 -0
- package/dist/terminal-builder.d.ts +209 -9
- package/dist/terminal-builder.d.ts.map +1 -1
- package/dist/terminal-builder.js +264 -20
- package/dist/terminal-builder.js.map +1 -1
- package/dist/type-guards.d.ts +38 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +42 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/unsuppressable.d.ts +54 -0
- package/dist/unsuppressable.d.ts.map +1 -0
- package/dist/unsuppressable.js +76 -0
- package/dist/unsuppressable.js.map +1 -0
- package/dist/vacuity-findings.d.ts +55 -0
- package/dist/vacuity-findings.d.ts.map +1 -0
- package/dist/vacuity-findings.js +124 -0
- package/dist/vacuity-findings.js.map +1 -0
- package/dist/violation.d.ts +152 -4
- package/dist/violation.d.ts.map +1 -1
- package/dist/violation.js +262 -1
- package/dist/violation.js.map +1 -1
- package/package.json +1 -1
package/dist/terminal-builder.js
CHANGED
|
@@ -1,29 +1,64 @@
|
|
|
1
|
+
import { zeroExaminedViolation, deadGlobViolation, unmetExpectNonEmptyViolation, zeroLoadedSourceViolation, expiredExpectEmptyViolation, } from './vacuity-findings.js';
|
|
2
|
+
import { UNSUPPRESSABLE } from './unsuppressable.js';
|
|
1
3
|
import { isSilent } from './silent-exclusion.js';
|
|
2
4
|
import { executeCheck, executeWarn, applyFilters } from './execute-rule.js';
|
|
5
|
+
import { shallowClone } from './shallow-clone.js';
|
|
3
6
|
/**
|
|
4
7
|
* Abstract base class for builders that share the terminal method pattern
|
|
5
8
|
* (because, rule, excluding, check, warn, severity) but have different
|
|
6
9
|
* element collection and evaluation models.
|
|
7
10
|
*
|
|
8
11
|
* Used by SliceRuleBuilder, SchemaRuleBuilder, ResolverRuleBuilder,
|
|
9
|
-
* PairFinalBuilder,
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* additional concerns (predicate/condition pipeline, fork semantics).
|
|
12
|
+
* PairFinalBuilder, SmellBuilder, CorrespondenceBuilder, TsconfigBuilder, and
|
|
13
|
+
* (as of plan 0088 Phase 4) RuleBuilder — all of which implement the same
|
|
14
|
+
* terminal methods and now share evidence-of-examination (ADR-010) alongside
|
|
15
|
+
* evaluation-model differences.
|
|
14
16
|
*/
|
|
15
17
|
export class TerminalBuilder {
|
|
16
18
|
_reason;
|
|
17
19
|
_metadata;
|
|
18
20
|
_exclusions = [];
|
|
19
21
|
_silentIndices = new Set();
|
|
22
|
+
/** ADR-010 part 3 — `.expectEmpty()`/`.expectNonEmpty()`. `undefined` = no declaration. */
|
|
23
|
+
_expectEmpty;
|
|
24
|
+
/**
|
|
25
|
+
* Declare that this rule's examined set is expected to be empty. A pass is
|
|
26
|
+
* legitimate only while the declaration stays true — the day a unit is
|
|
27
|
+
* examined, this rule reddens (ADR-010 part 3's expiry property). The
|
|
28
|
+
* complement of `expectNonEmpty()`; declaring both is nonsensical and the
|
|
29
|
+
* later call wins (last-write, matching `.rule()`'s own semantics).
|
|
30
|
+
*/
|
|
31
|
+
expectEmpty() {
|
|
32
|
+
const next = this.copy();
|
|
33
|
+
next._expectEmpty = true;
|
|
34
|
+
return next;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Declare that this rule's examined set must be non-empty — the opposite
|
|
38
|
+
* assertion, for a corpus the author has positive knowledge should never
|
|
39
|
+
* legitimately be empty. Unlike a bare, undeclared rule, this **overrides
|
|
40
|
+
* `assertsCardinality()`'s exemption**: a `.notExist()`-shaped condition
|
|
41
|
+
* that finds zero subjects normally passes silently (emptiness satisfies
|
|
42
|
+
* what it asserts) — but if the author declared `.expectNonEmpty()`, that
|
|
43
|
+
* silence is exactly what they said should never happen, so it reddens
|
|
44
|
+
* instead, naming the declaration as the reason. This is the real
|
|
45
|
+
* difference from not declaring anything: without it, a `.notExist()` rule
|
|
46
|
+
* over an accidentally-emptied corpus (a glob typo, a temporarily-missing
|
|
47
|
+
* folder) passes and says nothing; with it, that same emptiness is caught.
|
|
48
|
+
*/
|
|
49
|
+
expectNonEmpty() {
|
|
50
|
+
const next = this.copy();
|
|
51
|
+
next._expectEmpty = false;
|
|
52
|
+
return next;
|
|
53
|
+
}
|
|
20
54
|
/**
|
|
21
55
|
* Attach a human-readable rationale to the rule.
|
|
22
56
|
* Included in violation messages when `.check()` throws.
|
|
23
57
|
*/
|
|
24
58
|
because(reason) {
|
|
25
|
-
|
|
26
|
-
|
|
59
|
+
const next = this.copy();
|
|
60
|
+
next._reason = reason;
|
|
61
|
+
return next;
|
|
27
62
|
}
|
|
28
63
|
/**
|
|
29
64
|
* Attach rich metadata to the rule.
|
|
@@ -32,11 +67,12 @@ export class TerminalBuilder {
|
|
|
32
67
|
* If `metadata.because` is set, it also sets the reason (same as `.because()`).
|
|
33
68
|
*/
|
|
34
69
|
rule(metadata) {
|
|
35
|
-
|
|
70
|
+
const next = this.copy();
|
|
71
|
+
next._metadata = metadata;
|
|
36
72
|
if (metadata.because) {
|
|
37
|
-
|
|
73
|
+
next._reason = metadata.because;
|
|
38
74
|
}
|
|
39
|
-
return
|
|
75
|
+
return next;
|
|
40
76
|
}
|
|
41
77
|
/**
|
|
42
78
|
* Exclude specific elements from violation reporting.
|
|
@@ -50,16 +86,17 @@ export class TerminalBuilder {
|
|
|
50
86
|
* Emits a warning if an exclusion matches zero violations (stale exclusion).
|
|
51
87
|
*/
|
|
52
88
|
excluding(...patterns) {
|
|
89
|
+
const next = this.copy();
|
|
53
90
|
for (const p of patterns) {
|
|
54
91
|
if (isSilent(p)) {
|
|
55
|
-
|
|
56
|
-
|
|
92
|
+
next._exclusions.push(p.pattern);
|
|
93
|
+
next._silentIndices.add(next._exclusions.length - 1);
|
|
57
94
|
}
|
|
58
95
|
else {
|
|
59
|
-
|
|
96
|
+
next._exclusions.push(p);
|
|
60
97
|
}
|
|
61
98
|
}
|
|
62
|
-
return
|
|
99
|
+
return next;
|
|
63
100
|
}
|
|
64
101
|
/**
|
|
65
102
|
* Return a structured description of this rule without executing it.
|
|
@@ -75,13 +112,20 @@ export class TerminalBuilder {
|
|
|
75
112
|
imperative: this._metadata?.imperative,
|
|
76
113
|
};
|
|
77
114
|
}
|
|
115
|
+
/** This rule's own account of itself, for `vacuity-findings.ts`. */
|
|
116
|
+
facts() {
|
|
117
|
+
return {
|
|
118
|
+
ruleClass: { name: this.constructor.name },
|
|
119
|
+
reason: this._reason,
|
|
120
|
+
describeRule: () => this.describeRule(),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
78
123
|
/**
|
|
79
124
|
* Execute the rule and return violations after exclusion filtering.
|
|
80
125
|
* Does not throw — use for programmatic access (presets, aggregation).
|
|
81
126
|
*/
|
|
82
127
|
violations() {
|
|
83
|
-
|
|
84
|
-
return applyFilters(raw, {
|
|
128
|
+
return applyFilters(this.evidencedViolations(), {
|
|
85
129
|
reason: this._reason,
|
|
86
130
|
metadata: this._metadata,
|
|
87
131
|
exclusions: this._exclusions,
|
|
@@ -95,8 +139,7 @@ export class TerminalBuilder {
|
|
|
95
139
|
* @param options - Optional baseline, diff filtering, and output format
|
|
96
140
|
*/
|
|
97
141
|
check(options) {
|
|
98
|
-
|
|
99
|
-
executeCheck(violations, {
|
|
142
|
+
executeCheck(this.evidencedViolations(), {
|
|
100
143
|
reason: this._reason,
|
|
101
144
|
metadata: this._metadata,
|
|
102
145
|
exclusions: this._exclusions,
|
|
@@ -110,14 +153,77 @@ export class TerminalBuilder {
|
|
|
110
153
|
* @param options - Optional baseline, diff filtering, and output format
|
|
111
154
|
*/
|
|
112
155
|
warn(options) {
|
|
113
|
-
|
|
114
|
-
executeWarn(violations, {
|
|
156
|
+
executeWarn(this.evidencedViolations(), {
|
|
115
157
|
reason: this._reason,
|
|
116
158
|
metadata: this._metadata,
|
|
117
159
|
exclusions: this._exclusions,
|
|
118
160
|
silentIndices: this._silentIndices,
|
|
119
161
|
}, options);
|
|
120
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* ADR-010 — the evidence gate. Calls `collectViolations()` once and turns
|
|
165
|
+
* its `{ violations, examined, sourceEmpty }` verdict into the
|
|
166
|
+
* `ArchViolation[]` every terminal method still emits, applying the
|
|
167
|
+
* declared-empty precedence:
|
|
168
|
+
*
|
|
169
|
+
* - `examined === 0`, `sourceEmpty === true`: ADR-010 part 3's precedence
|
|
170
|
+
* rule — "an empty project outranks every token." The family's own
|
|
171
|
+
* upstream source (files loaded, a diagram parsed) produced nothing at
|
|
172
|
+
* all, before any predicate ran. Neither `.expectEmpty()` nor
|
|
173
|
+
* `assertsCardinality()` can rescue this: there is no selection to
|
|
174
|
+
* widen, and a `.notExist()`-shaped condition "passing" against an
|
|
175
|
+
* instrument that never loaded anything is not evidence of anything.
|
|
176
|
+
* Always an unsuppressable configuration finding.
|
|
177
|
+
* - `examined === 0`, `.expectNonEmpty()` declared: the caller has positive
|
|
178
|
+
* knowledge this corpus should never be empty — this OVERRIDES
|
|
179
|
+
* `assertsCardinality()`'s exemption (a `.notExist()` rule that would
|
|
180
|
+
* otherwise pass silently on zero subjects reddens instead, since silent
|
|
181
|
+
* emptiness is exactly what the declaration rules out). Fails, naming the
|
|
182
|
+
* unmet declaration.
|
|
183
|
+
* - `examined === 0`, no `.expectEmpty()` declared: the rule's own
|
|
184
|
+
* instrument is broken (a dead selector, an unreachable seam) — this is
|
|
185
|
+
* a configuration finding, unsuppressable (`bypassFilters`), not a silent
|
|
186
|
+
* pass. This is the guarantee ADR-009/010 exist for.
|
|
187
|
+
* - `examined === 0`, `.expectEmpty()` declared: satisfied. Passes.
|
|
188
|
+
* - `examined === 0`, `assertsCardinality()` true (and no `.expectNonEmpty()`
|
|
189
|
+
* override above): the rule's own conditions are satisfied BY emptiness
|
|
190
|
+
* (e.g. `.notExist()`) — a different exemption from `.expectEmpty()`,
|
|
191
|
+
* because it is a property of what the condition asserts, not a caller's
|
|
192
|
+
* declaration about the corpus. Passes, with no finding at all (not even
|
|
193
|
+
* a silent one — the condition already reported truthfully that it found
|
|
194
|
+
* nothing wrong).
|
|
195
|
+
* - `examined > 0`, `.expectEmpty()` declared: the declaration has expired
|
|
196
|
+
* — a unit was examined despite the author's "this stays empty" claim.
|
|
197
|
+
* Fails, appending an expiry finding to whatever `collectViolations()`
|
|
198
|
+
* found (never replacing it — the underlying findings are still real).
|
|
199
|
+
* - `examined > 0`, no declaration or `.expectNonEmpty()` (the ordinary
|
|
200
|
+
* case — `.expectNonEmpty()` has nothing left to assert once subjects
|
|
201
|
+
* exist): the rule's own violations stand as computed.
|
|
202
|
+
*/
|
|
203
|
+
evidencedViolations() {
|
|
204
|
+
const { violations, examined, sourceEmpty, deadGlob } = this.collectViolations();
|
|
205
|
+
if (examined === 0) {
|
|
206
|
+
if (sourceEmpty === true)
|
|
207
|
+
return [...violations, zeroLoadedSourceViolation(this.facts())];
|
|
208
|
+
if (this._expectEmpty === true)
|
|
209
|
+
return violations;
|
|
210
|
+
if (this._expectEmpty === false)
|
|
211
|
+
return [...violations, unmetExpectNonEmptyViolation(this.facts())];
|
|
212
|
+
if (this.assertsCardinality())
|
|
213
|
+
return violations;
|
|
214
|
+
// A specific dead-glob explanation, when the dialect could compute
|
|
215
|
+
// one, outranks the generic zero-examined message — both name the
|
|
216
|
+
// same underlying fact, and the specific one is strictly more
|
|
217
|
+
// actionable (it names the actual glob and why it can never match).
|
|
218
|
+
if (deadGlob !== undefined)
|
|
219
|
+
return [...violations, deadGlobViolation(this.facts(), deadGlob)];
|
|
220
|
+
return [...violations, zeroExaminedViolation(this.facts())];
|
|
221
|
+
}
|
|
222
|
+
if (this._expectEmpty === true) {
|
|
223
|
+
return [...violations, expiredExpectEmptyViolation(this.facts(), examined)];
|
|
224
|
+
}
|
|
225
|
+
return violations;
|
|
226
|
+
}
|
|
121
227
|
/**
|
|
122
228
|
* Execute the rule with the given severity.
|
|
123
229
|
* `.severity('error')` is equivalent to `.check()`.
|
|
@@ -131,5 +237,143 @@ export class TerminalBuilder {
|
|
|
131
237
|
this.warn();
|
|
132
238
|
}
|
|
133
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* An independent copy of this builder.
|
|
242
|
+
*
|
|
243
|
+
* **A held selection is immutable** (a bug class ported alongside the
|
|
244
|
+
* kernel — plan 0088 Phase 4, matching `ts-archunit`'s own documented
|
|
245
|
+
* "bug 0016"). Before this, every chain method mutated `this` in place:
|
|
246
|
+
*
|
|
247
|
+
* ```ts
|
|
248
|
+
* const repos = classes(p).that().extend('BaseRepository')
|
|
249
|
+
* repos.that().haveNameEndingWith('Legacy').should().notExist().check()
|
|
250
|
+
* repos.should().beExported().check() // silently narrowed to Legacy too
|
|
251
|
+
* ```
|
|
252
|
+
*
|
|
253
|
+
* A held selection reused for a second rule silently inherited the first
|
|
254
|
+
* rule's narrowing, exclusions, and rule id — found live in this repo's own
|
|
255
|
+
* test suite once the ADR-010 evidence gate stopped treating "examined
|
|
256
|
+
* zero units because a stale narrowing ate the selection" as an ordinary,
|
|
257
|
+
* silent pass. `because`/`rule`/`excluding`/`expectEmpty`/`expectNonEmpty`
|
|
258
|
+
* on `TerminalBuilder`, and `that`/`addPredicate`/`addCondition` on
|
|
259
|
+
* `RuleBuilder`, all return a copy rather than mutating `this`.
|
|
260
|
+
*
|
|
261
|
+
* Subclasses with additional fields beyond `TerminalBuilder`'s own MUST
|
|
262
|
+
* override this (see `RuleBuilder.copy()`) and call `super.copy()` first.
|
|
263
|
+
*/
|
|
264
|
+
copy() {
|
|
265
|
+
const clone = shallowClone(this);
|
|
266
|
+
clone.adoptFilterState(this);
|
|
267
|
+
clone._metadata = this._metadata ? { ...this._metadata } : undefined;
|
|
268
|
+
return clone;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Give this builder independent copies of another's filter state.
|
|
272
|
+
*
|
|
273
|
+
* `copy()` shallow-copies every field via `Object.assign`, so without this
|
|
274
|
+
* a copy would share its source's `_exclusions` array and `_silentIndices`
|
|
275
|
+
* set by reference — `.excluding()` on one would silently mutate the
|
|
276
|
+
* other, the exact hazard `copy()` exists to close, one field deeper.
|
|
277
|
+
*/
|
|
278
|
+
adoptFilterState(source) {
|
|
279
|
+
this._exclusions = [...source._exclusions];
|
|
280
|
+
this._silentIndices = new Set(source._silentIndices);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Is this rule's own assertion satisfied by an empty examined set, as a
|
|
284
|
+
* matter of what it asserts (`.notExist()`-shaped), not a caller's
|
|
285
|
+
* `.expectEmpty()` declaration? Default `false` — most families have no
|
|
286
|
+
* such condition and stay covered by the ordinary zero-examined gate.
|
|
287
|
+
* `RuleBuilder` overrides this to check its own `_conditions` against the
|
|
288
|
+
* kernel's `assertsCardinality()` registry (ADR-010, `cardinality.ts`).
|
|
289
|
+
*/
|
|
290
|
+
assertsCardinality() {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* The configuration finding for a rule that asserts nothing — bug 0155.
|
|
296
|
+
*
|
|
297
|
+
* An assertion-less rule — subjects found, nothing asserted about them —
|
|
298
|
+
* cannot fail, so it certifies nothing while reading as coverage.
|
|
299
|
+
*
|
|
300
|
+
* **The guard was unreachable, not merely quiet.** It used to read
|
|
301
|
+
* `_conditions.length === 0 && _phase === 'predicate'`, and `should()` sets
|
|
302
|
+
* the phase to `'condition'`, so for every rule shape the DSL documents it
|
|
303
|
+
* could never fire — the defect passed in total silence, never even reaching
|
|
304
|
+
* the stderr warning it was routed to. Hence no `_phase` term at the call
|
|
305
|
+
* site.
|
|
306
|
+
*
|
|
307
|
+
* **A finding, not a warning**, per ADR-009 rule 1's discriminator: the remedy
|
|
308
|
+
* is not optional. There is no state in which "keeps asserting nothing" is
|
|
309
|
+
* correct — add a condition, or delete the rule. The two rules ADR-009 names
|
|
310
|
+
* as deliberately `warn` (`no-silent-catch`, `no-empty-bodies`) warn *because*
|
|
311
|
+
* they carry suppressible false positives a reader must judge case by case.
|
|
312
|
+
* This carries none.
|
|
313
|
+
*
|
|
314
|
+
* **A declared emptiness expectation is an assertion**, so `_expectEmpty`
|
|
315
|
+
* exempts a rule from this gate. `.expectNonEmpty()` reddens when the corpus
|
|
316
|
+
* it says must never be empty becomes empty; `.expectEmpty()` reddens the day
|
|
317
|
+
* the set it says must stay empty gains a member. Neither lives in
|
|
318
|
+
* `_conditions`. Without that term the gate called a working corpus guard
|
|
319
|
+
* assertion-less and told its author to "add a condition or delete the rule" —
|
|
320
|
+
* both of which destroy the guard — and for `.expectEmpty()` reported two
|
|
321
|
+
* findings for one fault. Found in PR #71's review.
|
|
322
|
+
*
|
|
323
|
+
* **Placed AFTER the zero-examined branch**, so a dead selector still reports
|
|
324
|
+
* as a dead selector. Measured: `resideInFolder('srcc/**')` with no condition
|
|
325
|
+
* reports the dead glob, not this finding. An earlier draft of this docstring
|
|
326
|
+
* (and of the changeset and bug record) claimed the opposite — "reports the
|
|
327
|
+
* missing assertion only" — and justified it as gate-first. Both were wrong:
|
|
328
|
+
* `getElements()` and the predicate filter already ran, so the ordering saves
|
|
329
|
+
* no work, and the behaviour is the reverse. The real precedence is the better
|
|
330
|
+
* one, and is now stated as what it is: this finding fires only when subjects
|
|
331
|
+
* were actually selected.
|
|
332
|
+
*
|
|
333
|
+
* **Exported** rather than private to `RuleBuilder`. Its five siblings
|
|
334
|
+
* (`zeroExaminedViolation`, `deadGlobViolation`, `unmetExpectNonEmptyViolation`,
|
|
335
|
+
* `expiredExpectEmptyViolation`, `zeroLoadedSourceViolation`) have since moved to
|
|
336
|
+
* `vacuity-findings.ts`; this one stayed, because unlike them it is constructed
|
|
337
|
+
* from a builder's own condition list rather than from `RuleFacts` alone. The *detection* has to stay per-builder —
|
|
338
|
+
* `TerminalBuilder` has no `_conditions` — but a private constructor meant
|
|
339
|
+
* `eess-ts`'s slice/schema/resolver builders could not reuse it and were left
|
|
340
|
+
* warning while the kernel's rules failed: the same defect, one DSL, four
|
|
341
|
+
* different answers.
|
|
342
|
+
*
|
|
343
|
+
* `bypassFilters` makes it a **configuration** finding — `error` regardless of
|
|
344
|
+
* `.asSeverity('warn')`, refused by `.excluding()`, skipped by diff and
|
|
345
|
+
* baseline. It reports that the rule's own instrument is broken, not a fault
|
|
346
|
+
* in what was examined, so a filter aimed at the latter must not suppress it.
|
|
347
|
+
*/
|
|
348
|
+
export function assertionLessViolation(ruleId, advice) {
|
|
349
|
+
const remedy = advice ??
|
|
350
|
+
'Add a condition after .should() (a predicate-only method such as ' +
|
|
351
|
+
'areExported/areAsync filters elements, it does not assert), or delete the rule.';
|
|
352
|
+
// `message` is the diagnosis; `suggestion` is the remedy. Keeping them
|
|
353
|
+
// DISTINCT matters: `remedyRepeatsMessage()` suppresses a `Fix:` line only
|
|
354
|
+
// when it is byte-identical to `What:`, so folding the remedy into both
|
|
355
|
+
// printed the whole thing twice — plan 0147's double-print, which an earlier
|
|
356
|
+
// draft of this function reintroduced by appending UNSUPPRESSABLE to a
|
|
357
|
+
// suggestion that already repeated the message.
|
|
358
|
+
const message = `Rule '${ruleId}' selects subjects but asserts nothing about them, so it ` +
|
|
359
|
+
`cannot fail and certifies nothing.`;
|
|
360
|
+
return {
|
|
361
|
+
rule: ruleId,
|
|
362
|
+
element: ruleId,
|
|
363
|
+
file: '',
|
|
364
|
+
line: 0,
|
|
365
|
+
message,
|
|
366
|
+
// The `.expectNonEmpty()` carve-out is named ON PURPOSE. It satisfies this
|
|
367
|
+
// gate (a declared emptiness expectation IS an assertion), so an agent
|
|
368
|
+
// told "add a condition, or delete the rule" and liking neither will find
|
|
369
|
+
// that one token clears the finding — and on a non-empty corpus
|
|
370
|
+
// `.expectNonEmpty()` asserts nothing further. ADR-009 rule 3: a marker an
|
|
371
|
+
// agent can stamp to go green is worse than no marker unless the cost is
|
|
372
|
+
// stated, so state it here rather than let it be discovered.
|
|
373
|
+
suggestion: `${remedy} (\`.expectNonEmpty()\` also satisfies this gate, but asserts ` +
|
|
374
|
+
`only that the corpus is non-empty — it is not a substitute for the ` +
|
|
375
|
+
`condition you meant.) ${UNSUPPRESSABLE}`,
|
|
376
|
+
bypassFilters: true,
|
|
377
|
+
};
|
|
134
378
|
}
|
|
135
379
|
//# sourceMappingURL=terminal-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-builder.js","sourceRoot":"","sources":["../src/terminal-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"terminal-builder.js","sourceRoot":"","sources":["../src/terminal-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The narrowings this codebase needs at its JS-interop edges, in one place.
|
|
3
|
+
*
|
|
4
|
+
* [ADR-005](../../../adr/005-no-any-no-type-assertions.md) forbids `as` and
|
|
5
|
+
* prescribes type guards. The guards existed — `isRecord` was written **twice**,
|
|
6
|
+
* verbatim, in `tsconfig/tsconfig-builder.ts` and `cli/commands/init.ts` — while a
|
|
7
|
+
* third site cast instead of calling either.
|
|
8
|
+
* [ts-archunit Bug 0049](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0049-the-type-assertion-self-check-selected-classes.md).
|
|
9
|
+
*
|
|
10
|
+
* A duplicated predicate is not a style problem in this repo: bug 0044 was a
|
|
11
|
+
* measurement error caused by exactly that, and the fix was to delete the
|
|
12
|
+
* duplicate rather than to test both copies.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* A plain object, indexable by string.
|
|
16
|
+
*
|
|
17
|
+
* Excludes `null`, which `typeof` does not — and excludes **arrays**, which is
|
|
18
|
+
* load-bearing rather than tidy. Both retired copies had `!Array.isArray(value)`
|
|
19
|
+
* and the first draft of this shared one did not, which would have quietly
|
|
20
|
+
* widened `tsconfig-builder`'s deep-compare and `init`'s tsconfig reader to accept
|
|
21
|
+
* an array as a record. Consolidating two copies means picking one, and picking
|
|
22
|
+
* the weaker one is how a refactor becomes a defect.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* A value that can be called with no arguments.
|
|
27
|
+
*
|
|
28
|
+
* Narrower than it looks, and deliberately so: the return type is `unknown`, so
|
|
29
|
+
* every caller has to establish what came back rather than inheriting a claim.
|
|
30
|
+
*
|
|
31
|
+
* This exists because *removing* a cast is not automatically an improvement.
|
|
32
|
+
* Dropping `(exported as () => unknown)()` after a `typeof === 'function'` check
|
|
33
|
+
* left `exported` typed as `Function`, which trades an ADR-005 violation for an
|
|
34
|
+
* `@typescript-eslint/no-unsafe-call` error — the same unchecked call, differently
|
|
35
|
+
* spelled. A predicate narrows to a signature and satisfies both.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isNullaryCallable(value: unknown): value is () => unknown;
|
|
38
|
+
//# sourceMappingURL=type-guards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-guards.d.ts","sourceRoot":"","sources":["../src/type-guards.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,OAAO,CAExE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The narrowings this codebase needs at its JS-interop edges, in one place.
|
|
3
|
+
*
|
|
4
|
+
* [ADR-005](../../../adr/005-no-any-no-type-assertions.md) forbids `as` and
|
|
5
|
+
* prescribes type guards. The guards existed — `isRecord` was written **twice**,
|
|
6
|
+
* verbatim, in `tsconfig/tsconfig-builder.ts` and `cli/commands/init.ts` — while a
|
|
7
|
+
* third site cast instead of calling either.
|
|
8
|
+
* [ts-archunit Bug 0049](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0049-the-type-assertion-self-check-selected-classes.md).
|
|
9
|
+
*
|
|
10
|
+
* A duplicated predicate is not a style problem in this repo: bug 0044 was a
|
|
11
|
+
* measurement error caused by exactly that, and the fix was to delete the
|
|
12
|
+
* duplicate rather than to test both copies.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* A plain object, indexable by string.
|
|
16
|
+
*
|
|
17
|
+
* Excludes `null`, which `typeof` does not — and excludes **arrays**, which is
|
|
18
|
+
* load-bearing rather than tidy. Both retired copies had `!Array.isArray(value)`
|
|
19
|
+
* and the first draft of this shared one did not, which would have quietly
|
|
20
|
+
* widened `tsconfig-builder`'s deep-compare and `init`'s tsconfig reader to accept
|
|
21
|
+
* an array as a record. Consolidating two copies means picking one, and picking
|
|
22
|
+
* the weaker one is how a refactor becomes a defect.
|
|
23
|
+
*/
|
|
24
|
+
export function isRecord(value) {
|
|
25
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A value that can be called with no arguments.
|
|
29
|
+
*
|
|
30
|
+
* Narrower than it looks, and deliberately so: the return type is `unknown`, so
|
|
31
|
+
* every caller has to establish what came back rather than inheriting a claim.
|
|
32
|
+
*
|
|
33
|
+
* This exists because *removing* a cast is not automatically an improvement.
|
|
34
|
+
* Dropping `(exported as () => unknown)()` after a `typeof === 'function'` check
|
|
35
|
+
* left `exported` typed as `Function`, which trades an ADR-005 violation for an
|
|
36
|
+
* `@typescript-eslint/no-unsafe-call` error — the same unchecked call, differently
|
|
37
|
+
* spelled. A predicate narrows to a signature and satisfies both.
|
|
38
|
+
*/
|
|
39
|
+
export function isNullaryCallable(value) {
|
|
40
|
+
return typeof value === 'function';
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=type-guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-guards.js","sourceRoot":"","sources":["../src/type-guards.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,OAAO,KAAK,KAAK,UAAU,CAAA;AACpC,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one sentence that tells a reader a configuration finding has no escape
|
|
3
|
+
* hatch — [ADR-009](../../../adr/009-agent-first-failure-surfaces.md) rule 3.
|
|
4
|
+
*
|
|
5
|
+
* Stated on the finding rather than inside each remedy so the per-shape advice
|
|
6
|
+
* stays one sentence each and this stays one sentence in one place. Measured
|
|
7
|
+
* before it existed: a reader given only the remedy tries `.asSeverity('warn')`,
|
|
8
|
+
* `.excluding()`, the baseline and `--changed` — four CI cycles — because
|
|
9
|
+
* nothing told them those were refused.
|
|
10
|
+
*
|
|
11
|
+
* ## Why it lives here rather than inline
|
|
12
|
+
*
|
|
13
|
+
* It was written twice, inline, at two sites in `terminal-builder.ts`, and
|
|
14
|
+
* prettier had already split the second copy across lines. Two copies of a
|
|
15
|
+
* sentence that enumerates a list is the shape that goes stale: the code grew a
|
|
16
|
+
* sixth refusing surface and both copies still named five.
|
|
17
|
+
*
|
|
18
|
+
* ## The list is a claim, so it is guarded
|
|
19
|
+
*
|
|
20
|
+
* `tests/core/unsuppressable-sentence.test.ts` derives the refusing mechanisms
|
|
21
|
+
* **behaviourally** — one probe each — and compares that set against the names
|
|
22
|
+
* parsed out of this string. Disagreement fails in both directions: naming a
|
|
23
|
+
* mechanism that does not refuse (bug 0017's shape) and refusing by a mechanism
|
|
24
|
+
* this does not name. The second is what actually happened.
|
|
25
|
+
*
|
|
26
|
+
* The sixth surface is the inline `// eess-exclude` comment, refused in
|
|
27
|
+
* `execute-rule.ts` by the `v.bypassFilters === true ||` clause. It was omitted
|
|
28
|
+
* while it was hard to reach; [ts-archunit bug 0041](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0041-an-exclusion-comment-is-a-no-op-for-most-conditions.md)
|
|
29
|
+
* made inline comments work for every condition family, so an agent reading a
|
|
30
|
+
* five-item list and inferring exhaustiveness now reaches for the one mechanism
|
|
31
|
+
* the sentence forgot to mention.
|
|
32
|
+
*/
|
|
33
|
+
export declare const UNSUPPRESSABLE: string;
|
|
34
|
+
/**
|
|
35
|
+
* The declaration half, appended ONLY by the zero-examined producer.
|
|
36
|
+
*
|
|
37
|
+
* It used to live in {@link UNSUPPRESSABLE}, which every configuration finding
|
|
38
|
+
* carries — so it was stapled to findings a declaration cannot settle. Measured:
|
|
39
|
+
* on the assertion-gate finding ("this detector has no pattern") and on a dead
|
|
40
|
+
* selector glob, adding `.expectEmpty()` changes nothing, because both are
|
|
41
|
+
* decided before any declaration is consulted. The reader declares, re-runs, and
|
|
42
|
+
* gets the identical failure with the identical advice — ADR-009 rule 2's loop,
|
|
43
|
+
* introduced by the sentence written to close one.
|
|
44
|
+
*
|
|
45
|
+
* On the dead-glob kind it was worse than a no-op: there `overrides: { id: 'off' }`
|
|
46
|
+
* IS the working exit and declaring is not, so the message steered the reader off
|
|
47
|
+
* the only door that opens.
|
|
48
|
+
*
|
|
49
|
+
* Zero-examined is the one kind a declaration actually settles.
|
|
50
|
+
*/
|
|
51
|
+
export declare const DECLARE_INSTEAD: string;
|
|
52
|
+
/** The mechanism names the sentence claims to refuse, for the parity guard. */
|
|
53
|
+
export declare const UNSUPPRESSABLE_MECHANISMS: readonly [".warn()", ".asSeverity('warn')", ".excluding()", "// eess-exclude", "baseline", "diff-aware"];
|
|
54
|
+
//# sourceMappingURL=unsuppressable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsuppressable.d.ts","sourceRoot":"","sources":["../src/unsuppressable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,cAAc,QAe6D,CAAA;AAExF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,eAAe,QAEqC,CAAA;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,0GAO5B,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one sentence that tells a reader a configuration finding has no escape
|
|
3
|
+
* hatch — [ADR-009](../../../adr/009-agent-first-failure-surfaces.md) rule 3.
|
|
4
|
+
*
|
|
5
|
+
* Stated on the finding rather than inside each remedy so the per-shape advice
|
|
6
|
+
* stays one sentence each and this stays one sentence in one place. Measured
|
|
7
|
+
* before it existed: a reader given only the remedy tries `.asSeverity('warn')`,
|
|
8
|
+
* `.excluding()`, the baseline and `--changed` — four CI cycles — because
|
|
9
|
+
* nothing told them those were refused.
|
|
10
|
+
*
|
|
11
|
+
* ## Why it lives here rather than inline
|
|
12
|
+
*
|
|
13
|
+
* It was written twice, inline, at two sites in `terminal-builder.ts`, and
|
|
14
|
+
* prettier had already split the second copy across lines. Two copies of a
|
|
15
|
+
* sentence that enumerates a list is the shape that goes stale: the code grew a
|
|
16
|
+
* sixth refusing surface and both copies still named five.
|
|
17
|
+
*
|
|
18
|
+
* ## The list is a claim, so it is guarded
|
|
19
|
+
*
|
|
20
|
+
* `tests/core/unsuppressable-sentence.test.ts` derives the refusing mechanisms
|
|
21
|
+
* **behaviourally** — one probe each — and compares that set against the names
|
|
22
|
+
* parsed out of this string. Disagreement fails in both directions: naming a
|
|
23
|
+
* mechanism that does not refuse (bug 0017's shape) and refusing by a mechanism
|
|
24
|
+
* this does not name. The second is what actually happened.
|
|
25
|
+
*
|
|
26
|
+
* The sixth surface is the inline `// eess-exclude` comment, refused in
|
|
27
|
+
* `execute-rule.ts` by the `v.bypassFilters === true ||` clause. It was omitted
|
|
28
|
+
* while it was hard to reach; [ts-archunit bug 0041](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0041-an-exclusion-comment-is-a-no-op-for-most-conditions.md)
|
|
29
|
+
* made inline comments work for every condition family, so an agent reading a
|
|
30
|
+
* five-item list and inferring exhaustiveness now reaches for the one mechanism
|
|
31
|
+
* the sentence forgot to mention.
|
|
32
|
+
*/
|
|
33
|
+
export const UNSUPPRESSABLE = "This finding cannot be suppressed: not by .warn(), .asSeverity('warn'), " +
|
|
34
|
+
'.excluding(), a `// eess-exclude` comment, a baseline, or diff-aware mode. ' +
|
|
35
|
+
// The seventh thing an agent tries, and the one you least want it to learn.
|
|
36
|
+
// Measured: `recommended(p)` over a mis-scoped project reports four findings,
|
|
37
|
+
// each carrying the sentence above; adding `overrides: { id: 'off' }` reports
|
|
38
|
+
// three. One config line removed a finding that had just told the reader, in a
|
|
39
|
+
// sentence guarded by a parity test, that it could not be removed.
|
|
40
|
+
//
|
|
41
|
+
// Naming it here rather than leaving it discoverable by elimination: an agent
|
|
42
|
+
// that hits six walls and then finds `'off'` documented in `docs/presets.md`
|
|
43
|
+
// will stamp it, which is ADR-009 rule 1's trained-suppression dynamic produced
|
|
44
|
+
// by our own gate. It is NOT added to `UNSUPPRESSABLE_MECHANISMS`, because that
|
|
45
|
+
// list is the set the parity guard proves DO refuse — and this one does not.
|
|
46
|
+
"A preset's overrides: { id: 'off' } does remove it, by deleting the rule " +
|
|
47
|
+
'entirely — that is not a suppression, it is a permanent decision that never expires.';
|
|
48
|
+
/**
|
|
49
|
+
* The declaration half, appended ONLY by the zero-examined producer.
|
|
50
|
+
*
|
|
51
|
+
* It used to live in {@link UNSUPPRESSABLE}, which every configuration finding
|
|
52
|
+
* carries — so it was stapled to findings a declaration cannot settle. Measured:
|
|
53
|
+
* on the assertion-gate finding ("this detector has no pattern") and on a dead
|
|
54
|
+
* selector glob, adding `.expectEmpty()` changes nothing, because both are
|
|
55
|
+
* decided before any declaration is consulted. The reader declares, re-runs, and
|
|
56
|
+
* gets the identical failure with the identical advice — ADR-009 rule 2's loop,
|
|
57
|
+
* introduced by the sentence written to close one.
|
|
58
|
+
*
|
|
59
|
+
* On the dead-glob kind it was worse than a no-op: there `overrides: { id: 'off' }`
|
|
60
|
+
* IS the working exit and declaring is not, so the message steered the reader off
|
|
61
|
+
* the only door that opens.
|
|
62
|
+
*
|
|
63
|
+
* Zero-examined is the one kind a declaration actually settles.
|
|
64
|
+
*/
|
|
65
|
+
export const DECLARE_INSTEAD = 'If the rule genuinely has nothing to check, declare that instead — a declaration ' +
|
|
66
|
+
'is an assertion that expires, where deleting the rule is not.';
|
|
67
|
+
/** The mechanism names the sentence claims to refuse, for the parity guard. */
|
|
68
|
+
export const UNSUPPRESSABLE_MECHANISMS = [
|
|
69
|
+
'.warn()',
|
|
70
|
+
".asSeverity('warn')",
|
|
71
|
+
'.excluding()',
|
|
72
|
+
'// eess-exclude',
|
|
73
|
+
'baseline',
|
|
74
|
+
'diff-aware',
|
|
75
|
+
];
|
|
76
|
+
//# sourceMappingURL=unsuppressable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsuppressable.js","sourceRoot":"","sources":["../src/unsuppressable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,cAAc,GACzB,0EAA0E;IAC1E,6EAA6E;IAC7E,4EAA4E;IAC5E,8EAA8E;IAC9E,8EAA8E;IAC9E,+EAA+E;IAC/E,mEAAmE;IACnE,EAAE;IACF,8EAA8E;IAC9E,6EAA6E;IAC7E,gFAAgF;IAChF,gFAAgF;IAChF,6EAA6E;IAC7E,2EAA2E;IAC3E,sFAAsF,CAAA;AAExF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,eAAe,GAC1B,mFAAmF;IACnF,+DAA+D,CAAA;AAEjE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,SAAS;IACT,qBAAqB;IACrB,cAAc;IACd,iBAAiB;IACjB,UAAU;IACV,YAAY;CACJ,CAAA"}
|