@nielspeter/eess 0.2.2 → 0.5.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 +97 -4
- 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-generator.d.ts +9 -0
- package/dist/baseline-generator.d.ts.map +1 -1
- package/dist/baseline-generator.js +9 -0
- package/dist/baseline-generator.js.map +1 -1
- 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 +80 -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/cli-config.d.ts +59 -0
- package/dist/cli-config.d.ts.map +1 -0
- package/dist/cli-config.js +84 -0
- package/dist/cli-config.js.map +1 -0
- package/dist/collect-result.d.ts +94 -0
- package/dist/collect-result.d.ts.map +1 -0
- package/dist/collect-result.js +142 -0
- package/dist/collect-result.js.map +1 -0
- package/dist/combinators.d.ts +14 -0
- package/dist/combinators.d.ts.map +1 -1
- package/dist/combinators.js +53 -16
- package/dist/combinators.js.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 +43 -0
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +48 -1
- package/dist/condition.js.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 +57 -5
- package/dist/correspondence.d.ts.map +1 -1
- package/dist/correspondence.js +74 -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 +101 -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 -1
- package/dist/diff-aware.d.ts.map +1 -1
- package/dist/diff-aware.js +39 -9
- 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/emitter-findings.d.ts +167 -0
- package/dist/emitter-findings.d.ts.map +1 -0
- package/dist/emitter-findings.js +237 -0
- package/dist/emitter-findings.js.map +1 -0
- package/dist/errors.d.ts +73 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +93 -0
- package/dist/errors.js.map +1 -1
- package/dist/exclusion-comments.d.ts +36 -1
- package/dist/exclusion-comments.d.ts.map +1 -1
- package/dist/exclusion-comments.js +185 -37
- package/dist/exclusion-comments.js.map +1 -1
- package/dist/execute-rule.d.ts +11 -1
- package/dist/execute-rule.d.ts.map +1 -1
- package/dist/execute-rule.js +220 -11
- package/dist/execute-rule.js.map +1 -1
- package/dist/file-walk.d.ts +22 -0
- package/dist/file-walk.d.ts.map +1 -0
- package/dist/file-walk.js +51 -0
- package/dist/file-walk.js.map +1 -0
- 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 +96 -1
- package/dist/format-json.d.ts.map +1 -1
- package/dist/format-json.js +94 -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 +41 -19
- 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 +18 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +55 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +92 -0
- package/dist/internal.js.map +1 -0
- package/dist/mask-non-comment.d.ts +49 -0
- package/dist/mask-non-comment.d.ts.map +1 -0
- package/dist/mask-non-comment.js +261 -0
- package/dist/mask-non-comment.js.map +1 -0
- 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 +85 -0
- package/dist/owns-empty-discovery.js.map +1 -0
- package/dist/path-suffix.d.ts +57 -0
- package/dist/path-suffix.d.ts.map +1 -0
- package/dist/path-suffix.js +73 -0
- package/dist/path-suffix.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 +45 -0
- package/dist/predicate.d.ts.map +1 -1
- package/dist/predicate.js +20 -1
- package/dist/predicate.js.map +1 -1
- package/dist/preset-dispatch.d.ts +6 -15
- package/dist/preset-dispatch.d.ts.map +1 -1
- package/dist/preset-dispatch.js +35 -17
- 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 +42 -12
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +190 -14
- package/dist/report.js.map +1 -1
- package/dist/rule-builder-like.d.ts +17 -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 +124 -84
- package/dist/rule-builder.d.ts.map +1 -1
- package/dist/rule-builder.js +230 -181
- package/dist/rule-builder.js.map +1 -1
- package/dist/rule-description.d.ts +83 -1
- package/dist/rule-description.d.ts.map +1 -1
- package/dist/rule-description.js +75 -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 +45 -0
- package/dist/selection-memo.d.ts.map +1 -0
- package/dist/selection-memo.js +66 -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/silent-exclusion.d.ts +12 -0
- package/dist/silent-exclusion.d.ts.map +1 -1
- package/dist/silent-exclusion.js +22 -0
- package/dist/silent-exclusion.js.map +1 -1
- 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 +224 -10
- package/dist/terminal-builder.d.ts.map +1 -1
- package/dist/terminal-builder.js +336 -46
- 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 +49 -0
- package/dist/vacuity-findings.d.ts.map +1 -0
- package/dist/vacuity-findings.js +94 -0
- package/dist/vacuity-findings.js.map +1 -0
- package/dist/violation.d.ts +173 -4
- package/dist/violation.d.ts.map +1 -1
- package/dist/violation.js +262 -1
- package/dist/violation.js.map +1 -1
- package/dist/watch.d.ts +71 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +129 -0
- package/dist/watch.js.map +1 -0
- package/package.json +6 -1
package/dist/report.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { collectResult, hasEvidence } from './collect-result.js';
|
|
2
|
+
import { isEmitterFinding, noReceiptViolation, sourceEmptyViolation, passWithoutEvidenceViolation, expiredDeclarationViolation, contradictoryEvidenceViolation, } from './emitter-findings.js';
|
|
1
3
|
import { ArchRuleError } from './errors.js';
|
|
2
4
|
import { formatViolations } from './format.js';
|
|
3
5
|
import { formatViolationsJson } from './format-json.js';
|
|
@@ -9,37 +11,211 @@ import { formatViolationsGitHub } from './format-github.js';
|
|
|
9
11
|
* path) both delegate here, so the two reporting paths can no longer diverge
|
|
10
12
|
* (plan 0070). Emits nothing for an empty set.
|
|
11
13
|
*/
|
|
14
|
+
let violationsEmitted = 0;
|
|
15
|
+
/**
|
|
16
|
+
* How many violations this module has actually WRITTEN, ever.
|
|
17
|
+
*
|
|
18
|
+
* Read as a delta by a caller that aggregates reporting, to answer one question it
|
|
19
|
+
* cannot otherwise answer: *did something emit while I was loading that module?*
|
|
20
|
+
*
|
|
21
|
+
* The alternative a caller might reach for — counting the writes it SUPPRESSED and
|
|
22
|
+
* inferring "then nothing was written" — is a double negative and is unsound: a
|
|
23
|
+
* module that suppresses one terminal and leaks through another satisfies it while
|
|
24
|
+
* leaking. Measured on `eess-ts`, which shipped that inference and fell into
|
|
25
|
+
* exactly that case. Count emissions, not silences.
|
|
26
|
+
*/
|
|
27
|
+
export function violationsEmittedCount() {
|
|
28
|
+
return violationsEmitted;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The evidence gate both emitters share — ADR-014 §1, §4.
|
|
32
|
+
*
|
|
33
|
+
* Produced BEFORE the delivery mode is chosen, so the finding is in the value
|
|
34
|
+
* returned under `return`, rides the throw under `throw`, and is in the text
|
|
35
|
+
* written under `warn`. A contract that binds only one door does not bind.
|
|
36
|
+
*
|
|
37
|
+
* A value that already carries a finding passes through untouched (§4): a
|
|
38
|
+
* terminal that examined nothing has already named its own cause, and "you
|
|
39
|
+
* constructed nothing" beside "your glob is dead" is a false second remedy.
|
|
40
|
+
*/
|
|
41
|
+
function withEvidenceGate(violations) {
|
|
42
|
+
// Deliberately looser than the exported signatures. The TYPE refuses a bare
|
|
43
|
+
// array (D1/D2); this guard is for the untyped JavaScript caller the type
|
|
44
|
+
// cannot reach — ADR-014's stated residual, and the field failure in
|
|
45
|
+
// proposal 009 was exactly that consumer.
|
|
46
|
+
if (!hasEvidence(violations)) {
|
|
47
|
+
return collectResult([...violations, noReceiptViolation()], { examined: 0 });
|
|
48
|
+
}
|
|
49
|
+
// Expiry FIRST, and before the `examined > 0` exit — ADR-014 §3. A declaration
|
|
50
|
+
// that cannot be contradicted is not an assertion, which is the whole reason
|
|
51
|
+
// §3's amendment refuses to infer one from a config. Checked even when real
|
|
52
|
+
// violations are present: the declaration is wrong independently of them.
|
|
53
|
+
if (violations.declaredEmpty === true && violations.examined > 0) {
|
|
54
|
+
return collectResult([...violations, expiredDeclarationViolation(violations.examined)], {
|
|
55
|
+
examined: violations.examined,
|
|
56
|
+
sourceEmpty: violations.sourceEmpty,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// The same check for the third state — ADR-014 §3, added after review found
|
|
60
|
+
// `notRun` shipped as a suppressor with no falsifier while `declaredEmpty` had
|
|
61
|
+
// one. A rule that never ran examined nothing and found nothing; either half
|
|
62
|
+
// present contradicts the flag, and BOTH are read because either alone would
|
|
63
|
+
// leave the other as an unchecked claim.
|
|
64
|
+
//
|
|
65
|
+
// Before the two exits below, deliberately: `examined > 0` is precisely the
|
|
66
|
+
// state that contradicts `notRun`, so checking after them would make the
|
|
67
|
+
// finding unreachable — the mistake this ADR's §2 warns about in its own
|
|
68
|
+
// implementation notes.
|
|
69
|
+
if (violations.notRun === true && (violations.examined > 0 || violations.length > 0)) {
|
|
70
|
+
return collectResult([...violations, contradictoryEvidenceViolation(violations.examined, violations.length)], { examined: violations.examined, sourceEmpty: violations.sourceEmpty });
|
|
71
|
+
}
|
|
72
|
+
// **`sourceEmpty` beside real evidence is a contradiction, like its siblings.**
|
|
73
|
+
// A source that loaded nothing cannot have yielded units to examine — the flag
|
|
74
|
+
// is set precisely when `examined === 0`. Checked before the two exits below,
|
|
75
|
+
// for the same reason the `notRun` check above is: after them it would be
|
|
76
|
+
// unreachable.
|
|
77
|
+
//
|
|
78
|
+
// Without this, `sourceEmpty` was the one flag in the vocabulary that could
|
|
79
|
+
// quiet a zero and never be contradicted — `notRun` beside evidence is
|
|
80
|
+
// `emitter/contradictory-evidence`, `declaredEmpty` beside evidence is
|
|
81
|
+
// `emitter/expired-declaration`, and `{ examined: 900, sourceEmpty: true }` was
|
|
82
|
+
// silent. That asymmetry is what ADR-014's "every flag that can quiet a zero
|
|
83
|
+
// can be contradicted" row exists to refuse, and promoting `sourceEmpty` to a
|
|
84
|
+
// verdict-changing flag with its own id is what put it in scope.
|
|
85
|
+
if (violations.sourceEmpty === true && violations.examined > 0) {
|
|
86
|
+
return collectResult([
|
|
87
|
+
...violations,
|
|
88
|
+
contradictoryEvidenceViolation(violations.examined, violations.length, 'sourceEmpty'),
|
|
89
|
+
], { examined: violations.examined });
|
|
90
|
+
}
|
|
91
|
+
if (violations.length > 0)
|
|
92
|
+
return violations;
|
|
93
|
+
if (violations.examined > 0)
|
|
94
|
+
return violations;
|
|
95
|
+
// **An empty source outranks any declaration, and names the source** — ADR-014
|
|
96
|
+
// §4, and ADR-010 §3's own `gated` row ("Zero loaded source files outranks any
|
|
97
|
+
// `.expectEmpty()` declaration") which the terminal has always honoured.
|
|
98
|
+
//
|
|
99
|
+
// This branch is checked BEFORE `declaredEmpty` and `notRun`, and that ordering
|
|
100
|
+
// is the whole point. The comment that used to sit here said `sourceEmpty` does
|
|
101
|
+
// not legitimise a zero — and the next line returned green on `declaredEmpty`
|
|
102
|
+
// without ever reading it. Measured:
|
|
103
|
+
// `finishPreset(collectResult([], { examined: 0, sourceEmpty: true, declaredEmpty: true }))`
|
|
104
|
+
// was GREEN. A hand-assembled receipt claiming an empty source and declaring it
|
|
105
|
+
// away is precisely the escape hatch that comment claimed this ADR had closed.
|
|
106
|
+
//
|
|
107
|
+
// A terminal that loaded nothing already carries `zeroLoadedSourceViolation` and
|
|
108
|
+
// exits at the `length > 0` line above, so this fires for the hand-assembled
|
|
109
|
+
// receipt — the population ADR-014's gate exists for.
|
|
110
|
+
if (violations.sourceEmpty === true) {
|
|
111
|
+
return collectResult([...violations, sourceEmptyViolation()], {
|
|
112
|
+
examined: violations.examined,
|
|
113
|
+
sourceEmpty: true,
|
|
114
|
+
// The input's own declarations are carried through, not dropped. They do
|
|
115
|
+
// not rescue the verdict — that is the precedence this branch enforces —
|
|
116
|
+
// but a downstream reader still needs to know they were made, and the
|
|
117
|
+
// sibling branches above preserve what they were handed for the same
|
|
118
|
+
// reason.
|
|
119
|
+
declaredEmpty: violations.declaredEmpty,
|
|
120
|
+
notRun: violations.notRun,
|
|
121
|
+
deadGlob: violations.deadGlob,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (violations.declaredEmpty === true)
|
|
125
|
+
return violations;
|
|
126
|
+
// `notRun` legitimises a zero here for the same reason `declaredEmpty` does,
|
|
127
|
+
// and for the reason the MERGE already exempts it: a rule turned off examined
|
|
128
|
+
// nothing and found nothing, and reporting that as a vacuous pass is the
|
|
129
|
+
// "one disabled rule reddens a whole preset" failure `notRun` was added to
|
|
130
|
+
// prevent (measured on `adrEnforcement` with `adr/valid-tiers` off).
|
|
131
|
+
//
|
|
132
|
+
// **The two doors disagreed until this line.** `mergeCollectResults` exempts a
|
|
133
|
+
// `notRun` member from its dead filter; this gate had no such branch, so the
|
|
134
|
+
// per-builder doors judged an off rule alone and reddened it. Measured:
|
|
135
|
+
// `checkAll([healthy, off])` was GREEN while `eess-ts check` over the same two
|
|
136
|
+
// builders reported `1 of 2 rules failing`. Same receipts, opposite verdicts,
|
|
137
|
+
// decided by which door you walked through — and `check.ts` claimed per-builder
|
|
138
|
+
// gating was "strictly stronger, not weaker", which is how a false positive is
|
|
139
|
+
// manufactured.
|
|
140
|
+
//
|
|
141
|
+
// It is not a mute button: a `notRun` beside a non-zero `examined` or any
|
|
142
|
+
// violation is `emitter/contradictory-evidence`, checked above and reached
|
|
143
|
+
// before this line. And the all-off case still reports, because the merged
|
|
144
|
+
// receipt carries no `notRun` — so a preset whose every member is off sums to
|
|
145
|
+
// zero examined, undeclared, and reds exactly as ADR-014 §3 requires.
|
|
146
|
+
if (violations.notRun === true)
|
|
147
|
+
return violations;
|
|
148
|
+
return collectResult([passWithoutEvidenceViolation()], { examined: violations.examined });
|
|
149
|
+
}
|
|
12
150
|
export function reportViolations(violations, options = {}) {
|
|
13
|
-
|
|
14
|
-
|
|
151
|
+
const gated = withEvidenceGate(violations);
|
|
152
|
+
if (gated.length === 0)
|
|
153
|
+
return gated;
|
|
154
|
+
violationsEmitted += gated.length;
|
|
15
155
|
const format = options.format ?? 'terminal';
|
|
16
156
|
if (format === 'json') {
|
|
17
|
-
process.stdout.write(formatViolationsJson(
|
|
157
|
+
process.stdout.write(formatViolationsJson(gated, options.reason) + '\n');
|
|
18
158
|
}
|
|
19
159
|
else if (format === 'github') {
|
|
20
|
-
process.stdout.write(formatViolationsGitHub(
|
|
160
|
+
process.stdout.write(formatViolationsGitHub(gated, 'error') + '\n');
|
|
21
161
|
}
|
|
22
162
|
else {
|
|
23
163
|
// Rich text to stderr — test runners show the plain-text Error message,
|
|
24
164
|
// while stderr carries the colorized Why/Fix/Docs output.
|
|
25
|
-
process.stderr.write(formatViolations(
|
|
165
|
+
process.stderr.write(formatViolations(gated, options.reason) + '\n');
|
|
166
|
+
}
|
|
167
|
+
// ADR-014 §5: a bare `reportViolations` hands nothing back that a caller must
|
|
168
|
+
// act on, so a printed unsuppressable finding above a zero exit is the lie by
|
|
169
|
+
// another name. The gate's own finding escalates to a throw here.
|
|
170
|
+
if (gated.some((v) => v.bypassFilters === true && isEmitterFinding(v))) {
|
|
171
|
+
throw new ArchRuleError(gated, options.reason);
|
|
26
172
|
}
|
|
173
|
+
return gated;
|
|
27
174
|
}
|
|
28
175
|
/**
|
|
29
176
|
* Finish a preset: deliver its aggregated violations per the requested mode
|
|
30
|
-
* (plan 0070). `throw` (default) emits then throws
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
177
|
+
* (plan 0070). `throw` (default) emits then throws; `return` hands the
|
|
178
|
+
* violations back untouched (the caller owns emission and control flow);
|
|
179
|
+
* `warn` emits without throwing. Always returns the violations, so a caller can
|
|
180
|
+
* inspect them regardless of mode.
|
|
34
181
|
*/
|
|
35
182
|
export function finishPreset(violations, options = {}) {
|
|
36
183
|
const mode = options.report ?? 'throw';
|
|
184
|
+
// A caller's declaration reaches the gate ON the receipt (ADR-014 §3), never
|
|
185
|
+
// as a delivery option the gate reads separately — one boolean beside a sum
|
|
186
|
+
// cannot be weighed against that sum's parts.
|
|
187
|
+
const declared = options.expectEmpty === true && !violations.declaredEmpty
|
|
188
|
+
? collectResult(violations, {
|
|
189
|
+
examined: violations.examined,
|
|
190
|
+
sourceEmpty: violations.sourceEmpty,
|
|
191
|
+
deadGlob: violations.deadGlob,
|
|
192
|
+
declaredEmpty: true,
|
|
193
|
+
})
|
|
194
|
+
: violations;
|
|
195
|
+
// The gate runs before the mode is read, so every door carries the finding.
|
|
196
|
+
const gated = withEvidenceGate(declared);
|
|
37
197
|
if (mode === 'return')
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
198
|
+
return gated;
|
|
199
|
+
const emitterFinding = gated.some((v) => isEmitterFinding(v));
|
|
200
|
+
if (gated.length > 0) {
|
|
201
|
+
violationsEmitted += gated.length;
|
|
202
|
+
const format = options.format ?? 'terminal';
|
|
203
|
+
if (format === 'json') {
|
|
204
|
+
process.stdout.write(formatViolationsJson(gated, options.reason) + '\n');
|
|
205
|
+
}
|
|
206
|
+
else if (format === 'github') {
|
|
207
|
+
process.stdout.write(formatViolationsGitHub(gated, 'error') + '\n');
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
process.stderr.write(formatViolations(gated, options.reason) + '\n');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// §5 again: `warn` prints without failing for ordinary violations, but an
|
|
214
|
+
// unsuppressable configuration finding printed above a zero exit is exactly
|
|
215
|
+
// the silent green this ADR closes.
|
|
216
|
+
if ((mode === 'throw' && gated.length > 0) || emitterFinding) {
|
|
217
|
+
throw new ArchRuleError(gated, options.reason);
|
|
42
218
|
}
|
|
43
|
-
return
|
|
219
|
+
return gated;
|
|
44
220
|
}
|
|
45
221
|
//# sourceMappingURL=report.js.map
|
package/dist/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CollectResult } from './collect-result.js';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal shape the CLI runner and presets collect violations from: anything
|
|
4
|
+
* exposing a non-throwing, severity-stamped `.violations()`. Lives in core (not
|
|
5
|
+
* `cli/`) so presets can return `RuleBuilderLike[]` without depending on CLI
|
|
6
|
+
* infrastructure.
|
|
7
|
+
*
|
|
8
|
+
* **The single member carries the evidence** — plan 0235's D1. An accessor
|
|
9
|
+
* beside it could be walked around: a caller keeps calling `violations()`,
|
|
10
|
+
* never asks for the count, and the evidence is optional again by another
|
|
11
|
+
* route. One member, and it hands back the receipt, so a rule file that cannot
|
|
12
|
+
* say what it examined does not compile.
|
|
13
|
+
*/
|
|
14
|
+
export interface RuleBuilderLike {
|
|
15
|
+
violations: () => CollectResult;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=rule-builder-like.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-builder-like.d.ts","sourceRoot":"","sources":["../src/rule-builder-like.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,aAAa,CAAA;CAChC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-builder-like.js","sourceRoot":"","sources":["../src/rule-builder-like.ts"],"names":[],"mappings":""}
|
package/dist/rule-builder.d.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import type { Predicate } from './predicate.js';
|
|
2
2
|
import type { Condition, ConditionContext } from './condition.js';
|
|
3
|
-
import type { ArchViolation } from './violation.js';
|
|
4
|
-
import type { CheckOptions } from './check-options.js';
|
|
5
|
-
import type { RuleMetadata } from './rule-metadata.js';
|
|
6
3
|
import type { RuleDescription } from './rule-description.js';
|
|
7
|
-
import type { SilentExclusion } from './silent-exclusion.js';
|
|
8
4
|
import type { Selection, ElementInfo } from './correspondence.js';
|
|
5
|
+
import type { GlobNode } from './glob-site.js';
|
|
6
|
+
import { TerminalBuilder, type CollectResult } from './terminal-builder.js';
|
|
7
|
+
/**
|
|
8
|
+
* The actual walk `RuleBuilder.globs()` delegates to — a free function
|
|
9
|
+
* rather than a method body, to keep the class itself under this repo's own
|
|
10
|
+
* 300-line class-length gate (`arch.internal.rules.ts`).
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The globs a rule declares, stamped with where each came from.
|
|
14
|
+
*
|
|
15
|
+
* Exported (as family plumbing) so `eess-ts` can call it instead of keeping its
|
|
16
|
+
* own copy — they were 94% similar, and the difference was not cosmetic: only
|
|
17
|
+
* the eess-ts copy honoured `originLabel`. The kernel's `Predicate` has carried
|
|
18
|
+
* that field all along and this function ignored it, so a preset's label was
|
|
19
|
+
* dropped for `eess-md`, `eess-mermaid` and `eess-gherkin`. Found by unifying.
|
|
20
|
+
*/
|
|
21
|
+
export declare function declaredGlobsOf<T>(predicates: readonly Predicate<T>[], conditions: readonly Condition<T>[]): GlobNode[];
|
|
9
22
|
/**
|
|
10
23
|
* Abstract base class for all rule builders.
|
|
11
24
|
*
|
|
@@ -15,20 +28,35 @@ import type { Selection, ElementInfo } from './correspondence.js';
|
|
|
15
28
|
* 3. Add condition methods that call `addCondition()`
|
|
16
29
|
*
|
|
17
30
|
* The builder accumulates predicates and conditions. Nothing executes
|
|
18
|
-
* until a terminal method (`.check()`, `.warn()`, `.severity()`) is called
|
|
31
|
+
* until a terminal method (`.check()`, `.warn()`, `.severity()`) is called —
|
|
32
|
+
* inherited from {@link TerminalBuilder} (plan 0088 Phase 4), which also
|
|
33
|
+
* supplies `because`/`rule`/`excluding`/`violations`/`expectEmpty`/
|
|
34
|
+
* `expectNonEmpty` and the ADR-010 evidence gate. `RuleBuilder` keeps its own
|
|
35
|
+
* `<T, P>` two-param generic — `P` is the dialect's project type, kept
|
|
36
|
+
* separate from `TerminalBuilder` (which has no project concept, since
|
|
37
|
+
* `correspondence()`, `SmellBuilder`, and the graphql builders don't share a
|
|
38
|
+
* uniform notion of "one project") — every builder across all five dialects
|
|
39
|
+
* depends on this exact two-param signature, so it is not replaced by the
|
|
40
|
+
* fold, only extended.
|
|
19
41
|
*/
|
|
20
|
-
export declare abstract class RuleBuilder<T, P = unknown> {
|
|
42
|
+
export declare abstract class RuleBuilder<T, P = unknown> extends TerminalBuilder {
|
|
21
43
|
protected readonly project: P;
|
|
22
44
|
protected _predicates: Predicate<T>[];
|
|
23
45
|
protected _conditions: Condition<T>[];
|
|
24
|
-
protected _reason?: string;
|
|
25
|
-
protected _metadata?: RuleMetadata;
|
|
26
|
-
protected _exclusions: (string | RegExp)[];
|
|
27
|
-
protected _silentIndices: Set<number>;
|
|
28
46
|
protected _phase: 'predicate' | 'condition';
|
|
47
|
+
/** Whether `.should()` was ever reached — bug 0155 state 3/4 vs state 1. */
|
|
48
|
+
protected _reachedShould: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Descriptions of predicate-only methods used AFTER `.should()` — bug 0155
|
|
51
|
+
* state 2. A dual-use method dispatches to a condition in that phase and
|
|
52
|
+
* never lands here, so anything recorded is genuinely a filter written where
|
|
53
|
+
* an assertion was meant.
|
|
54
|
+
*/
|
|
55
|
+
protected _misplaced: string[];
|
|
29
56
|
constructor(project: P);
|
|
30
57
|
/**
|
|
31
|
-
* Begin the predicate phase. Returns
|
|
58
|
+
* Begin the predicate phase. Returns a COPY — a held selection is never
|
|
59
|
+
* edited by narrowing it (see `TerminalBuilder.copy()`).
|
|
32
60
|
* Purely a readability marker — `.that().haveNameMatching(...)` reads like English.
|
|
33
61
|
* Explicitly resets phase to 'predicate' — defensive against `.should().that()` misuse.
|
|
34
62
|
*/
|
|
@@ -59,50 +87,6 @@ export declare abstract class RuleBuilder<T, P = unknown> {
|
|
|
59
87
|
* as a predicate; if it has `evaluate` it is treated as a condition.
|
|
60
88
|
*/
|
|
61
89
|
satisfy(custom: Predicate<T> | Condition<T>): this;
|
|
62
|
-
/**
|
|
63
|
-
* Attach a human-readable rationale to the rule.
|
|
64
|
-
* Included in violation messages when `.check()` throws.
|
|
65
|
-
*/
|
|
66
|
-
because(reason: string): this;
|
|
67
|
-
/**
|
|
68
|
-
* Attach rich metadata to the rule.
|
|
69
|
-
* Provides educational context in violation output: why, how to fix, docs link.
|
|
70
|
-
*
|
|
71
|
-
* If `metadata.because` is set, it also sets the reason (same as `.because()`).
|
|
72
|
-
*/
|
|
73
|
-
rule(metadata: RuleMetadata): this;
|
|
74
|
-
/**
|
|
75
|
-
* Exclude specific violations from reporting by matching against
|
|
76
|
-
* the violation's `element`, `file`, or `message` fields.
|
|
77
|
-
*
|
|
78
|
-
* Matched violations are silently suppressed. Use for permanent,
|
|
79
|
-
* intentional exceptions — not for temporary violations (use baseline for those).
|
|
80
|
-
*
|
|
81
|
-
* Patterns are matched against all three fields. Prefer anchored regexes
|
|
82
|
-
* or full string matches over short substrings, especially for `message`
|
|
83
|
-
* matching, to avoid accidentally suppressing unrelated violations whose
|
|
84
|
-
* messages happen to contain the same text.
|
|
85
|
-
*
|
|
86
|
-
* Emits a warning if an exclusion matches zero violations — so renamed
|
|
87
|
-
* or deleted exceptions don't silently stay in the rule.
|
|
88
|
-
*
|
|
89
|
-
* For narrowing a rule's scope at the predicate phase (so the rule never
|
|
90
|
-
* evaluates the excluded element), use `satisfy(not(<predicate>))` instead.
|
|
91
|
-
* See the "Excluding a file from a rule's scope" recipe in docs/recipes.md.
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* // Exclude by element name (fully qualified)
|
|
95
|
-
* .excluding('Asset.getImageUrl')
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* // Exclude by file path (regex anchored to suffix)
|
|
99
|
-
* .excluding(/repositories\/index\.ts$/)
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* // Multiple exclusions, mixed forms
|
|
103
|
-
* .excluding('Asset.getImageUrl', /\/legacy\//, /generated/)
|
|
104
|
-
*/
|
|
105
|
-
excluding(...patterns: (string | RegExp | SilentExclusion)[]): this;
|
|
106
90
|
/**
|
|
107
91
|
* Return the predicate-filtered elements as a labelled `Selection<T>` for
|
|
108
92
|
* cross-validation (`correspondence()`). The set is exactly the elements this
|
|
@@ -121,38 +105,61 @@ export declare abstract class RuleBuilder<T, P = unknown> {
|
|
|
121
105
|
*/
|
|
122
106
|
describeRule(): RuleDescription;
|
|
123
107
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
|
|
127
|
-
violations(): ArchViolation[];
|
|
128
|
-
/**
|
|
129
|
-
* Execute the rule and throw `ArchRuleError` if any violations are found.
|
|
130
|
-
* This is the primary terminal method — use in test assertions.
|
|
108
|
+
* ADR-010: is this rule's assertion satisfied by an empty selection, as a
|
|
109
|
+
* property of what its conditions assert (`.notExist()`) rather than a
|
|
110
|
+
* caller's `.expectEmpty()` declaration?
|
|
131
111
|
*
|
|
132
|
-
*
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
*
|
|
137
|
-
*
|
|
112
|
+
* `.every()`, not `.some()` — `.andShould()` ANDs, so a rule reading
|
|
113
|
+
* `.should().notExist().andShould().beExported()` still asserts something
|
|
114
|
+
* about subjects that exist, and exempting it on the strength of the one
|
|
115
|
+
* cardinality condition would silence the other. An empty condition list is
|
|
116
|
+
* NOT exempt: `[].every()` is vacuously `true`, and a rule with zero
|
|
117
|
+
* conditions is the assertion-less case `collectViolations()` now reports as
|
|
118
|
+
* a configuration finding (bug 0155; it used to be an unreachable stderr
|
|
119
|
+
* warning, which this comment named until that fix landed) — this override
|
|
120
|
+
* must not paper over it by also declaring it cardinality-satisfied.
|
|
138
121
|
*
|
|
139
|
-
*
|
|
122
|
+
* **Load-bearing:** for a DEAD selector this early return is the only thing
|
|
123
|
+
* between an assertion-less rule and a silent pass (the gate runs after the
|
|
124
|
+
* zero-examined branch). Pinned in `assertion-less-rules.test.ts`.
|
|
140
125
|
*/
|
|
141
|
-
warn(options?: CheckOptions): void;
|
|
142
126
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
|
|
147
|
-
|
|
127
|
+
* Does this rule assert anything at all? — bug 0155.
|
|
128
|
+
*
|
|
129
|
+
* Conditions alone are not enough: a predicate-only method written *after*
|
|
130
|
+
* `.should()` silently shrank the set the conditions run over, possibly to
|
|
131
|
+
* empty, in which case they hold vacuously. That rule asserts nothing
|
|
132
|
+
* meaningful even though `_conditions` is non-empty.
|
|
133
|
+
*/
|
|
134
|
+
protected assertsSomething(): boolean;
|
|
135
|
+
protected assertionAdvice(): string;
|
|
136
|
+
protected assertsCardinality(): boolean;
|
|
137
|
+
/**
|
|
138
|
+
* ADR-010 part 3: did this rule's own underlying source (the project, a
|
|
139
|
+
* diagram, an SDL string — whatever `P` loads) produce nothing at all,
|
|
140
|
+
* before `getElements()`'s own domain-specific extraction or any
|
|
141
|
+
* predicate ran? Default `false` — the kernel has no way to know, since
|
|
142
|
+
* `P` is opaque here. A dialect's concrete builder, which knows `P`'s real
|
|
143
|
+
* shape (e.g. `ArchProject`), overrides this to check its own project's
|
|
144
|
+
* file count directly — never `getElements().length`, which conflates "the
|
|
145
|
+
* source is empty" with "this domain legitimately has nothing in a
|
|
146
|
+
* healthy project" (e.g. zero JSX elements in a backend-only codebase).
|
|
147
|
+
*/
|
|
148
|
+
protected sourceEmpty(): boolean;
|
|
149
|
+
/** Declared globs, position derived from `_phase`. Pure — no `P` needed. */
|
|
150
|
+
globs(): readonly GlobNode[];
|
|
151
|
+
/** This rule's own project, for `diagnose()`/`doctor` outside `.check()`. */
|
|
152
|
+
getProject(): P;
|
|
153
|
+
/** A `globs()` tree diagnosably dead, when zero examined — same shape as `sourceEmpty()`. */
|
|
154
|
+
protected deadGlobDiagnosis(): string | undefined;
|
|
148
155
|
/**
|
|
149
156
|
* Register a predicate. Called by concrete builder methods like
|
|
150
|
-
* `.haveNameMatching()`, `.extend()`, etc.
|
|
157
|
+
* `.haveNameMatching()`, `.extend()`, etc. Returns a COPY — see `copy()`.
|
|
151
158
|
*/
|
|
152
159
|
protected addPredicate(predicate: Predicate<T>): this;
|
|
153
160
|
/**
|
|
154
161
|
* Register a condition. Called by concrete builder methods like
|
|
155
|
-
* `.notContain()`, `.notExist()`, etc.
|
|
162
|
+
* `.notContain()`, `.notExist()`, etc. Returns a COPY — see `copy()`.
|
|
156
163
|
*/
|
|
157
164
|
protected addCondition(condition: Condition<T>): this;
|
|
158
165
|
/**
|
|
@@ -161,10 +168,31 @@ export declare abstract class RuleBuilder<T, P = unknown> {
|
|
|
161
168
|
*/
|
|
162
169
|
protected abstract getElements(): T[];
|
|
163
170
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
171
|
+
* An independent copy, carrying **both** predicates and conditions.
|
|
172
|
+
* Extends `TerminalBuilder.copy()` with the fields this class adds.
|
|
173
|
+
*
|
|
174
|
+
* Subclasses with additional mutable-reference fields of their own (arrays,
|
|
175
|
+
* sets, maps a chain method pushes onto) MUST override this and call
|
|
176
|
+
* `super.copy()` first, the same reason `TerminalBuilder.copy()`'s own
|
|
177
|
+
* docstring gives — `Object.assign` shallow-copies a field, it does not
|
|
178
|
+
* give it independent identity.
|
|
179
|
+
*/
|
|
180
|
+
protected copy(): this;
|
|
181
|
+
/**
|
|
182
|
+
* A fork of this builder, carrying BOTH lists. Used by `.should()` to support
|
|
183
|
+
* named selections without mutation.
|
|
184
|
+
*
|
|
185
|
+
* **Nothing here clears the conditions** (bug 0156). It used to, and a second
|
|
186
|
+
* `.should()` therefore silently discarded the first assertion — a rule that
|
|
187
|
+
* asserted something turned into one that asserted less, by a chain method.
|
|
188
|
+
* `.should().X().should().Y()` now accumulates exactly as `.andShould()` does.
|
|
166
189
|
*
|
|
167
|
-
*
|
|
190
|
+
* Ported from `packages/ts/src/core/rule-builder.ts`, where the engine copy
|
|
191
|
+
* landed the fix and left the kernel behind. Found by the architect review of
|
|
192
|
+
* PR #72: `eess-md`, `eess-mermaid` and `eess-gherkin` all extend THIS class,
|
|
193
|
+
* so they carried the defect — and `check:corpus`, `check:ledger` and
|
|
194
|
+
* `check:diagram` are md/mermaid gates, meaning this repo's own corpus
|
|
195
|
+
* enforcement ran on the broken copy.
|
|
168
196
|
*/
|
|
169
197
|
protected fork(): this;
|
|
170
198
|
/**
|
|
@@ -172,10 +200,22 @@ export declare abstract class RuleBuilder<T, P = unknown> {
|
|
|
172
200
|
*/
|
|
173
201
|
private buildRuleDescription;
|
|
174
202
|
/**
|
|
175
|
-
* Execute the full pipeline: filter elements with predicates,
|
|
176
|
-
*
|
|
177
|
-
|
|
178
|
-
|
|
203
|
+
* Execute the full pipeline: filter elements with predicates, evaluate
|
|
204
|
+
* conditions, return violations plus the ADR-010 evidence — the
|
|
205
|
+
* post-predicate subject count, whatever it is. `TerminalBuilder.check()` /
|
|
206
|
+
* `.warn()` / `.violations()` decide what a zero count means (a
|
|
207
|
+
* configuration finding, unless `.expectEmpty()` was declared); this method
|
|
208
|
+
* only reports the count, never interprets it.
|
|
209
|
+
*
|
|
210
|
+
* `sourceEmpty` (ADR-010 part 3) is deliberately not `allElements.length
|
|
211
|
+
* === 0` — a domain's own `getElements()` legitimately returning nothing in
|
|
212
|
+
* a healthy, fully-loaded project (e.g. zero JSX elements in a
|
|
213
|
+
* backend-only codebase) is not brokenness, and conflating the two broke
|
|
214
|
+
* `.notExist()`'s cardinality exemption for exactly that case. Subclasses
|
|
215
|
+
* that can answer "did my underlying source load anything at all" override
|
|
216
|
+
* `sourceEmpty()`; the default assumes it did.
|
|
217
|
+
*/
|
|
218
|
+
protected collectViolations(): CollectResult;
|
|
179
219
|
/**
|
|
180
220
|
* Build the `ConditionContext` passed to each condition.
|
|
181
221
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-builder.d.ts","sourceRoot":"","sources":["../src/rule-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rule-builder.d.ts","sourceRoot":"","sources":["../src/rule-builder.ts"],"names":[],"mappings":"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"}
|