@nielspeter/eess 0.4.0 → 0.5.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 +94 -1
- 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 +1 -1
- package/dist/cardinality.d.ts.map +1 -1
- package/dist/cardinality.js +20 -5
- package/dist/cardinality.js.map +1 -1
- package/dist/cli-config.d.ts +106 -0
- package/dist/cli-config.d.ts.map +1 -0
- package/dist/cli-config.js +231 -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/condition.d.ts +37 -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.d.ts +30 -0
- package/dist/correspondence.d.ts.map +1 -1
- package/dist/correspondence.js +36 -1
- package/dist/correspondence.js.map +1 -1
- package/dist/dedupe-config-findings.d.ts.map +1 -1
- package/dist/dedupe-config-findings.js +27 -0
- package/dist/dedupe-config-findings.js.map +1 -1
- package/dist/diff-aware.d.ts +8 -9
- package/dist/diff-aware.d.ts.map +1 -1
- package/dist/diff-aware.js +38 -17
- package/dist/diff-aware.js.map +1 -1
- 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 +48 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +61 -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 +12 -3
- package/dist/execute-rule.d.ts.map +1 -1
- package/dist/execute-rule.js +184 -7
- 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-json.d.ts +13 -0
- package/dist/format-json.d.ts.map +1 -1
- package/dist/format-json.js +24 -0
- package/dist/format-json.js.map +1 -1
- package/dist/index.d.ts +14 -46
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -41
- 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.map +1 -1
- package/dist/owns-empty-discovery.js +12 -4
- package/dist/owns-empty-discovery.js.map +1 -1
- 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/predicate.d.ts +12 -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 -34
- package/dist/preset-dispatch.d.ts.map +1 -1
- package/dist/preset-dispatch.js +30 -52
- package/dist/preset-dispatch.js.map +1 -1
- package/dist/report.d.ts +31 -7
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +174 -15
- package/dist/report.js.map +1 -1
- package/dist/rule-builder-like.d.ts +8 -2
- package/dist/rule-builder-like.d.ts.map +1 -1
- package/dist/rule-builder.d.ts +15 -0
- package/dist/rule-builder.d.ts.map +1 -1
- package/dist/rule-builder.js +41 -36
- package/dist/rule-builder.js.map +1 -1
- package/dist/rule-description.d.ts +50 -0
- package/dist/rule-description.d.ts.map +1 -1
- package/dist/rule-description.js +58 -0
- package/dist/rule-description.js.map +1 -1
- package/dist/selection-memo.d.ts +1 -3
- package/dist/selection-memo.d.ts.map +1 -1
- package/dist/selection-memo.js +1 -3
- package/dist/selection-memo.js.map +1 -1
- 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/terminal-builder.d.ts +29 -15
- package/dist/terminal-builder.d.ts.map +1 -1
- package/dist/terminal-builder.js +97 -51
- package/dist/terminal-builder.js.map +1 -1
- package/dist/vacuity-findings.d.ts +0 -6
- package/dist/vacuity-findings.d.ts.map +1 -1
- package/dist/vacuity-findings.js +27 -57
- package/dist/vacuity-findings.js.map +1 -1
- package/dist/violation.d.ts +22 -1
- package/dist/violation.d.ts.map +1 -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';
|
|
@@ -25,38 +27,195 @@ let violationsEmitted = 0;
|
|
|
25
27
|
export function violationsEmittedCount() {
|
|
26
28
|
return violationsEmitted;
|
|
27
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
|
+
}
|
|
28
150
|
export function reportViolations(violations, options = {}) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
151
|
+
const gated = withEvidenceGate(violations);
|
|
152
|
+
if (gated.length === 0)
|
|
153
|
+
return gated;
|
|
154
|
+
violationsEmitted += gated.length;
|
|
32
155
|
const format = options.format ?? 'terminal';
|
|
33
156
|
if (format === 'json') {
|
|
34
|
-
process.stdout.write(formatViolationsJson(
|
|
157
|
+
process.stdout.write(formatViolationsJson(gated, options.reason) + '\n');
|
|
35
158
|
}
|
|
36
159
|
else if (format === 'github') {
|
|
37
|
-
process.stdout.write(formatViolationsGitHub(
|
|
160
|
+
process.stdout.write(formatViolationsGitHub(gated, 'error') + '\n');
|
|
38
161
|
}
|
|
39
162
|
else {
|
|
40
163
|
// Rich text to stderr — test runners show the plain-text Error message,
|
|
41
164
|
// while stderr carries the colorized Why/Fix/Docs output.
|
|
42
|
-
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);
|
|
43
172
|
}
|
|
173
|
+
return gated;
|
|
44
174
|
}
|
|
45
175
|
/**
|
|
46
176
|
* Finish a preset: deliver its aggregated violations per the requested mode
|
|
47
|
-
* (plan 0070). `throw` (default) emits then throws
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
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.
|
|
51
181
|
*/
|
|
52
182
|
export function finishPreset(violations, options = {}) {
|
|
53
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);
|
|
54
197
|
if (mode === 'return')
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
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);
|
|
59
218
|
}
|
|
60
|
-
return
|
|
219
|
+
return gated;
|
|
61
220
|
}
|
|
62
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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"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CollectResult } from './collect-result.js';
|
|
2
2
|
/**
|
|
3
3
|
* Minimal shape the CLI runner and presets collect violations from: anything
|
|
4
4
|
* exposing a non-throwing, severity-stamped `.violations()`. Lives in core (not
|
|
5
5
|
* `cli/`) so presets can return `RuleBuilderLike[]` without depending on CLI
|
|
6
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.
|
|
7
13
|
*/
|
|
8
14
|
export interface RuleBuilderLike {
|
|
9
|
-
violations: () =>
|
|
15
|
+
violations: () => CollectResult;
|
|
10
16
|
}
|
|
11
17
|
//# sourceMappingURL=rule-builder-like.d.ts.map
|
|
@@ -1 +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,
|
|
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"}
|
package/dist/rule-builder.d.ts
CHANGED
|
@@ -4,6 +4,21 @@ import type { RuleDescription } from './rule-description.js';
|
|
|
4
4
|
import type { Selection, ElementInfo } from './correspondence.js';
|
|
5
5
|
import type { GlobNode } from './glob-site.js';
|
|
6
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[];
|
|
7
22
|
/**
|
|
8
23
|
* Abstract base class for all rule builders.
|
|
9
24
|
*
|
|
@@ -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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"}
|
package/dist/rule-builder.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { assertionAdviceFor } from './assertion-advice.js';
|
|
2
|
+
import { collectResult } from './collect-result.js';
|
|
3
|
+
import { selectMatching, matchingElements } from './correspondence.js';
|
|
4
|
+
import { recordPredicate } from './predicate.js';
|
|
2
5
|
import { countDeclaredGlobs, stampGlobs } from './glob-site.js';
|
|
3
6
|
import { TerminalBuilder, assertionLessViolation } from './terminal-builder.js';
|
|
4
7
|
import { assertsCardinality as conditionAssertsCardinality } from './cardinality.js';
|
|
8
|
+
import { ruleDescriptionOf, ruleDescriptionFrom } from './rule-description.js';
|
|
9
|
+
import { conditionContextFrom } from './condition.js';
|
|
5
10
|
/**
|
|
6
11
|
* A declared glob's own label in a dead-glob finding — the predicate/
|
|
7
12
|
* condition's description, disambiguated with the literal glob only when
|
|
@@ -18,12 +23,31 @@ function describeOrigin(description, glob, siteCount) {
|
|
|
18
23
|
* rather than a method body, to keep the class itself under this repo's own
|
|
19
24
|
* 300-line class-length gate (`arch.internal.rules.ts`).
|
|
20
25
|
*/
|
|
21
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The globs a rule declares, stamped with where each came from.
|
|
28
|
+
*
|
|
29
|
+
* Exported (as family plumbing) so `eess-ts` can call it instead of keeping its
|
|
30
|
+
* own copy — they were 94% similar, and the difference was not cosmetic: only
|
|
31
|
+
* the eess-ts copy honoured `originLabel`. The kernel's `Predicate` has carried
|
|
32
|
+
* that field all along and this function ignored it, so a preset's label was
|
|
33
|
+
* dropped for `eess-md`, `eess-mermaid` and `eess-gherkin`. Found by unifying.
|
|
34
|
+
*/
|
|
35
|
+
export function declaredGlobsOf(
|
|
36
|
+
// `readonly`, widened so a caller holding a frozen declaration can pass it
|
|
37
|
+
// without a copy. Strictly more permissive; the body only iterates.
|
|
38
|
+
predicates, conditions) {
|
|
22
39
|
const trees = [];
|
|
23
40
|
for (const predicate of predicates) {
|
|
24
41
|
if (predicate.globs) {
|
|
25
42
|
const count = countDeclaredGlobs(predicate.globs);
|
|
26
|
-
trees.push(stampGlobs(predicate.globs, 'selector', (g) =>
|
|
43
|
+
trees.push(stampGlobs(predicate.globs, 'selector', (g) =>
|
|
44
|
+
// A preset's `originLabel` names the option the user wrote rather
|
|
45
|
+
// than the calls it expanded into. Used VERBATIM, skipping
|
|
46
|
+
// `describeOrigin`: that appends `("glob")` to disambiguate a
|
|
47
|
+
// predicate holding several sites, and a label already names exactly
|
|
48
|
+
// one option and one glob — left in, the finding read
|
|
49
|
+
// `shared: "**/x/**" ("**/x/**")`.
|
|
50
|
+
predicate.originLabel ?? describeOrigin(predicate.description, g, count)));
|
|
27
51
|
}
|
|
28
52
|
}
|
|
29
53
|
for (const condition of conditions) {
|
|
@@ -139,22 +163,18 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
139
163
|
* @param opts.identify - map an element to its message metadata (name/file/line)
|
|
140
164
|
*/
|
|
141
165
|
select(opts) {
|
|
142
|
-
|
|
143
|
-
return { elements: filtered, label: opts.label, identify: opts.identify };
|
|
166
|
+
return selectMatching(this.getElements(), this._predicates, opts);
|
|
144
167
|
}
|
|
145
168
|
/**
|
|
146
169
|
* Return a structured description of this rule without executing it.
|
|
147
170
|
* Used by the `explain` CLI subcommand.
|
|
148
171
|
*/
|
|
149
172
|
describeRule() {
|
|
150
|
-
return {
|
|
173
|
+
return ruleDescriptionFrom({
|
|
174
|
+
metadata: this._metadata,
|
|
175
|
+
reason: this._reason,
|
|
151
176
|
rule: this.buildRuleDescription(),
|
|
152
|
-
|
|
153
|
-
because: this._reason,
|
|
154
|
-
suggestion: this._metadata?.suggestion,
|
|
155
|
-
docs: this._metadata?.docs,
|
|
156
|
-
imperative: this._metadata?.imperative,
|
|
157
|
-
};
|
|
177
|
+
});
|
|
158
178
|
}
|
|
159
179
|
/**
|
|
160
180
|
* ADR-010: is this rule's assertion satisfied by an empty selection, as a
|
|
@@ -231,13 +251,7 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
231
251
|
*/
|
|
232
252
|
addPredicate(predicate) {
|
|
233
253
|
const next = this.copy();
|
|
234
|
-
next._predicates.
|
|
235
|
-
// Bug 0155 state 2: a predicate-only method used after `.should()`. Dual-use
|
|
236
|
-
// methods dispatch to conditions in that phase and never reach here, so this
|
|
237
|
-
// is a filter written where an assertion was meant — the one state whose fix
|
|
238
|
-
// is "move it before .should()", not "add a condition".
|
|
239
|
-
if (next._phase === 'condition')
|
|
240
|
-
next._misplaced.push(predicate.description);
|
|
254
|
+
recordPredicate(predicate, next._predicates, next._misplaced, next._phase);
|
|
241
255
|
return next;
|
|
242
256
|
}
|
|
243
257
|
/**
|
|
@@ -292,14 +306,7 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
292
306
|
* Build the rule description from predicates and conditions.
|
|
293
307
|
*/
|
|
294
308
|
buildRuleDescription() {
|
|
295
|
-
|
|
296
|
-
const conditionDesc = this._conditions.map((c) => c.description).join(' and ');
|
|
297
|
-
const parts = [];
|
|
298
|
-
if (predicateDesc)
|
|
299
|
-
parts.push(`that ${predicateDesc}`);
|
|
300
|
-
if (conditionDesc)
|
|
301
|
-
parts.push(`should ${conditionDesc}`);
|
|
302
|
-
return parts.join(' ');
|
|
309
|
+
return ruleDescriptionOf(this._predicates, this._conditions);
|
|
303
310
|
}
|
|
304
311
|
/**
|
|
305
312
|
* Execute the full pipeline: filter elements with predicates, evaluate
|
|
@@ -319,7 +326,7 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
319
326
|
*/
|
|
320
327
|
collectViolations() {
|
|
321
328
|
const allElements = this.getElements();
|
|
322
|
-
const filtered = allElements
|
|
329
|
+
const filtered = matchingElements(allElements, this._predicates);
|
|
323
330
|
const examined = filtered.length;
|
|
324
331
|
if (filtered.length === 0) {
|
|
325
332
|
const sourceEmpty = this.sourceEmpty();
|
|
@@ -328,7 +335,7 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
328
335
|
// useful to say about one glob's fate against zero loaded files, and
|
|
329
336
|
// `sourceEmpty` outranks it in `evidencedViolations()` regardless.
|
|
330
337
|
const deadGlob = sourceEmpty ? undefined : this.deadGlobDiagnosis();
|
|
331
|
-
return
|
|
338
|
+
return collectResult([], { examined, sourceEmpty, deadGlob });
|
|
332
339
|
}
|
|
333
340
|
// Bug 0155 — gate-first, before the conditions run. See
|
|
334
341
|
// `assertionLessViolation` for why this is a finding and not a warning,
|
|
@@ -337,7 +344,7 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
337
344
|
// assertion too — see `assertionLessViolation`.
|
|
338
345
|
if (!this.assertsSomething() && this._expectEmpty === undefined) {
|
|
339
346
|
const ruleId = this._metadata?.id ?? (this.buildRuleDescription() || 'unnamed');
|
|
340
|
-
return
|
|
347
|
+
return collectResult([assertionLessViolation(ruleId, this.assertionAdvice())], { examined });
|
|
341
348
|
}
|
|
342
349
|
// Step 4: Build context for conditions
|
|
343
350
|
const context = this.buildConditionContext();
|
|
@@ -346,7 +353,7 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
346
353
|
for (const condition of this._conditions) {
|
|
347
354
|
violations.push(...condition.evaluate(filtered, context));
|
|
348
355
|
}
|
|
349
|
-
return
|
|
356
|
+
return collectResult(violations, { examined });
|
|
350
357
|
}
|
|
351
358
|
/**
|
|
352
359
|
* Build the `ConditionContext` passed to each condition.
|
|
@@ -356,13 +363,11 @@ export class RuleBuilder extends TerminalBuilder {
|
|
|
356
363
|
* Call `super.buildConditionContext()` and spread the result.
|
|
357
364
|
*/
|
|
358
365
|
buildConditionContext() {
|
|
359
|
-
return {
|
|
366
|
+
return conditionContextFrom({
|
|
367
|
+
metadata: this._metadata,
|
|
368
|
+
reason: this._reason,
|
|
360
369
|
rule: this.buildRuleDescription(),
|
|
361
|
-
|
|
362
|
-
ruleId: this._metadata?.id,
|
|
363
|
-
suggestion: this._metadata?.suggestion,
|
|
364
|
-
docs: this._metadata?.docs,
|
|
365
|
-
};
|
|
370
|
+
});
|
|
366
371
|
}
|
|
367
372
|
}
|
|
368
373
|
//# sourceMappingURL=rule-builder.js.map
|
package/dist/rule-builder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-builder.js","sourceRoot":"","sources":["../src/rule-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"rule-builder.js","sourceRoot":"","sources":["../src/rule-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RuleMetadata } from './rule-metadata.js';
|
|
1
2
|
/**
|
|
2
3
|
* Structured description of a rule, returned by `.describe()`.
|
|
3
4
|
* Used by the `explain` CLI subcommand to dump active rules as JSON.
|
|
@@ -43,4 +44,53 @@ export interface Describable {
|
|
|
43
44
|
* two test copies already did.
|
|
44
45
|
*/
|
|
45
46
|
export declare function isDescribable(value: unknown): value is Describable;
|
|
47
|
+
/**
|
|
48
|
+
* The metadata half of a rule's description — one projection, four callers.
|
|
49
|
+
*
|
|
50
|
+
* `id`, `because`, `suggestion` and `docs` are read off `RuleMetadata` the same
|
|
51
|
+
* way everywhere. `rule` and `imperative` are the two axes that genuinely vary,
|
|
52
|
+
* so they are parameters rather than something this function guesses:
|
|
53
|
+
*
|
|
54
|
+
* - `rule` — the base `TerminalBuilder` says `id ?? 'unnamed'` (a SENTINEL that
|
|
55
|
+
* `dedupe-config-findings.ts` keys on, not an identity), a `RuleBuilder` says
|
|
56
|
+
* the assembled `ruleDescriptionOf(...)` sentence, and several `eess-ts`
|
|
57
|
+
* subclasses override it again on top of `...super.describeRule()`.
|
|
58
|
+
* - `imperativeFallback` — what to say for an agent prompt when the author
|
|
59
|
+
* declared no `imperative`. `eess-ts`'s `RuleDeclaration` falls back to the
|
|
60
|
+
* reason and its `describeRuleOf` to a derived Do-NOT/MUST sentence; the
|
|
61
|
+
* kernel has no fallback, and that is not an oversight this extraction
|
|
62
|
+
* quietly fixed — `imperative` is read only by `eess-ts`'s
|
|
63
|
+
* `explain --format agent`, and `eess-mermaid`'s `explain` does not project
|
|
64
|
+
* the field at all. The kernel has no consumer to be wrong for.
|
|
65
|
+
*
|
|
66
|
+
* The four bodies were otherwise identical, which `no-copy-paste` reported as a
|
|
67
|
+
* 93% cluster.
|
|
68
|
+
*/
|
|
69
|
+
export declare function ruleDescriptionFrom(opts: {
|
|
70
|
+
metadata: RuleMetadata | undefined;
|
|
71
|
+
reason: string | undefined;
|
|
72
|
+
rule: string;
|
|
73
|
+
imperativeFallback?: string;
|
|
74
|
+
}): RuleDescription;
|
|
75
|
+
/**
|
|
76
|
+
* A rule's English description, assembled from what it declared.
|
|
77
|
+
*
|
|
78
|
+
* `subject` is the noun the rule opens with when its builder has one — the
|
|
79
|
+
* GraphQL builders say `schema that ... should ...` and
|
|
80
|
+
* `resolvers that ... should ...`; the general builders open with `that`. That
|
|
81
|
+
* one word was the only difference between four copies of this function, which
|
|
82
|
+
* `no-copy-paste` reported as a cluster at 100%.
|
|
83
|
+
*
|
|
84
|
+
* An empty predicate or condition list contributes nothing rather than an empty
|
|
85
|
+
* clause, so a rule that never reached `.should()` reads `that X` and not
|
|
86
|
+
* `that X should ` — the missing half is what `assertionAdvice` then reports.
|
|
87
|
+
*
|
|
88
|
+
* `slice-rule-builder.ts` is deliberately NOT a caller: its description is a
|
|
89
|
+
* different sentence (`slices [a, b] should ...`), not this one with a prefix.
|
|
90
|
+
*/
|
|
91
|
+
export declare function ruleDescriptionOf(predicates: readonly {
|
|
92
|
+
description: string;
|
|
93
|
+
}[], conditions: readonly {
|
|
94
|
+
description: string;
|
|
95
|
+
}[], subject?: string): string;
|
|
46
96
|
//# sourceMappingURL=rule-description.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-description.d.ts","sourceRoot":"","sources":["../src/rule-description.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,eAAe,CAAA;CACpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAIlE"}
|
|
1
|
+
{"version":3,"file":"rule-description.d.ts","sourceRoot":"","sources":["../src/rule-description.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,eAAe,CAAA;CACpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAIlE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAA;IAClC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,GAAG,eAAe,CASlB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,EAC9C,UAAU,EAAE,SAAS;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,EAC9C,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAOR"}
|
package/dist/rule-description.js
CHANGED
|
@@ -15,4 +15,62 @@ export function isDescribable(value) {
|
|
|
15
15
|
return false;
|
|
16
16
|
return typeof value.describeRule === 'function';
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The metadata half of a rule's description — one projection, four callers.
|
|
20
|
+
*
|
|
21
|
+
* `id`, `because`, `suggestion` and `docs` are read off `RuleMetadata` the same
|
|
22
|
+
* way everywhere. `rule` and `imperative` are the two axes that genuinely vary,
|
|
23
|
+
* so they are parameters rather than something this function guesses:
|
|
24
|
+
*
|
|
25
|
+
* - `rule` — the base `TerminalBuilder` says `id ?? 'unnamed'` (a SENTINEL that
|
|
26
|
+
* `dedupe-config-findings.ts` keys on, not an identity), a `RuleBuilder` says
|
|
27
|
+
* the assembled `ruleDescriptionOf(...)` sentence, and several `eess-ts`
|
|
28
|
+
* subclasses override it again on top of `...super.describeRule()`.
|
|
29
|
+
* - `imperativeFallback` — what to say for an agent prompt when the author
|
|
30
|
+
* declared no `imperative`. `eess-ts`'s `RuleDeclaration` falls back to the
|
|
31
|
+
* reason and its `describeRuleOf` to a derived Do-NOT/MUST sentence; the
|
|
32
|
+
* kernel has no fallback, and that is not an oversight this extraction
|
|
33
|
+
* quietly fixed — `imperative` is read only by `eess-ts`'s
|
|
34
|
+
* `explain --format agent`, and `eess-mermaid`'s `explain` does not project
|
|
35
|
+
* the field at all. The kernel has no consumer to be wrong for.
|
|
36
|
+
*
|
|
37
|
+
* The four bodies were otherwise identical, which `no-copy-paste` reported as a
|
|
38
|
+
* 93% cluster.
|
|
39
|
+
*/
|
|
40
|
+
export function ruleDescriptionFrom(opts) {
|
|
41
|
+
return {
|
|
42
|
+
rule: opts.rule,
|
|
43
|
+
id: opts.metadata?.id,
|
|
44
|
+
because: opts.reason,
|
|
45
|
+
suggestion: opts.metadata?.suggestion,
|
|
46
|
+
docs: opts.metadata?.docs,
|
|
47
|
+
imperative: opts.metadata?.imperative ?? opts.imperativeFallback,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A rule's English description, assembled from what it declared.
|
|
52
|
+
*
|
|
53
|
+
* `subject` is the noun the rule opens with when its builder has one — the
|
|
54
|
+
* GraphQL builders say `schema that ... should ...` and
|
|
55
|
+
* `resolvers that ... should ...`; the general builders open with `that`. That
|
|
56
|
+
* one word was the only difference between four copies of this function, which
|
|
57
|
+
* `no-copy-paste` reported as a cluster at 100%.
|
|
58
|
+
*
|
|
59
|
+
* An empty predicate or condition list contributes nothing rather than an empty
|
|
60
|
+
* clause, so a rule that never reached `.should()` reads `that X` and not
|
|
61
|
+
* `that X should ` — the missing half is what `assertionAdvice` then reports.
|
|
62
|
+
*
|
|
63
|
+
* `slice-rule-builder.ts` is deliberately NOT a caller: its description is a
|
|
64
|
+
* different sentence (`slices [a, b] should ...`), not this one with a prefix.
|
|
65
|
+
*/
|
|
66
|
+
export function ruleDescriptionOf(predicates, conditions, subject) {
|
|
67
|
+
const predicateDesc = predicates.map((p) => p.description).join(' and ');
|
|
68
|
+
const conditionDesc = conditions.map((c) => c.description).join(' and ');
|
|
69
|
+
const parts = subject === undefined ? [] : [subject];
|
|
70
|
+
if (predicateDesc)
|
|
71
|
+
parts.push(`that ${predicateDesc}`);
|
|
72
|
+
if (conditionDesc)
|
|
73
|
+
parts.push(`should ${conditionDesc}`);
|
|
74
|
+
return parts.join(' ');
|
|
75
|
+
}
|
|
18
76
|
//# sourceMappingURL=rule-description.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-description.js","sourceRoot":"","sources":["../src/rule-description.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rule-description.js","sourceRoot":"","sources":["../src/rule-description.ts"],"names":[],"mappings":"AAsCA;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAC7D,IAAI,CAAC,CAAC,cAAc,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC5C,OAAO,OAAO,KAAK,CAAC,YAAY,KAAK,UAAU,CAAA;AACjD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAKnC;IACC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE;QACrB,OAAO,EAAE,IAAI,CAAC,MAAM;QACpB,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU;QACrC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI;QACzB,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU,IAAI,IAAI,CAAC,kBAAkB;KACjE,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAA8C,EAC9C,UAA8C,EAC9C,OAAgB;IAEhB,MAAM,aAAa,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACxE,MAAM,aAAa,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACxE,MAAM,KAAK,GAAa,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IAC9D,IAAI,aAAa;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,aAAa,EAAE,CAAC,CAAA;IACtD,IAAI,aAAa;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,aAAa,EAAE,CAAC,CAAA;IACxD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC"}
|
package/dist/selection-memo.d.ts
CHANGED
|
@@ -38,9 +38,7 @@
|
|
|
38
38
|
* const selectionOf = selectionMemo<ArchFunction>()
|
|
39
39
|
*
|
|
40
40
|
* private selected(): ArchFunction[] {
|
|
41
|
-
* return selectionOf(this, () =>
|
|
42
|
-
* this.getElements().filter((e) => this._predicates.every((p) => p.test(e))),
|
|
43
|
-
* )
|
|
41
|
+
* return selectionOf(this, () => matchingElements(this.getElements(), this._predicates))
|
|
44
42
|
* }
|
|
45
43
|
*/
|
|
46
44
|
export declare function selectionMemo<T>(): (owner: object, compute: () => T[]) => T[];
|