@nielspeter/eess 0.2.2 → 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 +2 -1
- package/dist/execute-rule.d.ts.map +1 -1
- package/dist/execute-rule.js +38 -6
- 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 +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 +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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What inline exclusion comments suppressed, said out loud.
|
|
3
|
+
*
|
|
4
|
+
* Every other filter in the pipeline discloses itself: `.excluding()` warns on
|
|
5
|
+
* a pattern that matched nothing (`execute-rule.ts`), diff-aware has
|
|
6
|
+
* `suppressionNotice`, the baseline has `unmatchedBaselineFinding`. The inline
|
|
7
|
+
* comment filter dropped violations and returned no count — and after
|
|
8
|
+
* [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)
|
|
9
|
+
* made comments work for every condition family, it became the **widest** filter
|
|
10
|
+
* in the pipeline and the only silent one.
|
|
11
|
+
*
|
|
12
|
+
* `diff-disclosure.ts` states the principle this module inherits: *a run with
|
|
13
|
+
* every finding suppressed is indistinguishable from a clean run.* That is the
|
|
14
|
+
* false green ADR-008 exists to prevent, and it is worse here than for
|
|
15
|
+
* `--changed`, because a comment is written once and then read by nobody. The
|
|
16
|
+
* flag at least appears in the CI invocation.
|
|
17
|
+
*
|
|
18
|
+
* ## Identities, not a total (ADR-009 rule 4)
|
|
19
|
+
*
|
|
20
|
+
* The notice names **which rule in which file**, not just how many. A count is a
|
|
21
|
+
* snapshot: it moves when anything moves and tells the reader nothing about what
|
|
22
|
+
* to look at. `arch/no-cycles in src/legacy/gateway.ts` is an identity — it
|
|
23
|
+
* survives a renumbering and it is greppable.
|
|
24
|
+
*
|
|
25
|
+
* Capped, and the cap is disclosed rather than silent: a suite with hundreds of
|
|
26
|
+
* exemptions should not print hundreds of lines on every green run, but
|
|
27
|
+
* "and 47 more" is a very different statement from printing 3 and implying that
|
|
28
|
+
* is all of them.
|
|
29
|
+
*
|
|
30
|
+
* ## Why module state
|
|
31
|
+
*
|
|
32
|
+
* `applyFilters` runs once per rule and has no view of the run. This is the same
|
|
33
|
+
* shape `edge-coverage.ts` uses for the same reason: accumulate per rule, reset
|
|
34
|
+
* at the run boundary, render once. The two aggregating surfaces — CLI `check`
|
|
35
|
+
* and `checkAll` — both already call a reset and read a notice, so this hooks
|
|
36
|
+
* into a pattern that exists rather than inventing one.
|
|
37
|
+
*
|
|
38
|
+
* The per-rule terminals (`.check()` / `.warn()` on a single rule) have no run
|
|
39
|
+
* boundary, so they do not render it. Same honest limitation `diff-disclosure`
|
|
40
|
+
* documents and for the same reason: a per-rule count presented as a run total
|
|
41
|
+
* would be wrong, and 79 lines on a 79-rule suite is not disclosure.
|
|
42
|
+
*/
|
|
43
|
+
/** Start a run. Both aggregating surfaces call this before evaluating. */
|
|
44
|
+
export declare function resetCommentSuppression(): void;
|
|
45
|
+
/** Record that a comment suppressed a finding. Called from `applyFilters`. */
|
|
46
|
+
export declare function recordCommentSuppression(ruleId: string, file: string): void;
|
|
47
|
+
/** Every suppression recorded this run, for `--format json`. */
|
|
48
|
+
export declare function commentSuppressions(): readonly {
|
|
49
|
+
ruleId: string;
|
|
50
|
+
file: string;
|
|
51
|
+
}[];
|
|
52
|
+
/**
|
|
53
|
+
* One or more lines naming what inline comments suppressed, or `undefined` when
|
|
54
|
+
* nothing was.
|
|
55
|
+
*
|
|
56
|
+
* `undefined` rather than `''` so a caller cannot print a blank line by
|
|
57
|
+
* forgetting to check — both call sites feed this straight to `writeStderr`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function commentSuppressionNotice(): string | undefined;
|
|
60
|
+
//# sourceMappingURL=comment-suppression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-suppression.d.ts","sourceRoot":"","sources":["../src/comment-suppression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAaH,0EAA0E;AAC1E,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3E;AAED,gEAAgE;AAChE,wBAAgB,mBAAmB,IAAI,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAEjF;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,SAAS,CA0B7D"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What inline exclusion comments suppressed, said out loud.
|
|
3
|
+
*
|
|
4
|
+
* Every other filter in the pipeline discloses itself: `.excluding()` warns on
|
|
5
|
+
* a pattern that matched nothing (`execute-rule.ts`), diff-aware has
|
|
6
|
+
* `suppressionNotice`, the baseline has `unmatchedBaselineFinding`. The inline
|
|
7
|
+
* comment filter dropped violations and returned no count — and after
|
|
8
|
+
* [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)
|
|
9
|
+
* made comments work for every condition family, it became the **widest** filter
|
|
10
|
+
* in the pipeline and the only silent one.
|
|
11
|
+
*
|
|
12
|
+
* `diff-disclosure.ts` states the principle this module inherits: *a run with
|
|
13
|
+
* every finding suppressed is indistinguishable from a clean run.* That is the
|
|
14
|
+
* false green ADR-008 exists to prevent, and it is worse here than for
|
|
15
|
+
* `--changed`, because a comment is written once and then read by nobody. The
|
|
16
|
+
* flag at least appears in the CI invocation.
|
|
17
|
+
*
|
|
18
|
+
* ## Identities, not a total (ADR-009 rule 4)
|
|
19
|
+
*
|
|
20
|
+
* The notice names **which rule in which file**, not just how many. A count is a
|
|
21
|
+
* snapshot: it moves when anything moves and tells the reader nothing about what
|
|
22
|
+
* to look at. `arch/no-cycles in src/legacy/gateway.ts` is an identity — it
|
|
23
|
+
* survives a renumbering and it is greppable.
|
|
24
|
+
*
|
|
25
|
+
* Capped, and the cap is disclosed rather than silent: a suite with hundreds of
|
|
26
|
+
* exemptions should not print hundreds of lines on every green run, but
|
|
27
|
+
* "and 47 more" is a very different statement from printing 3 and implying that
|
|
28
|
+
* is all of them.
|
|
29
|
+
*
|
|
30
|
+
* ## Why module state
|
|
31
|
+
*
|
|
32
|
+
* `applyFilters` runs once per rule and has no view of the run. This is the same
|
|
33
|
+
* shape `edge-coverage.ts` uses for the same reason: accumulate per rule, reset
|
|
34
|
+
* at the run boundary, render once. The two aggregating surfaces — CLI `check`
|
|
35
|
+
* and `checkAll` — both already call a reset and read a notice, so this hooks
|
|
36
|
+
* into a pattern that exists rather than inventing one.
|
|
37
|
+
*
|
|
38
|
+
* The per-rule terminals (`.check()` / `.warn()` on a single rule) have no run
|
|
39
|
+
* boundary, so they do not render it. Same honest limitation `diff-disclosure`
|
|
40
|
+
* documents and for the same reason: a per-rule count presented as a run total
|
|
41
|
+
* would be wrong, and 79 lines on a 79-rule suite is not disclosure.
|
|
42
|
+
*/
|
|
43
|
+
let suppressions = [];
|
|
44
|
+
/** How many identities the notice lists before summarising the remainder. */
|
|
45
|
+
const LISTED = 5;
|
|
46
|
+
/** Start a run. Both aggregating surfaces call this before evaluating. */
|
|
47
|
+
export function resetCommentSuppression() {
|
|
48
|
+
suppressions = [];
|
|
49
|
+
}
|
|
50
|
+
/** Record that a comment suppressed a finding. Called from `applyFilters`. */
|
|
51
|
+
export function recordCommentSuppression(ruleId, file) {
|
|
52
|
+
suppressions.push({ ruleId, file });
|
|
53
|
+
}
|
|
54
|
+
/** Every suppression recorded this run, for `--format json`. */
|
|
55
|
+
export function commentSuppressions() {
|
|
56
|
+
return suppressions;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One or more lines naming what inline comments suppressed, or `undefined` when
|
|
60
|
+
* nothing was.
|
|
61
|
+
*
|
|
62
|
+
* `undefined` rather than `''` so a caller cannot print a blank line by
|
|
63
|
+
* forgetting to check — both call sites feed this straight to `writeStderr`.
|
|
64
|
+
*/
|
|
65
|
+
export function commentSuppressionNotice() {
|
|
66
|
+
if (suppressions.length === 0)
|
|
67
|
+
return undefined;
|
|
68
|
+
const total = suppressions.length;
|
|
69
|
+
const findings = total === 1 ? 'finding' : 'findings';
|
|
70
|
+
const head = `[eess] ${String(total)} ${findings} suppressed by inline ` +
|
|
71
|
+
`// eess-exclude comments. They are exemptions, not passes:`;
|
|
72
|
+
// Deduplicate by identity so one rule silenced ten times in one file reads as
|
|
73
|
+
// one line with a count, rather than ten lines that look like ten problems.
|
|
74
|
+
const byIdentity = new Map();
|
|
75
|
+
for (const s of suppressions) {
|
|
76
|
+
const key = `${s.ruleId} in ${s.file === '' ? '(no file)' : s.file}`;
|
|
77
|
+
byIdentity.set(key, (byIdentity.get(key) ?? 0) + 1);
|
|
78
|
+
}
|
|
79
|
+
const entries = [...byIdentity.entries()];
|
|
80
|
+
const shown = entries.slice(0, LISTED);
|
|
81
|
+
const lines = shown.map(([key, n]) => ` - ${key}${n > 1 ? ` (${String(n)}×)` : ''}`);
|
|
82
|
+
// The cap is stated. A silent truncation reads as "that is all of them", which
|
|
83
|
+
// is the same lie as no disclosure at all.
|
|
84
|
+
if (entries.length > LISTED) {
|
|
85
|
+
lines.push(` - …and ${String(entries.length - LISTED)} more`);
|
|
86
|
+
}
|
|
87
|
+
return [head, ...lines].join('\n');
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=comment-suppression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-suppression.js","sourceRoot":"","sources":["../src/comment-suppression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAQH,IAAI,YAAY,GAAkB,EAAE,CAAA;AAEpC,6EAA6E;AAC7E,MAAM,MAAM,GAAG,CAAC,CAAA;AAEhB,0EAA0E;AAC1E,MAAM,UAAU,uBAAuB;IACrC,YAAY,GAAG,EAAE,CAAA;AACnB,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,wBAAwB,CAAC,MAAc,EAAE,IAAY;IACnE,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;AACrC,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,mBAAmB;IACjC,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAE/C,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAA;IACjC,MAAM,QAAQ,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAA;IACrD,MAAM,IAAI,GACR,UAAU,MAAM,CAAC,KAAK,CAAC,IAAI,QAAQ,wBAAwB;QAC3D,4DAA4D,CAAA;IAE9D,8EAA8E;IAC9E,4EAA4E;IAC5E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAA;IAC5C,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACpE,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACrD,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAA;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACrF,+EAA+E;IAC/E,2CAA2C;IAC3C,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;IAChE,CAAC;IACD,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACpC,CAAC"}
|
package/dist/condition.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ArchViolation } from './violation.js';
|
|
2
|
+
import type { DeclaredGlobs } from './glob-site.js';
|
|
2
3
|
/**
|
|
3
4
|
* Context passed to conditions during evaluation.
|
|
4
5
|
*
|
|
@@ -52,5 +53,10 @@ export interface Condition<T> {
|
|
|
52
53
|
* @returns Violations for elements that don't satisfy the condition
|
|
53
54
|
*/
|
|
54
55
|
evaluate(elements: T[], context: ConditionContext): ArchViolation[];
|
|
56
|
+
/**
|
|
57
|
+
* The path globs this condition matches against, if any. See
|
|
58
|
+
* `Predicate.globs` — same contract, stamped with `position: 'condition'`.
|
|
59
|
+
*/
|
|
60
|
+
readonly globs?: DeclaredGlobs;
|
|
55
61
|
}
|
|
56
62
|
//# sourceMappingURL=condition.d.ts.map
|
package/dist/condition.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"condition.d.ts","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAA;IACZ,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAE5B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"condition.d.ts","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAA;IACZ,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAE5B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAAA;IAEnE;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAC/B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F2 — the shared set-difference + non-vacuity core.
|
|
3
|
+
*
|
|
4
|
+
* Pure and engine-neutral: it compares two key sets by **identity** and reports
|
|
5
|
+
* the asymmetric differences plus emptiness. It never counts and never decides
|
|
6
|
+
* policy — the caller chooses which differences are violations (coverage /
|
|
7
|
+
* orphans / bijection) and how to treat an empty side (non-vacuity).
|
|
8
|
+
*
|
|
9
|
+
* Both `correspondence()` (plan 0064+/proposal 017) and `crossLayer`'s
|
|
10
|
+
* existence check (`haveMatchingCounterpart`) build on this one function, so
|
|
11
|
+
* the two "every X has a matching Y" engines can never drift — and neither can
|
|
12
|
+
* silently green on an empty side (ADR-008).
|
|
13
|
+
*/
|
|
14
|
+
/** The identity comparison of two key sets. No counts — sets of keys. */
|
|
15
|
+
export interface CorrespondenceResult {
|
|
16
|
+
/** Side-A keys with no match in side B — coverage gaps. */
|
|
17
|
+
readonly missing: readonly string[];
|
|
18
|
+
/** Side-B keys with no match in side A — orphans. */
|
|
19
|
+
readonly orphans: readonly string[];
|
|
20
|
+
/** True when side A contributed zero keys (non-vacuity signal). */
|
|
21
|
+
readonly aEmpty: boolean;
|
|
22
|
+
/** True when side B contributed zero keys (non-vacuity signal). */
|
|
23
|
+
readonly bEmpty: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Compare two key sets by identity.
|
|
27
|
+
*
|
|
28
|
+
* `missing` = A \ B (every A-key must have a B match for coverage).
|
|
29
|
+
* `orphans` = B \ A (every B-key must have an A source for no-orphans).
|
|
30
|
+
* Bijection holds iff both are empty. Duplicate keys within a side collapse
|
|
31
|
+
* (set semantics); order of `missing`/`orphans` follows first appearance in
|
|
32
|
+
* the respective input for stable, readable output.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setCorrespondence(aKeys: Iterable<string>, bKeys: Iterable<string>): CorrespondenceResult;
|
|
35
|
+
//# sourceMappingURL=correspondence-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correspondence-core.d.ts","sourceRoot":"","sources":["../src/correspondence-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,yEAAyE;AACzE,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GACtB,oBAAoB,CAetB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F2 — the shared set-difference + non-vacuity core.
|
|
3
|
+
*
|
|
4
|
+
* Pure and engine-neutral: it compares two key sets by **identity** and reports
|
|
5
|
+
* the asymmetric differences plus emptiness. It never counts and never decides
|
|
6
|
+
* policy — the caller chooses which differences are violations (coverage /
|
|
7
|
+
* orphans / bijection) and how to treat an empty side (non-vacuity).
|
|
8
|
+
*
|
|
9
|
+
* Both `correspondence()` (plan 0064+/proposal 017) and `crossLayer`'s
|
|
10
|
+
* existence check (`haveMatchingCounterpart`) build on this one function, so
|
|
11
|
+
* the two "every X has a matching Y" engines can never drift — and neither can
|
|
12
|
+
* silently green on an empty side (ADR-008).
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Compare two key sets by identity.
|
|
16
|
+
*
|
|
17
|
+
* `missing` = A \ B (every A-key must have a B match for coverage).
|
|
18
|
+
* `orphans` = B \ A (every B-key must have an A source for no-orphans).
|
|
19
|
+
* Bijection holds iff both are empty. Duplicate keys within a side collapse
|
|
20
|
+
* (set semantics); order of `missing`/`orphans` follows first appearance in
|
|
21
|
+
* the respective input for stable, readable output.
|
|
22
|
+
*/
|
|
23
|
+
export function setCorrespondence(aKeys, bKeys) {
|
|
24
|
+
const a = new Set(aKeys);
|
|
25
|
+
const b = new Set(bKeys);
|
|
26
|
+
const missing = [];
|
|
27
|
+
for (const key of a) {
|
|
28
|
+
if (!b.has(key))
|
|
29
|
+
missing.push(key);
|
|
30
|
+
}
|
|
31
|
+
const orphans = [];
|
|
32
|
+
for (const key of b) {
|
|
33
|
+
if (!a.has(key))
|
|
34
|
+
orphans.push(key);
|
|
35
|
+
}
|
|
36
|
+
return { missing, orphans, aEmpty: a.size === 0, bEmpty: b.size === 0 };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=correspondence-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correspondence-core.js","sourceRoot":"","sources":["../src/correspondence-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAcH;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAuB,EACvB,KAAuB;IAEvB,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACxB,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IAExB,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;QACpB,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACpC,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,KAAK,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;QACpB,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACpC,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAA;AACzE,CAAC"}
|
package/dist/correspondence.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { TerminalBuilder } from './terminal-builder.js';
|
|
1
|
+
import { TerminalBuilder, type CollectResult } from './terminal-builder.js';
|
|
3
2
|
/** How to identify an element for a two-sided violation message. */
|
|
4
3
|
export interface ElementInfo {
|
|
5
4
|
readonly name: string;
|
|
@@ -65,13 +64,36 @@ export declare class CorrespondenceBuilder<L, R> extends TerminalBuilder {
|
|
|
65
64
|
should(): this;
|
|
66
65
|
/** Chain another correspondence check (AND). */
|
|
67
66
|
andShould(): this;
|
|
68
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Every element on the named side(s) has a counterpart on the other. An
|
|
69
|
+
* absence assertion — "no element lacks a counterpart" — so it's marked
|
|
70
|
+
* cardinality-exempt (see `assertsCardinality()`).
|
|
71
|
+
*/
|
|
69
72
|
beComplete(opts?: {
|
|
70
73
|
direction?: Direction;
|
|
71
74
|
}): this;
|
|
72
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Related endpoints on one side have matching relations on the other. Also
|
|
77
|
+
* an absence assertion — "no matched pair disagrees" — cardinality-exempt
|
|
78
|
+
* for the same reason `beComplete()` is.
|
|
79
|
+
*/
|
|
73
80
|
preserveRelations(spec: RelationSpec<L, R>): this;
|
|
74
|
-
protected collectViolations():
|
|
81
|
+
protected collectViolations(): CollectResult;
|
|
82
|
+
/**
|
|
83
|
+
* ADR-010: mirrors `RuleBuilder.assertsCardinality()` — per-check, not a
|
|
84
|
+
* blanket class-wide exemption. `beComplete()`/`preserveRelations()` mark
|
|
85
|
+
* the function they push via `marksAssertsCardinality()`, the same
|
|
86
|
+
* unforgeable registry `.notExist()` uses, because both are absence
|
|
87
|
+
* assertions ("no element lacks a counterpart", "no matched pair
|
|
88
|
+
* disagrees") that can never produce a violation from an empty side — an
|
|
89
|
+
* empty selection here is the correctly computed input (e.g. release-gate's
|
|
90
|
+
* "no packages changed this diff" on a clean tree), not a broken instrument.
|
|
91
|
+
* `.every()`, not `.some()`, for the same reason `RuleBuilder` uses it: a
|
|
92
|
+
* future check type that isn't an absence assertion must not silently
|
|
93
|
+
* inherit this exemption just because it's chained alongside one that is.
|
|
94
|
+
* An empty `_checks` list is not exempt — nothing was asserted yet.
|
|
95
|
+
*/
|
|
96
|
+
protected assertsCardinality(): boolean;
|
|
75
97
|
}
|
|
76
98
|
/** Bind two selections into a correspondence. */
|
|
77
99
|
export declare function correspondence<L, R>(opts: CorrespondenceOptions<L, R>): CorrespondenceBuilder<L, R>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"correspondence.d.ts","sourceRoot":"","sources":["../src/correspondence.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"correspondence.d.ts","sourceRoot":"","sources":["../src/correspondence.ts"],"names":[],"mappings":"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"}
|
package/dist/correspondence.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TerminalBuilder } from './terminal-builder.js';
|
|
2
2
|
import { matchSelections } from './matching.js';
|
|
3
|
+
import { marksAssertsCardinality, assertsCardinality as checkAssertsCardinality, } from './cardinality.js';
|
|
3
4
|
function violationFor(info, message, ruleId) {
|
|
4
5
|
return {
|
|
5
6
|
rule: 'correspondence',
|
|
@@ -33,19 +34,52 @@ export class CorrespondenceBuilder extends TerminalBuilder {
|
|
|
33
34
|
andShould() {
|
|
34
35
|
return this;
|
|
35
36
|
}
|
|
36
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Every element on the named side(s) has a counterpart on the other. An
|
|
39
|
+
* absence assertion — "no element lacks a counterpart" — so it's marked
|
|
40
|
+
* cardinality-exempt (see `assertsCardinality()`).
|
|
41
|
+
*/
|
|
37
42
|
beComplete(opts = {}) {
|
|
38
|
-
this._checks.push((o, ruleId) => completeness(o, opts.direction ?? 'both', ruleId));
|
|
43
|
+
this._checks.push(marksAssertsCardinality((o, ruleId) => completeness(o, opts.direction ?? 'both', ruleId)));
|
|
39
44
|
return this;
|
|
40
45
|
}
|
|
41
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Related endpoints on one side have matching relations on the other. Also
|
|
48
|
+
* an absence assertion — "no matched pair disagrees" — cardinality-exempt
|
|
49
|
+
* for the same reason `beComplete()` is.
|
|
50
|
+
*/
|
|
42
51
|
preserveRelations(spec) {
|
|
43
|
-
this._checks.push((o, ruleId) => relations(o, spec, ruleId));
|
|
52
|
+
this._checks.push(marksAssertsCardinality((o, ruleId) => relations(o, spec, ruleId)));
|
|
44
53
|
return this;
|
|
45
54
|
}
|
|
46
55
|
collectViolations() {
|
|
47
56
|
const ruleId = this._metadata?.id;
|
|
48
|
-
|
|
57
|
+
const violations = this._checks.flatMap((check) => check(this.opts, ruleId));
|
|
58
|
+
// ADR-010: the unit is the key sets of both sides — a correspondence over
|
|
59
|
+
// an empty left AND an empty right examined nothing, regardless of how
|
|
60
|
+
// many checks were chained; one side alone being empty is still real
|
|
61
|
+
// examination of the other.
|
|
62
|
+
const examined = this.opts.left.elements.length + this.opts.right.elements.length;
|
|
63
|
+
return { violations, examined };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* ADR-010: mirrors `RuleBuilder.assertsCardinality()` — per-check, not a
|
|
67
|
+
* blanket class-wide exemption. `beComplete()`/`preserveRelations()` mark
|
|
68
|
+
* the function they push via `marksAssertsCardinality()`, the same
|
|
69
|
+
* unforgeable registry `.notExist()` uses, because both are absence
|
|
70
|
+
* assertions ("no element lacks a counterpart", "no matched pair
|
|
71
|
+
* disagrees") that can never produce a violation from an empty side — an
|
|
72
|
+
* empty selection here is the correctly computed input (e.g. release-gate's
|
|
73
|
+
* "no packages changed this diff" on a clean tree), not a broken instrument.
|
|
74
|
+
* `.every()`, not `.some()`, for the same reason `RuleBuilder` uses it: a
|
|
75
|
+
* future check type that isn't an absence assertion must not silently
|
|
76
|
+
* inherit this exemption just because it's chained alongside one that is.
|
|
77
|
+
* An empty `_checks` list is not exempt — nothing was asserted yet.
|
|
78
|
+
*/
|
|
79
|
+
assertsCardinality() {
|
|
80
|
+
if (this._checks.length === 0)
|
|
81
|
+
return false;
|
|
82
|
+
return this._checks.every((check) => checkAssertsCardinality(check));
|
|
49
83
|
}
|
|
50
84
|
}
|
|
51
85
|
/** Bind two selections into a correspondence. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"correspondence.js","sourceRoot":"","sources":["../src/correspondence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"correspondence.js","sourceRoot":"","sources":["../src/correspondence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One option, one finding — plan 0074, deciding 0069's appendix item 4.
|
|
3
|
+
*
|
|
4
|
+
* A preset generates rules combinatorially. `strictBoundaries` emits one
|
|
5
|
+
* `shared-isolation` rule per (shared glob × boundary folder) pair, so one
|
|
6
|
+
* wrong character in one option produces a finding per pair — same id, same
|
|
7
|
+
* glob, same cause, same remedy.
|
|
8
|
+
*
|
|
9
|
+
* **Measured on this repository**, `strictBoundaries({ folders: '**\/src/*',
|
|
10
|
+
* shared: ['**\/src/not-built-yet/**'] })`: 84 rules, **83 configuration
|
|
11
|
+
* findings, 2 distinct messages**. (0069's appendix recorded 38 for the same
|
|
12
|
+
* shape; the repo has grown more boundary folders since.) Every one is *true*,
|
|
13
|
+
* which is why exempting them is wrong — bug 0018 is precisely a preset
|
|
14
|
+
* silently enforcing nothing. But a report shaped like that makes a one-line
|
|
15
|
+
* fix look like an 83-line disaster, and it contradicts this project's own rule
|
|
16
|
+
* that findings are **identities, never totals**.
|
|
17
|
+
*
|
|
18
|
+
* So the fan-out collapses to the identity that matches the edit: the option
|
|
19
|
+
* the user actually wrote. The count survives as *context on that finding*
|
|
20
|
+
* rather than as 83 rows — which is not the snapshot ADR-009 rule 4 bars,
|
|
21
|
+
* because it is attached to a named identity rather than standing in for one.
|
|
22
|
+
*
|
|
23
|
+
* ## What the key is, and why each part is in it
|
|
24
|
+
*
|
|
25
|
+
* `(rule file, rule id, offending glob)`.
|
|
26
|
+
*
|
|
27
|
+
* - **The glob, not the message.** 0069's appendix is explicit: two different
|
|
28
|
+
* `shared` entries that both match nothing are **two findings, because they
|
|
29
|
+
* are two edits**. Keying on the message would merge them whenever the
|
|
30
|
+
* wording happened to coincide, and split them whenever it did not.
|
|
31
|
+
* - **The rule id**, so `shared-discovery` and `shared-isolation` stay
|
|
32
|
+
* distinct: they are different faults about the same glob and have different
|
|
33
|
+
* remedies.
|
|
34
|
+
* - **The rule file.** Two rule files that instantiate the same preset with the
|
|
35
|
+
* same bad glob are two edits in two places, and collapsing them would hide
|
|
36
|
+
* one. Empty in a test-file context, where every rule shares one origin, so
|
|
37
|
+
* the key degrades to `(id, glob)` there rather than merging across files
|
|
38
|
+
* that were never distinguished.
|
|
39
|
+
*
|
|
40
|
+
* ## What is deliberately NOT deduplicated
|
|
41
|
+
*
|
|
42
|
+
* Ordinary violations. Each names a distinct element at a distinct position and
|
|
43
|
+
* every one is a separate edit — collapsing them is the snapshot rule 4 bars,
|
|
44
|
+
* and it would hide real work. Only **configuration findings** fan out from a
|
|
45
|
+
* single authored mistake, and `bypassFilters` is exactly the flag that marks
|
|
46
|
+
* one.
|
|
47
|
+
*/
|
|
48
|
+
import type { ArchViolation } from './violation.js';
|
|
49
|
+
/**
|
|
50
|
+
* Collapse each configuration finding that a preset fanned out.
|
|
51
|
+
*
|
|
52
|
+
* Order-preserving, keeping the **first** occurrence, so the report reads in
|
|
53
|
+
* the order the rules were declared rather than in hash order.
|
|
54
|
+
*/
|
|
55
|
+
export declare function dedupeConfigFindings(violations: readonly ArchViolation[]): ArchViolation[];
|
|
56
|
+
//# sourceMappingURL=dedupe-config-findings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupe-config-findings.d.ts","sourceRoot":"","sources":["../src/dedupe-config-findings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAUnD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,GAAG,aAAa,EAAE,CAsC1F"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** How the surviving finding states the fan-out it stands for. */
|
|
2
|
+
function affectedNote(count) {
|
|
3
|
+
return (` This one option generated ${String(count)} rules that cannot enforce anything; they are ` +
|
|
4
|
+
`not listed separately because they are one edit.`);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Collapse each configuration finding that a preset fanned out.
|
|
8
|
+
*
|
|
9
|
+
* Order-preserving, keeping the **first** occurrence, so the report reads in
|
|
10
|
+
* the order the rules were declared rather than in hash order.
|
|
11
|
+
*/
|
|
12
|
+
export function dedupeConfigFindings(violations) {
|
|
13
|
+
// Count first, then emit. A single pass that appends the note on first sight
|
|
14
|
+
// cannot know the total yet, and appending it on the last sight would reorder
|
|
15
|
+
// the report.
|
|
16
|
+
const counts = new Map();
|
|
17
|
+
for (const violation of violations) {
|
|
18
|
+
const key = keyFor(violation);
|
|
19
|
+
if (key === undefined)
|
|
20
|
+
continue;
|
|
21
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
22
|
+
}
|
|
23
|
+
const emitted = new Set();
|
|
24
|
+
const result = [];
|
|
25
|
+
for (const violation of violations) {
|
|
26
|
+
const key = keyFor(violation);
|
|
27
|
+
if (key === undefined) {
|
|
28
|
+
result.push(violation);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (emitted.has(key))
|
|
32
|
+
continue;
|
|
33
|
+
emitted.add(key);
|
|
34
|
+
const total = counts.get(key) ?? 1;
|
|
35
|
+
if (total === 1) {
|
|
36
|
+
result.push(violation);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// The note goes on `message` AND `suggestion`. They are separate fields with
|
|
40
|
+
// separate consumers — the terminal prints `Fix:` from `suggestion`, and
|
|
41
|
+
// `--format json` carries both — and a reader who sees only one of them
|
|
42
|
+
// otherwise learns a different number of affected rules from each.
|
|
43
|
+
result.push({
|
|
44
|
+
...violation,
|
|
45
|
+
message: violation.message + affectedNote(total),
|
|
46
|
+
suggestion: violation.suggestion === undefined ? undefined : violation.suggestion + affectedNote(total),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The dedupe key, or `undefined` for a finding that must never be collapsed.
|
|
53
|
+
*
|
|
54
|
+
* `undefined` for anything that is not a configuration finding, and for a
|
|
55
|
+
* configuration finding with no `element` to key on — a missing key must mean
|
|
56
|
+
* "keep it", never "merge everything that lacks one".
|
|
57
|
+
*/
|
|
58
|
+
function keyFor(violation) {
|
|
59
|
+
if (violation.bypassFilters !== true)
|
|
60
|
+
return undefined;
|
|
61
|
+
const identity = violation.ruleId ?? violation.rule;
|
|
62
|
+
// `'unnamed'` is `TerminalBuilder.describeRule()`'s SENTINEL, not an identity.
|
|
63
|
+
// A family that never overrides it gives every one of its rules the same key,
|
|
64
|
+
// so distinct rules merged and the survivor claimed they were "one edit" —
|
|
65
|
+
// measured on three different `duplicateBodies` detectors in plan 0099. A
|
|
66
|
+
// missing key must mean "keep it", never "merge everything that lacks one",
|
|
67
|
+
// which is the rule the empty-string guard beside this already follows.
|
|
68
|
+
if (identity === '' || identity === 'unnamed')
|
|
69
|
+
return undefined;
|
|
70
|
+
if (violation.element === '' || violation.element === 'unnamed')
|
|
71
|
+
return undefined;
|
|
72
|
+
return `${violation.file} ${identity} ${violation.element}`;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=dedupe-config-findings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupe-config-findings.js","sourceRoot":"","sources":["../src/dedupe-config-findings.ts"],"names":[],"mappings":"AAiDA,kEAAkE;AAClE,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,CACL,8BAA8B,MAAM,CAAC,KAAK,CAAC,gDAAgD;QAC3F,kDAAkD,CACnD,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAoC;IACvE,6EAA6E;IAC7E,8EAA8E;IAC9E,cAAc;IACd,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IACxC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;QAC7B,IAAI,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IAC7C,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,MAAM,MAAM,GAAoB,EAAE,CAAA;IAClC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;QAC7B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACtB,SAAQ;QACV,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAChB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACtB,SAAQ;QACV,CAAC;QACD,6EAA6E;QAC7E,yEAAyE;QACzE,wEAAwE;QACxE,mEAAmE;QACnE,MAAM,CAAC,IAAI,CAAC;YACV,GAAG,SAAS;YACZ,OAAO,EAAE,SAAS,CAAC,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC;YAChD,UAAU,EACR,SAAS,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC;SAC9F,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,MAAM,CAAC,SAAwB;IACtC,IAAI,SAAS,CAAC,aAAa,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACtD,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,IAAI,SAAS,CAAC,IAAI,CAAA;IACnD,+EAA+E;IAC/E,8EAA8E;IAC9E,2EAA2E;IAC3E,0EAA0E;IAC1E,4EAA4E;IAC5E,wEAAwE;IACxE,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC/D,IAAI,SAAS,CAAC,OAAO,KAAK,EAAE,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACjF,OAAO,GAAG,SAAS,CAAC,IAAI,IAAI,QAAQ,IAAI,SAAS,CAAC,OAAO,EAAE,CAAA;AAC7D,CAAC"}
|
package/dist/define.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Predicate } from './predicate.js';
|
|
2
|
+
import type { DeclaredGlobs } from './glob-site.js';
|
|
2
3
|
import type { Condition, ConditionContext } from './condition.js';
|
|
3
4
|
import type { ArchViolation } from './violation.js';
|
|
4
5
|
/**
|
|
@@ -15,8 +16,40 @@ import type { ArchViolation } from './violation.js';
|
|
|
15
16
|
*
|
|
16
17
|
* classes(p).that().satisfy(isAbstract).should().beExported().check()
|
|
17
18
|
* ```
|
|
19
|
+
*
|
|
20
|
+
* ## Declaring a glob
|
|
21
|
+
*
|
|
22
|
+
* If the predicate matches paths against a glob, pass it as `globs` — otherwise
|
|
23
|
+
* `doctor` cannot see it, and a typo'd glob narrows the selection to nothing
|
|
24
|
+
* while reporting a clean bill of health. This is a **selector**-position glob,
|
|
25
|
+
* the one position that is acted on today, which is why the omission was bug
|
|
26
|
+
* 0030's higher-severity half.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const inGeneratedFolder = definePredicate<SourceFile>(
|
|
31
|
+
* "reside in '**\/generated/**'",
|
|
32
|
+
* (file) => picomatch('**\/generated/**')(file.getFilePath()),
|
|
33
|
+
* globNode({ glob: '**\/generated/**', kind: 'file-path' }),
|
|
34
|
+
* )
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* The `kind` must say what the glob is really matched against, because it
|
|
38
|
+
* selects which paths the glob is checked for satisfiability:
|
|
39
|
+
*
|
|
40
|
+
* - `file-path` — an **absolute** file path. Anchor the glob (`'**\/src/**'`,
|
|
41
|
+
* not `'src/**'`), or it matches nothing.
|
|
42
|
+
* - `parent-dir` — the immediate parent directory of a file.
|
|
43
|
+
* - `import-target` — a resolved module path **or a bare specifier**.
|
|
44
|
+
* Deliberately has no path universe, so `'fastify'` is never reported dead.
|
|
45
|
+
* - `specifier` / `literal` — a string in the source rather than a path.
|
|
46
|
+
*
|
|
47
|
+
* A wrong `kind` is believed: declaring a bare specifier as `file-path` earns a
|
|
48
|
+
* false dead-glob report, and declaring a real path as `import-target` silently
|
|
49
|
+
* exempts it from checking. When unsure, the honest choice is to declare
|
|
50
|
+
* nothing — the same position as before this parameter existed.
|
|
18
51
|
*/
|
|
19
|
-
export declare function definePredicate<T>(description: string, test: (element: T) => boolean): Predicate<T>;
|
|
52
|
+
export declare function definePredicate<T>(description: string, test: (element: T) => boolean, globs?: DeclaredGlobs): Predicate<T>;
|
|
20
53
|
/**
|
|
21
54
|
* Create a custom condition for use in `.should().satisfy()` chains.
|
|
22
55
|
*
|
|
@@ -36,6 +69,15 @@ export declare function definePredicate<T>(description: string, test: (element:
|
|
|
36
69
|
*
|
|
37
70
|
* classes(p).that().extend('Base').should().satisfy(useSharedHelper).check()
|
|
38
71
|
* ```
|
|
72
|
+
*
|
|
73
|
+
* ## Declaring a glob
|
|
74
|
+
*
|
|
75
|
+
* As with `definePredicate`, pass `globs` if the condition matches paths against
|
|
76
|
+
* one — see that function for the `kind` values and what a wrong one costs. The
|
|
77
|
+
* difference is what happens next: a **condition**-position glob is deliberately
|
|
78
|
+
* *not* reported when it matches nothing, because a denylist glob matching
|
|
79
|
+
* nothing is indistinguishable from a ban being respected. Declaring it makes it
|
|
80
|
+
* visible to `globs()` and `explain`; it does not make it a finding.
|
|
39
81
|
*/
|
|
40
|
-
export declare function defineCondition<T>(description: string, evaluate: (elements: T[], context: ConditionContext) => ArchViolation[]): Condition<T>;
|
|
82
|
+
export declare function defineCondition<T>(description: string, evaluate: (elements: T[], context: ConditionContext) => ArchViolation[], globs?: DeclaredGlobs): Condition<T>;
|
|
41
83
|
//# sourceMappingURL=define.d.ts.map
|
package/dist/define.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,EAC7B,KAAK,CAAC,EAAE,aAAa,GACpB,SAAS,CAAC,CAAC,CAAC,CAEd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,gBAAgB,KAAK,aAAa,EAAE,EACvE,KAAK,CAAC,EAAE,aAAa,GACpB,SAAS,CAAC,CAAC,CAAC,CAEd"}
|