@shrkcrft/boundaries 0.1.0-alpha.30 → 0.1.0-alpha.31
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/dist/baseline/compute-baseline.d.ts +8 -0
- package/dist/baseline/compute-baseline.d.ts.map +1 -1
- package/dist/baseline/compute-baseline.js +8 -7
- package/dist/baseline/diff-baseline.d.ts +28 -0
- package/dist/baseline/diff-baseline.d.ts.map +1 -1
- package/dist/baseline/diff-baseline.js +29 -0
- package/dist/evaluate/boundary-unit-finding.d.ts +10 -0
- package/dist/evaluate/boundary-unit-finding.d.ts.map +1 -0
- package/dist/evaluate/boundary-unit-finding.js +21 -0
- package/dist/evaluate/boundary-unit-kind.d.ts +7 -0
- package/dist/evaluate/boundary-unit-kind.d.ts.map +1 -0
- package/dist/evaluate/boundary-unit-kind.js +14 -0
- package/dist/evaluate/evaluate-boundaries.d.ts +230 -3
- package/dist/evaluate/evaluate-boundaries.d.ts.map +1 -1
- package/dist/evaluate/evaluate-boundaries.js +481 -53
- package/dist/evaluate/i-boundary-rule-settle-input.d.ts +27 -0
- package/dist/evaluate/i-boundary-rule-settle-input.d.ts.map +1 -0
- package/dist/evaluate/i-boundary-rule-settle-input.js +1 -0
- package/dist/evaluate/i-boundary-rule-settlement.d.ts +23 -0
- package/dist/evaluate/i-boundary-rule-settlement.d.ts.map +1 -0
- package/dist/evaluate/i-boundary-rule-settlement.js +1 -0
- package/dist/evaluate/i-boundary-unit-finding.d.ts +22 -0
- package/dist/evaluate/i-boundary-unit-finding.d.ts.map +1 -0
- package/dist/evaluate/i-boundary-unit-finding.js +1 -0
- package/dist/evaluate/settle-boundary-rule.d.ts +23 -0
- package/dist/evaluate/settle-boundary-rule.d.ts.map +1 -0
- package/dist/evaluate/settle-boundary-rule.js +73 -0
- package/dist/evaluate/with-boundary-rule-settlement.d.ts +11 -0
- package/dist/evaluate/with-boundary-rule-settlement.d.ts.map +1 -0
- package/dist/evaluate/with-boundary-rule-settlement.js +44 -0
- package/dist/extract/code-zones.d.ts +95 -3
- package/dist/extract/code-zones.d.ts.map +1 -1
- package/dist/extract/code-zones.js +313 -4
- package/dist/extract/extract-tokens.d.ts +30 -0
- package/dist/extract/extract-tokens.d.ts.map +1 -1
- package/dist/extract/extract-tokens.js +233 -27
- package/dist/extract/i-labeled-source.d.ts +12 -0
- package/dist/extract/i-labeled-source.d.ts.map +1 -0
- package/dist/extract/i-labeled-source.js +1 -0
- package/dist/extract/import-edges.d.ts +1 -0
- package/dist/extract/import-edges.d.ts.map +1 -1
- package/dist/extract/import-edges.js +20 -7
- package/dist/extract/inspect-source.d.ts +32 -3
- package/dist/extract/inspect-source.d.ts.map +1 -1
- package/dist/extract/inspect-source.js +50 -7
- package/dist/extract/parse-imports.d.ts +34 -12
- package/dist/extract/parse-imports.d.ts.map +1 -1
- package/dist/extract/parse-imports.js +119 -20
- package/dist/extract/scan-literals.d.ts +12 -1
- package/dist/extract/scan-literals.d.ts.map +1 -1
- package/dist/extract/scan-literals.js +15 -1
- package/dist/extract/source-liveness-request.d.ts +24 -0
- package/dist/extract/source-liveness-request.d.ts.map +1 -0
- package/dist/extract/source-liveness-request.js +45 -0
- package/dist/generated/check-provenance.d.ts.map +1 -1
- package/dist/generated/check-provenance.js +3 -1
- package/dist/generated/read-regen-tree.d.ts +16 -0
- package/dist/generated/read-regen-tree.d.ts.map +1 -0
- package/dist/generated/read-regen-tree.js +61 -0
- package/dist/generated/scan-generated.d.ts +8 -0
- package/dist/generated/scan-generated.d.ts.map +1 -1
- package/dist/generated/scan-generated.js +25 -10
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -0
- package/dist/model/boundary-intended-empty-line.d.ts +10 -0
- package/dist/model/boundary-intended-empty-line.d.ts.map +1 -0
- package/dist/model/boundary-intended-empty-line.js +11 -0
- package/dist/model/boundary-markable-list.d.ts +15 -0
- package/dist/model/boundary-markable-list.d.ts.map +1 -0
- package/dist/model/boundary-markable-list.js +15 -0
- package/dist/model/boundary-rule-input-keys.d.ts +11 -0
- package/dist/model/boundary-rule-input-keys.d.ts.map +1 -0
- package/dist/model/boundary-rule-input-keys.js +29 -0
- package/dist/model/boundary-rule-input.d.ts +23 -0
- package/dist/model/boundary-rule-input.d.ts.map +1 -0
- package/dist/model/boundary-rule-input.js +1 -0
- package/dist/model/boundary-rule-key-problems.d.ts +11 -0
- package/dist/model/boundary-rule-key-problems.d.ts.map +1 -0
- package/dist/model/boundary-rule-key-problems.js +36 -0
- package/dist/model/boundary-rule-marker-problems.d.ts +26 -0
- package/dist/model/boundary-rule-marker-problems.d.ts.map +1 -0
- package/dist/model/boundary-rule-marker-problems.js +94 -0
- package/dist/model/boundary-rule-scope.d.ts +88 -0
- package/dist/model/boundary-rule-scope.d.ts.map +1 -0
- package/dist/model/boundary-rule-scope.js +175 -0
- package/dist/model/boundary-rule.d.ts +120 -5
- package/dist/model/boundary-rule.d.ts.map +1 -1
- package/dist/model/boundary-rule.js +148 -1
- package/dist/model/boundary-unit-problem-issue.d.ts +9 -0
- package/dist/model/boundary-unit-problem-issue.d.ts.map +1 -0
- package/dist/model/boundary-unit-problem-issue.js +16 -0
- package/dist/model/normalize-boundary-rule.d.ts +21 -0
- package/dist/model/normalize-boundary-rule.d.ts.map +1 -0
- package/dist/model/normalize-boundary-rule.js +62 -0
- package/dist/policy/evaluate-policy.d.ts +80 -3
- package/dist/policy/evaluate-policy.d.ts.map +1 -1
- package/dist/policy/evaluate-policy.js +152 -19
- package/dist/policy/i-policy-rule-liveness.d.ts +15 -0
- package/dist/policy/i-policy-rule-liveness.d.ts.map +1 -0
- package/dist/policy/i-policy-rule-liveness.js +1 -0
- package/dist/policy/run-policy.d.ts +1 -1
- package/dist/policy/run-policy.d.ts.map +1 -1
- package/dist/policy/run-policy.js +171 -21
- package/dist/registry/load-boundary-rules.d.ts +29 -1
- package/dist/registry/load-boundary-rules.d.ts.map +1 -1
- package/dist/registry/load-boundary-rules.js +38 -8
- package/dist/scan/glob.d.ts +90 -0
- package/dist/scan/glob.d.ts.map +1 -1
- package/dist/scan/glob.js +205 -0
- package/dist/scan/i-glob-unit-measure.d.ts +22 -0
- package/dist/scan/i-glob-unit-measure.d.ts.map +1 -0
- package/dist/scan/i-glob-unit-measure.js +1 -0
- package/dist/scan/import-pattern.d.ts +109 -0
- package/dist/scan/import-pattern.d.ts.map +1 -0
- package/dist/scan/import-pattern.js +191 -0
- package/dist/scan/node-builtin-package-names.d.ts +13 -0
- package/dist/scan/node-builtin-package-names.d.ts.map +1 -0
- package/dist/scan/node-builtin-package-names.js +25 -0
- package/dist/scan/scan-imports.d.ts +36 -3
- package/dist/scan/scan-imports.d.ts.map +1 -1
- package/dist/scan/scan-imports.js +114 -45
- package/dist/util/blank-run-hazard-finding.d.ts +34 -0
- package/dist/util/blank-run-hazard-finding.d.ts.map +1 -0
- package/dist/util/blank-run-hazard-finding.js +1 -0
- package/dist/util/blank-run-hazard.d.ts +29 -0
- package/dist/util/blank-run-hazard.d.ts.map +1 -0
- package/dist/util/blank-run-hazard.js +450 -0
- package/dist/util/dead-glob-units.d.ts +46 -0
- package/dist/util/dead-glob-units.d.ts.map +1 -0
- package/dist/util/dead-glob-units.js +116 -0
- package/dist/util/glob-list-liveness-input.d.ts +31 -0
- package/dist/util/glob-list-liveness-input.d.ts.map +1 -0
- package/dist/util/glob-list-liveness-input.js +65 -0
- package/dist/util/i-dead-glob-unit.d.ts +24 -0
- package/dist/util/i-dead-glob-unit.d.ts.map +1 -0
- package/dist/util/i-dead-glob-unit.js +1 -0
- package/dist/util/i-glob-list-units.d.ts +25 -0
- package/dist/util/i-glob-list-units.d.ts.map +1 -0
- package/dist/util/i-glob-list-units.js +1 -0
- package/dist/util/i-glob-liveness-list.d.ts +23 -0
- package/dist/util/i-glob-liveness-list.d.ts.map +1 -0
- package/dist/util/i-glob-liveness-list.js +1 -0
- package/dist/util/i-glob-liveness-request.d.ts +11 -0
- package/dist/util/i-glob-liveness-request.d.ts.map +1 -0
- package/dist/util/i-glob-liveness-request.js +1 -0
- package/dist/util/i-glob-negation.d.ts +13 -0
- package/dist/util/i-glob-negation.d.ts.map +1 -0
- package/dist/util/i-glob-negation.js +1 -0
- package/dist/util/matched-files.d.ts +22 -0
- package/dist/util/matched-files.d.ts.map +1 -0
- package/dist/util/matched-files.js +1 -0
- package/dist/util/negation-cause.d.ts +15 -0
- package/dist/util/negation-cause.d.ts.map +1 -0
- package/dist/util/negation-cause.js +21 -0
- package/dist/util/plane-scan-exclude-dirs.d.ts +17 -0
- package/dist/util/plane-scan-exclude-dirs.d.ts.map +1 -0
- package/dist/util/plane-scan-exclude-dirs.js +22 -0
- package/dist/util/read-glob-list-liveness.d.ts +20 -0
- package/dist/util/read-glob-list-liveness.d.ts.map +1 -0
- package/dist/util/read-glob-list-liveness.js +20 -0
- package/dist/util/read-scope-coverage.d.ts +90 -0
- package/dist/util/read-scope-coverage.d.ts.map +1 -0
- package/dist/util/read-scope-coverage.js +173 -0
- package/dist/util/read-scope.d.ts +14 -0
- package/dist/util/read-scope.d.ts.map +1 -0
- package/dist/util/read-scope.js +1 -0
- package/dist/util/read-selected-files.d.ts +16 -0
- package/dist/util/read-selected-files.d.ts.map +1 -0
- package/dist/util/read-selected-files.js +25 -0
- package/dist/util/settle-glob-lists.d.ts +12 -0
- package/dist/util/settle-glob-lists.d.ts.map +1 -0
- package/dist/util/settle-glob-lists.js +13 -0
- package/dist/util/unread-file-reason.d.ts +26 -0
- package/dist/util/unread-file-reason.d.ts.map +1 -0
- package/dist/util/unread-file-reason.js +26 -0
- package/dist/util/unread-file.d.ts +10 -0
- package/dist/util/unread-file.d.ts.map +1 -0
- package/dist/util/unread-file.js +1 -0
- package/dist/util/walk-files.d.ts +74 -10
- package/dist/util/walk-files.d.ts.map +1 -1
- package/dist/util/walk-files.js +137 -31
- package/dist/wiring/evaluate-wiring.d.ts +79 -5
- package/dist/wiring/evaluate-wiring.d.ts.map +1 -1
- package/dist/wiring/evaluate-wiring.js +267 -12
- package/dist/wiring/explain-wiring.d.ts +49 -5
- package/dist/wiring/explain-wiring.d.ts.map +1 -1
- package/dist/wiring/explain-wiring.js +118 -10
- package/dist/wiring/i-idiom-role-coverage.d.ts +24 -0
- package/dist/wiring/i-idiom-role-coverage.d.ts.map +1 -0
- package/dist/wiring/i-idiom-role-coverage.js +1 -0
- package/dist/wiring/i-registration-query-verdict.d.ts +38 -0
- package/dist/wiring/i-registration-query-verdict.d.ts.map +1 -0
- package/dist/wiring/i-registration-query-verdict.js +1 -0
- package/dist/wiring/i-registration-roles.d.ts +71 -0
- package/dist/wiring/i-registration-roles.d.ts.map +1 -0
- package/dist/wiring/i-registration-roles.js +1 -0
- package/dist/wiring/measure-idiom-role-coverage.d.ts +12 -0
- package/dist/wiring/measure-idiom-role-coverage.d.ts.map +1 -0
- package/dist/wiring/measure-idiom-role-coverage.js +20 -0
- package/dist/wiring/measure-registration-roles.d.ts +25 -0
- package/dist/wiring/measure-registration-roles.d.ts.map +1 -0
- package/dist/wiring/measure-registration-roles.js +124 -0
- package/dist/wiring/plan-wiring-fix.js +2 -2
- package/dist/wiring/registration-graph.d.ts +12 -0
- package/dist/wiring/registration-graph.d.ts.map +1 -1
- package/dist/wiring/registration-graph.js +16 -5
- package/dist/wiring/registration-query-verdict.d.ts +23 -0
- package/dist/wiring/registration-query-verdict.d.ts.map +1 -0
- package/dist/wiring/registration-query-verdict.js +118 -0
- package/dist/wiring/registry-query.d.ts +8 -0
- package/dist/wiring/registry-query.d.ts.map +1 -1
- package/dist/wiring/registry-query.js +12 -5
- package/dist/wiring/scan-wiring-files.d.ts +4 -2
- package/dist/wiring/scan-wiring-files.d.ts.map +1 -1
- package/dist/wiring/scan-wiring-files.js +52 -15
- package/dist/wiring/sink-imports.d.ts +11 -0
- package/dist/wiring/sink-imports.d.ts.map +1 -1
- package/dist/wiring/sink-imports.js +11 -2
- package/dist/wiring/trace-literal.d.ts +6 -0
- package/dist/wiring/trace-literal.d.ts.map +1 -1
- package/dist/wiring/trace-literal.js +5 -2
- package/dist/wiring/wiring-labeled-sources.d.ts +12 -0
- package/dist/wiring/wiring-labeled-sources.d.ts.map +1 -0
- package/dist/wiring/wiring-labeled-sources.js +20 -0
- package/package.json +2 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IUnreadFile } from './unread-file.js';
|
|
2
|
+
/**
|
|
3
|
+
* What one rule's walk covered: the glob-matched files in its scope that were
|
|
4
|
+
* READ, and the ones that were not. `readScopeCoverage` folds this into the
|
|
5
|
+
* rule's coverage record. It is the only function that decides how an unread
|
|
6
|
+
* file changes a verdict.
|
|
7
|
+
*/
|
|
8
|
+
export interface IReadScope {
|
|
9
|
+
/** Glob-matched files in the rule's scope that the reader read. */
|
|
10
|
+
readonly read: number;
|
|
11
|
+
/** Glob-matched files in the rule's scope that it did not read. */
|
|
12
|
+
readonly unread: readonly IUnreadFile[];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=read-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-scope.d.ts","sourceRoot":"","sources":["../../src/util/read-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IMatchedFiles } from './matched-files.js';
|
|
2
|
+
/**
|
|
3
|
+
* THE reader for ONE glob list: the one reader's positive walk
|
|
4
|
+
* (`readMatchingFiles`, memoised), narrowed to the files the list SELECTS
|
|
5
|
+
* (`globListSelects` — an inclusion glob matches, no negation does) and to the
|
|
6
|
+
* unread entries in front of it (`unreadMatching`, negation-aware).
|
|
7
|
+
*
|
|
8
|
+
* Every single-list plane reader goes through here — an extraction source, an
|
|
9
|
+
* extractor baseline, a doc-reference rule, a generated tree, a trace — so "which
|
|
10
|
+
* files does this list read?" has one answer, and an over-cap file the author
|
|
11
|
+
* excluded never makes the rule PARTIAL. A caller walking the UNION of several
|
|
12
|
+
* lists must not use this on the union (one list's `!` would leak into
|
|
13
|
+
* another's scope): walk the union, then select per list.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readSelectedFiles(root: string, globs: readonly string[], excludeDirs?: ReadonlySet<string>, allowDotDirs?: ReadonlySet<string>): IMatchedFiles;
|
|
16
|
+
//# sourceMappingURL=read-selected-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-selected-files.d.ts","sourceRoot":"","sources":["../../src/util/read-selected-files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,WAAW,GAAE,WAAW,CAAC,MAAM,CAAa,EAC5C,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,GAC5C,aAAa,CAOf"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { globListSelects } from "../scan/glob.js";
|
|
2
|
+
import { unreadMatching } from "./read-scope-coverage.js";
|
|
3
|
+
import { readMatchingFiles } from "./walk-files.js";
|
|
4
|
+
/**
|
|
5
|
+
* THE reader for ONE glob list: the one reader's positive walk
|
|
6
|
+
* (`readMatchingFiles`, memoised), narrowed to the files the list SELECTS
|
|
7
|
+
* (`globListSelects` — an inclusion glob matches, no negation does) and to the
|
|
8
|
+
* unread entries in front of it (`unreadMatching`, negation-aware).
|
|
9
|
+
*
|
|
10
|
+
* Every single-list plane reader goes through here — an extraction source, an
|
|
11
|
+
* extractor baseline, a doc-reference rule, a generated tree, a trace — so "which
|
|
12
|
+
* files does this list read?" has one answer, and an over-cap file the author
|
|
13
|
+
* excluded never makes the rule PARTIAL. A caller walking the UNION of several
|
|
14
|
+
* lists must not use this on the union (one list's `!` would leak into
|
|
15
|
+
* another's scope): walk the union, then select per list.
|
|
16
|
+
*/
|
|
17
|
+
export function readSelectedFiles(root, globs, excludeDirs = new Set(), allowDotDirs = new Set()) {
|
|
18
|
+
const matched = readMatchingFiles(root, globs, excludeDirs, allowDotDirs);
|
|
19
|
+
const files = new Map();
|
|
20
|
+
for (const [path, content] of matched.files) {
|
|
21
|
+
if (globListSelects(path, globs))
|
|
22
|
+
files.set(path, content);
|
|
23
|
+
}
|
|
24
|
+
return { files, unread: Object.freeze(unreadMatching(matched.unread, globs)) };
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ISettledUnitLiveness } from '@shrkcrft/core';
|
|
2
|
+
import type { IGlobLivenessRequest } from './i-glob-liveness-request.js';
|
|
3
|
+
/**
|
|
4
|
+
* Settle a gate rule's glob lists through core's one authority,
|
|
5
|
+
* `settleUnitLiveness`, over THE gate-plane observation predicate
|
|
6
|
+
* (`globListLivenessInput`) — the call every plane engine makes before it
|
|
7
|
+
* decides what an empty rule is (`settleRuleEmptiness` takes the result as its
|
|
8
|
+
* `liveness`). `settled.dead` holds UNMARKED dead units only; a marked glob is
|
|
9
|
+
* intended-empty or went-live, and `settled.acceptance` is its printed record.
|
|
10
|
+
*/
|
|
11
|
+
export declare function settleGlobLists(request: IGlobLivenessRequest): ISettledUnitLiveness;
|
|
12
|
+
//# sourceMappingURL=settle-glob-lists.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settle-glob-lists.d.ts","sourceRoot":"","sources":["../../src/util/settle-glob-lists.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEzE;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,oBAAoB,CAEnF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { settleUnitLiveness } from '@shrkcrft/core';
|
|
2
|
+
import { globListLivenessInput } from "./glob-list-liveness-input.js";
|
|
3
|
+
/**
|
|
4
|
+
* Settle a gate rule's glob lists through core's one authority,
|
|
5
|
+
* `settleUnitLiveness`, over THE gate-plane observation predicate
|
|
6
|
+
* (`globListLivenessInput`) — the call every plane engine makes before it
|
|
7
|
+
* decides what an empty rule is (`settleRuleEmptiness` takes the result as its
|
|
8
|
+
* `liveness`). `settled.dead` holds UNMARKED dead units only; a marked glob is
|
|
9
|
+
* intended-empty or went-live, and `settled.acceptance` is its printed record.
|
|
10
|
+
*/
|
|
11
|
+
export function settleGlobLists(request) {
|
|
12
|
+
return settleUnitLiveness(globListLivenessInput(request));
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why the one reader (`readMatchingFiles`) matched a file against a rule's
|
|
3
|
+
* globs but did not read it. Either way the file is IN the rule's scope and
|
|
4
|
+
* was never examined, which is a coverage gap. It is never narrowing.
|
|
5
|
+
*/
|
|
6
|
+
export declare enum UnreadFileReason {
|
|
7
|
+
/** Larger than `MAX_SCAN_FILE_BYTES`, so the reader skipped it. */
|
|
8
|
+
OverReadCap = "over-read-cap",
|
|
9
|
+
/**
|
|
10
|
+
* A regenerated file (`generated check`'s temp tree) larger than
|
|
11
|
+
* `MAX_REGEN_FILE_BYTES`, so it was never read or byte-compared.
|
|
12
|
+
*/
|
|
13
|
+
OverRegenCap = "over-regen-cap",
|
|
14
|
+
/** The stat or the read failed (permissions, or a delete racing the walk). */
|
|
15
|
+
Unreadable = "unreadable",
|
|
16
|
+
/**
|
|
17
|
+
* A DIRECTORY the walk could not list (permissions): no file beneath it was
|
|
18
|
+
* ever matched, let alone read. The entry's `path` ends in `/` (the root is
|
|
19
|
+
* `./`), and it is in front of every rule whose globs could match beneath it
|
|
20
|
+
* — `unreadEntryMatches`, the one test every plane uses. It used to be a
|
|
21
|
+
* silent `return`, so a violation under a `chmod 000` directory read as a
|
|
22
|
+
* clean `0` with full coverage.
|
|
23
|
+
*/
|
|
24
|
+
UnreadableDirectory = "unreadable-directory"
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=unread-file-reason.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unread-file-reason.d.ts","sourceRoot":"","sources":["../../src/util/unread-file-reason.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,oBAAY,gBAAgB;IAC1B,mEAAmE;IACnE,WAAW,kBAAkB;IAC7B;;;OAGG;IACH,YAAY,mBAAmB;IAC/B,8EAA8E;IAC9E,UAAU,eAAe;IACzB;;;;;;;OAOG;IACH,mBAAmB,yBAAyB;CAC7C"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why the one reader (`readMatchingFiles`) matched a file against a rule's
|
|
3
|
+
* globs but did not read it. Either way the file is IN the rule's scope and
|
|
4
|
+
* was never examined, which is a coverage gap. It is never narrowing.
|
|
5
|
+
*/
|
|
6
|
+
export var UnreadFileReason;
|
|
7
|
+
(function (UnreadFileReason) {
|
|
8
|
+
/** Larger than `MAX_SCAN_FILE_BYTES`, so the reader skipped it. */
|
|
9
|
+
UnreadFileReason["OverReadCap"] = "over-read-cap";
|
|
10
|
+
/**
|
|
11
|
+
* A regenerated file (`generated check`'s temp tree) larger than
|
|
12
|
+
* `MAX_REGEN_FILE_BYTES`, so it was never read or byte-compared.
|
|
13
|
+
*/
|
|
14
|
+
UnreadFileReason["OverRegenCap"] = "over-regen-cap";
|
|
15
|
+
/** The stat or the read failed (permissions, or a delete racing the walk). */
|
|
16
|
+
UnreadFileReason["Unreadable"] = "unreadable";
|
|
17
|
+
/**
|
|
18
|
+
* A DIRECTORY the walk could not list (permissions): no file beneath it was
|
|
19
|
+
* ever matched, let alone read. The entry's `path` ends in `/` (the root is
|
|
20
|
+
* `./`), and it is in front of every rule whose globs could match beneath it
|
|
21
|
+
* — `unreadEntryMatches`, the one test every plane uses. It used to be a
|
|
22
|
+
* silent `return`, so a violation under a `chmod 000` directory read as a
|
|
23
|
+
* clean `0` with full coverage.
|
|
24
|
+
*/
|
|
25
|
+
UnreadFileReason["UnreadableDirectory"] = "unreadable-directory";
|
|
26
|
+
})(UnreadFileReason || (UnreadFileReason = {}));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { UnreadFileReason } from './unread-file-reason.js';
|
|
2
|
+
/** A file a rule's globs matched that the one reader did not read. */
|
|
3
|
+
export interface IUnreadFile {
|
|
4
|
+
/** Project-relative POSIX path. */
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly reason: UnreadFileReason;
|
|
7
|
+
/** Size on disk, when the stat succeeded. */
|
|
8
|
+
readonly bytes?: number;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=unread-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unread-file.d.ts","sourceRoot":"","sources":["../../src/util/unread-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,mCAAmC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,19 +1,65 @@
|
|
|
1
|
+
import type { IMatchedFiles } from './matched-files.js';
|
|
1
2
|
/** Vendor / build / VCS dirs never scanned. */
|
|
2
3
|
export declare const SKIP_DIRS: ReadonlySet<string>;
|
|
3
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Files larger than this are not READ (regex token extraction over multi-MB
|
|
6
|
+
* blobs is pointless). They are still MATCHED: `readMatchingFiles` reports
|
|
7
|
+
* each one as unread, and every coverage record counts it as unexamined.
|
|
8
|
+
*/
|
|
4
9
|
export declare const MAX_SCAN_FILE_BYTES = 1000000;
|
|
5
10
|
/**
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
11
|
+
* The cap on one file of a REGENERATED temp tree (`generated check` /
|
|
12
|
+
* `generated update`): a runaway regen must not be read into memory whole. A
|
|
13
|
+
* file over it is reported unread (`UnreadFileReason.OverRegenCap`) by
|
|
14
|
+
* `readRegenTree`, never dropped.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_REGEN_FILE_BYTES = 2000000;
|
|
17
|
+
/**
|
|
18
|
+
* THE rule for which directories the walk never enters: a vendor/build/VCS
|
|
19
|
+
* dir (`SKIP_DIRS`, absolute), an `excludeDirs` entry (project-relative), or a
|
|
20
|
+
* dot-directory the caller's globs did not name (`allowDotDirs`).
|
|
21
|
+
*
|
|
22
|
+
* A file under a skipped directory is outside every plane's scope by design,
|
|
23
|
+
* and is neither read nor reported unread. Anything that asks "would the walk
|
|
24
|
+
* reach this path?" must ask this predicate; it must not keep its own copy of
|
|
25
|
+
* the list. (Round 11: run-policy used to re-derive it to guess which unread
|
|
26
|
+
* changed paths were in scope. It now reads the reader's own unread list.)
|
|
27
|
+
*/
|
|
28
|
+
export declare function walkSkipsDirectory(name: string, relPath: string, excludeDirs: ReadonlySet<string>, allowDotDirs?: ReadonlySet<string>): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Walk `root`, returning project-relative POSIX paths that match any INCLUSION
|
|
31
|
+
* glob. `excludeDirs` is a set of project-relative POSIX directory paths to
|
|
32
|
+
* prune entirely (e.g. the SharkCraft asset/config dir).
|
|
33
|
+
*
|
|
34
|
+
* A walk is a POSITIVE union primitive: a `!` entry is dropped here, never
|
|
35
|
+
* applied. Planes walk the union of many rules' lists once, and a negation
|
|
36
|
+
* applied to that union would delete rule A's `!x` from rule B's scope — a
|
|
37
|
+
* silent under-selection. Each list's negations subtract per list, after the
|
|
38
|
+
* walk, through `globListSelects` (or `readSelectedFiles` for one list).
|
|
39
|
+
*
|
|
40
|
+
* `unlistedDirs`, when given, collects every directory the walk entered but
|
|
41
|
+
* could not LIST (a permission error — not a directory that vanished), as a
|
|
42
|
+
* project-relative path ending in `/` (the root is `./`). Every file beneath
|
|
43
|
+
* one was never matched; `readMatchingFiles` reports it unread rather than
|
|
44
|
+
* letting its files drop out of every rule's scope in silence.
|
|
9
45
|
*/
|
|
10
|
-
export declare function walkMatching(root: string, globs: readonly string[], excludeDirs?: ReadonlySet<string>, allowDotDirs?: ReadonlySet<string
|
|
46
|
+
export declare function walkMatching(root: string, globs: readonly string[], excludeDirs?: ReadonlySet<string>, allowDotDirs?: ReadonlySet<string>, unlistedDirs?: string[]): string[];
|
|
47
|
+
/** How many times `readMatchingFiles` walked + read the tree, and how many calls the memo served. */
|
|
48
|
+
export declare function readMatchingFilesStats(): {
|
|
49
|
+
readonly walks: number;
|
|
50
|
+
readonly memoHits: number;
|
|
51
|
+
};
|
|
52
|
+
/** Reset {@link readMatchingFilesStats}. */
|
|
53
|
+
export declare function resetReadMatchingFilesStats(): void;
|
|
11
54
|
/**
|
|
12
55
|
* Run `fn` with the read memo enabled, then clear it.
|
|
13
56
|
*
|
|
14
|
-
* ONLY wrap a scan that performs no writes
|
|
15
|
-
*
|
|
16
|
-
*
|
|
57
|
+
* ONLY wrap a scan that performs no writes of its own for its duration. A
|
|
58
|
+
* command the scan SPAWNS (a baseline's `compute.run`, a generated artifact's
|
|
59
|
+
* `regen`) may rewrite files, so every spawn site calls
|
|
60
|
+
* {@link clearFileReadCache} right after the child exits: a read after the
|
|
61
|
+
* spawn is then fresh, and reuse inside the window stays exact rather than
|
|
62
|
+
* merely probable.
|
|
17
63
|
*/
|
|
18
64
|
export declare function withFileReadCache<T>(fn: () => T): T;
|
|
19
65
|
/** Drop every memoized read. For tests, and for a caller that has just written. */
|
|
@@ -27,6 +73,24 @@ export declare function clearFileReadCache(): void;
|
|
|
27
73
|
* wander into `.venv` and `.yarn` the moment someone wrote a recursive glob.
|
|
28
74
|
*/
|
|
29
75
|
export declare function dotDirsNamedBy(globs: readonly string[]): Set<string>;
|
|
30
|
-
/**
|
|
31
|
-
|
|
76
|
+
/**
|
|
77
|
+
* THE reader: walk + read every file matching `globs`.
|
|
78
|
+
*
|
|
79
|
+
* Returns the files it read AND the matched files it did not read (over
|
|
80
|
+
* {@link MAX_SCAN_FILE_BYTES}, or unreadable), each with its reason. It used
|
|
81
|
+
* to drop those silently. Every plane then counted its expected scope from
|
|
82
|
+
* what was read, so an over-cap file holding a forbidden token read
|
|
83
|
+
* "examined 1 of 1 ✓". Now the gap is part of the return value, and every
|
|
84
|
+
* engine folds it into its rule coverage through `readScopeCoverage`.
|
|
85
|
+
*
|
|
86
|
+
* A path deleted between the walk and the stat is no longer in scope and is
|
|
87
|
+
* not reported.
|
|
88
|
+
*
|
|
89
|
+
* POSITIVE-ONLY, like {@link walkMatching}: a `!` entry in `globs` never
|
|
90
|
+
* subtracts here, because callers hand this the union of many lists. The
|
|
91
|
+
* result is every file an inclusion glob matched; a caller reading ONE list
|
|
92
|
+
* selects through `readSelectedFiles`, a caller reading many filters each
|
|
93
|
+
* list with `globListSelects` / `unreadMatching`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function readMatchingFiles(root: string, globs: readonly string[], excludeDirs?: ReadonlySet<string>, allowDotDirs?: ReadonlySet<string>): IMatchedFiles;
|
|
32
96
|
//# sourceMappingURL=walk-files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"walk-files.d.ts","sourceRoot":"","sources":["../../src/util/walk-files.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"walk-files.d.ts","sourceRoot":"","sources":["../../src/util/walk-files.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,+CAA+C;AAC/C,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,MAAM,CAUxC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,UAAY,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,UAAY,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,GAC5C,OAAO,CAGT;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,WAAW,GAAE,WAAW,CAAC,MAAM,CAAa,EAC5C,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,EAC7C,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,MAAM,EAAE,CAqCV;AA0BD,qGAAqG;AACrG,wBAAgB,sBAAsB,IAAI;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAE9F;AAED,4CAA4C;AAC5C,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CASnD;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAGD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAWpE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,WAAW,GAAE,WAAW,CAAC,MAAM,CAAa,EAC5C,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,GAC5C,aAAa,CA8Df"}
|
package/dist/util/walk-files.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import * as nodePath from 'node:path';
|
|
3
|
-
import { matchesAny } from "../scan/glob.js";
|
|
3
|
+
import { globListWalkGlobs, globMayMatchUnder, matchesAny } from "../scan/glob.js";
|
|
4
|
+
import { UnreadFileReason } from "./unread-file-reason.js";
|
|
4
5
|
/** Vendor / build / VCS dirs never scanned. */
|
|
5
6
|
export const SKIP_DIRS = new Set([
|
|
6
7
|
'node_modules',
|
|
@@ -13,21 +14,66 @@ export const SKIP_DIRS = new Set([
|
|
|
13
14
|
'.turbo',
|
|
14
15
|
'.cache',
|
|
15
16
|
]);
|
|
16
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Files larger than this are not READ (regex token extraction over multi-MB
|
|
19
|
+
* blobs is pointless). They are still MATCHED: `readMatchingFiles` reports
|
|
20
|
+
* each one as unread, and every coverage record counts it as unexamined.
|
|
21
|
+
*/
|
|
17
22
|
export const MAX_SCAN_FILE_BYTES = 1_000_000;
|
|
18
23
|
/**
|
|
19
|
-
*
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
24
|
+
* The cap on one file of a REGENERATED temp tree (`generated check` /
|
|
25
|
+
* `generated update`): a runaway regen must not be read into memory whole. A
|
|
26
|
+
* file over it is reported unread (`UnreadFileReason.OverRegenCap`) by
|
|
27
|
+
* `readRegenTree`, never dropped.
|
|
28
|
+
*/
|
|
29
|
+
export const MAX_REGEN_FILE_BYTES = 2_000_000;
|
|
30
|
+
/**
|
|
31
|
+
* THE rule for which directories the walk never enters: a vendor/build/VCS
|
|
32
|
+
* dir (`SKIP_DIRS`, absolute), an `excludeDirs` entry (project-relative), or a
|
|
33
|
+
* dot-directory the caller's globs did not name (`allowDotDirs`).
|
|
34
|
+
*
|
|
35
|
+
* A file under a skipped directory is outside every plane's scope by design,
|
|
36
|
+
* and is neither read nor reported unread. Anything that asks "would the walk
|
|
37
|
+
* reach this path?" must ask this predicate; it must not keep its own copy of
|
|
38
|
+
* the list. (Round 11: run-policy used to re-derive it to guess which unread
|
|
39
|
+
* changed paths were in scope. It now reads the reader's own unread list.)
|
|
40
|
+
*/
|
|
41
|
+
export function walkSkipsDirectory(name, relPath, excludeDirs, allowDotDirs = new Set()) {
|
|
42
|
+
if (SKIP_DIRS.has(name) || excludeDirs.has(relPath))
|
|
43
|
+
return true;
|
|
44
|
+
return name.startsWith('.') && !allowDotDirs.has(name);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Walk `root`, returning project-relative POSIX paths that match any INCLUSION
|
|
48
|
+
* glob. `excludeDirs` is a set of project-relative POSIX directory paths to
|
|
49
|
+
* prune entirely (e.g. the SharkCraft asset/config dir).
|
|
50
|
+
*
|
|
51
|
+
* A walk is a POSITIVE union primitive: a `!` entry is dropped here, never
|
|
52
|
+
* applied. Planes walk the union of many rules' lists once, and a negation
|
|
53
|
+
* applied to that union would delete rule A's `!x` from rule B's scope — a
|
|
54
|
+
* silent under-selection. Each list's negations subtract per list, after the
|
|
55
|
+
* walk, through `globListSelects` (or `readSelectedFiles` for one list).
|
|
56
|
+
*
|
|
57
|
+
* `unlistedDirs`, when given, collects every directory the walk entered but
|
|
58
|
+
* could not LIST (a permission error — not a directory that vanished), as a
|
|
59
|
+
* project-relative path ending in `/` (the root is `./`). Every file beneath
|
|
60
|
+
* one was never matched; `readMatchingFiles` reports it unread rather than
|
|
61
|
+
* letting its files drop out of every rule's scope in silence.
|
|
22
62
|
*/
|
|
23
|
-
export function walkMatching(root, globs, excludeDirs = new Set(), allowDotDirs = new Set()) {
|
|
63
|
+
export function walkMatching(root, globs, excludeDirs = new Set(), allowDotDirs = new Set(), unlistedDirs) {
|
|
64
|
+
const walkGlobs = globListWalkGlobs(globs);
|
|
24
65
|
const out = [];
|
|
25
66
|
const visit = (abs) => {
|
|
26
67
|
let entries;
|
|
27
68
|
try {
|
|
28
69
|
entries = readdirSync(abs, { withFileTypes: true });
|
|
29
70
|
}
|
|
30
|
-
catch {
|
|
71
|
+
catch (e) {
|
|
72
|
+
const code = e.code;
|
|
73
|
+
if (unlistedDirs && code !== 'ENOENT' && code !== 'ENOTDIR') {
|
|
74
|
+
const rel = nodePath.relative(root, abs).split(nodePath.sep).join('/');
|
|
75
|
+
unlistedDirs.push(rel === '' ? './' : `${rel}/`);
|
|
76
|
+
}
|
|
31
77
|
return;
|
|
32
78
|
}
|
|
33
79
|
for (const e of entries) {
|
|
@@ -43,14 +89,12 @@ export function walkMatching(root, globs, excludeDirs = new Set(), allowDotDirs
|
|
|
43
89
|
// `.gradle`). But a rule may legitimately target one — an agent skill
|
|
44
90
|
// file lives in `.claude/skills` — so a caller can name the dot-dirs
|
|
45
91
|
// its OWN globs ask for. `SKIP_DIRS` stays absolute either way.
|
|
46
|
-
if (
|
|
47
|
-
continue;
|
|
48
|
-
if (e.name.startsWith('.') && !allowDotDirs.has(e.name))
|
|
92
|
+
if (walkSkipsDirectory(e.name, rel, excludeDirs, allowDotDirs))
|
|
49
93
|
continue;
|
|
50
94
|
visit(childAbs);
|
|
51
95
|
}
|
|
52
96
|
else if (e.isFile()) {
|
|
53
|
-
if (matchesAny(rel,
|
|
97
|
+
if (matchesAny(rel, walkGlobs))
|
|
54
98
|
out.push(rel);
|
|
55
99
|
}
|
|
56
100
|
}
|
|
@@ -77,12 +121,27 @@ export function walkMatching(root, globs, excludeDirs = new Set(), allowDotDirs
|
|
|
77
121
|
const READ_MEMO = new Map();
|
|
78
122
|
/** True only inside {@link withFileReadCache}. */
|
|
79
123
|
let memoEnabled = false;
|
|
124
|
+
/** Tree walks `readMatchingFiles` performed, and walks the memo served (tests / perf locks). */
|
|
125
|
+
let walkCount = 0;
|
|
126
|
+
let readMemoHits = 0;
|
|
127
|
+
/** How many times `readMatchingFiles` walked + read the tree, and how many calls the memo served. */
|
|
128
|
+
export function readMatchingFilesStats() {
|
|
129
|
+
return { walks: walkCount, memoHits: readMemoHits };
|
|
130
|
+
}
|
|
131
|
+
/** Reset {@link readMatchingFilesStats}. */
|
|
132
|
+
export function resetReadMatchingFilesStats() {
|
|
133
|
+
walkCount = 0;
|
|
134
|
+
readMemoHits = 0;
|
|
135
|
+
}
|
|
80
136
|
/**
|
|
81
137
|
* Run `fn` with the read memo enabled, then clear it.
|
|
82
138
|
*
|
|
83
|
-
* ONLY wrap a scan that performs no writes
|
|
84
|
-
*
|
|
85
|
-
*
|
|
139
|
+
* ONLY wrap a scan that performs no writes of its own for its duration. A
|
|
140
|
+
* command the scan SPAWNS (a baseline's `compute.run`, a generated artifact's
|
|
141
|
+
* `regen`) may rewrite files, so every spawn site calls
|
|
142
|
+
* {@link clearFileReadCache} right after the child exits: a read after the
|
|
143
|
+
* spawn is then fresh, and reuse inside the window stays exact rather than
|
|
144
|
+
* merely probable.
|
|
86
145
|
*/
|
|
87
146
|
export function withFileReadCache(fn) {
|
|
88
147
|
const previous = memoEnabled;
|
|
@@ -110,7 +169,8 @@ export function clearFileReadCache() {
|
|
|
110
169
|
*/
|
|
111
170
|
export function dotDirsNamedBy(globs) {
|
|
112
171
|
const out = new Set();
|
|
113
|
-
|
|
172
|
+
// Only an inclusion glob can put a file in scope, so only it opens a dot-dir.
|
|
173
|
+
for (const glob of globListWalkGlobs(globs)) {
|
|
114
174
|
for (const segment of glob.split('/')) {
|
|
115
175
|
if (segment.startsWith('.') && segment.length > 1 && !segment.includes('*') && !segment.includes('?')) {
|
|
116
176
|
out.add(segment);
|
|
@@ -119,43 +179,89 @@ export function dotDirsNamedBy(globs) {
|
|
|
119
179
|
}
|
|
120
180
|
return out;
|
|
121
181
|
}
|
|
122
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* THE reader: walk + read every file matching `globs`.
|
|
184
|
+
*
|
|
185
|
+
* Returns the files it read AND the matched files it did not read (over
|
|
186
|
+
* {@link MAX_SCAN_FILE_BYTES}, or unreadable), each with its reason. It used
|
|
187
|
+
* to drop those silently. Every plane then counted its expected scope from
|
|
188
|
+
* what was read, so an over-cap file holding a forbidden token read
|
|
189
|
+
* "examined 1 of 1 ✓". Now the gap is part of the return value, and every
|
|
190
|
+
* engine folds it into its rule coverage through `readScopeCoverage`.
|
|
191
|
+
*
|
|
192
|
+
* A path deleted between the walk and the stat is no longer in scope and is
|
|
193
|
+
* not reported.
|
|
194
|
+
*
|
|
195
|
+
* POSITIVE-ONLY, like {@link walkMatching}: a `!` entry in `globs` never
|
|
196
|
+
* subtracts here, because callers hand this the union of many lists. The
|
|
197
|
+
* result is every file an inclusion glob matched; a caller reading ONE list
|
|
198
|
+
* selects through `readSelectedFiles`, a caller reading many filters each
|
|
199
|
+
* list with `globListSelects` / `unreadMatching`.
|
|
200
|
+
*/
|
|
123
201
|
export function readMatchingFiles(root, globs, excludeDirs = new Set(), allowDotDirs = new Set()) {
|
|
202
|
+
// `['src/**', '!src/x.ts']` and `['src/**']` are one walk, so one memo entry.
|
|
203
|
+
const walkGlobs = globListWalkGlobs(globs);
|
|
124
204
|
const key = memoEnabled
|
|
125
|
-
? `${root}\u0000${[...
|
|
205
|
+
? `${root}\u0000${[...walkGlobs].sort().join('\u0001')}\u0000${[...excludeDirs].sort().join('\u0001')}` +
|
|
126
206
|
`\u0000${[...allowDotDirs].sort().join('\u0001')}`
|
|
127
207
|
: undefined;
|
|
128
208
|
if (key !== undefined) {
|
|
129
209
|
const hit = READ_MEMO.get(key);
|
|
130
|
-
// Hand back a COPY: callers routinely mutate the
|
|
131
|
-
// scan filters it, the generated scan partitions it), and a
|
|
132
|
-
// would let one rule's bookkeeping corrupt the next
|
|
133
|
-
|
|
134
|
-
|
|
210
|
+
// Hand back a COPY of the map: callers routinely mutate the one they get
|
|
211
|
+
// (the wiring scan filters it, the generated scan partitions it), and a
|
|
212
|
+
// shared instance would let one rule's bookkeeping corrupt the next
|
|
213
|
+
// rule's inputs. The unread list is frozen, so it is shared as is.
|
|
214
|
+
if (hit) {
|
|
215
|
+
readMemoHits += 1;
|
|
216
|
+
return { files: new Map(hit.files), unread: hit.unread };
|
|
217
|
+
}
|
|
135
218
|
}
|
|
136
|
-
|
|
137
|
-
|
|
219
|
+
walkCount += 1;
|
|
220
|
+
const files = new Map();
|
|
221
|
+
const unread = [];
|
|
222
|
+
const unlistedDirs = [];
|
|
223
|
+
const matchedPaths = walkMatching(root, walkGlobs, excludeDirs, allowDotDirs, unlistedDirs);
|
|
224
|
+
// A directory the walk could not list hides every file beneath it. It is in
|
|
225
|
+
// front of any glob that could match there, so it is reported unread (the
|
|
226
|
+
// rule over that tree settles PARTIAL), never dropped from scope in silence.
|
|
227
|
+
// Only an inclusion glob can be in front of it: `!**/x` has a `**` segment,
|
|
228
|
+
// but a negation selects nothing on its own.
|
|
229
|
+
for (const dir of unlistedDirs) {
|
|
230
|
+
if (walkGlobs.some((g) => globMayMatchUnder(g, dir))) {
|
|
231
|
+
unread.push({ path: dir, reason: UnreadFileReason.UnreadableDirectory });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
for (const rel of matchedPaths) {
|
|
138
235
|
const abs = nodePath.join(root, rel);
|
|
139
|
-
let size
|
|
236
|
+
let size;
|
|
140
237
|
try {
|
|
141
238
|
const st = statSync(abs);
|
|
142
239
|
if (!st.isFile())
|
|
143
240
|
continue;
|
|
144
241
|
size = st.size;
|
|
145
242
|
}
|
|
146
|
-
catch {
|
|
243
|
+
catch (e) {
|
|
244
|
+
// Deleted since the walk saw it: gone, so out of scope. Anything else
|
|
245
|
+
// (a permission error) leaves a matched file unexamined.
|
|
246
|
+
if (e.code !== 'ENOENT') {
|
|
247
|
+
unread.push({ path: rel, reason: UnreadFileReason.Unreadable });
|
|
248
|
+
}
|
|
147
249
|
continue;
|
|
148
250
|
}
|
|
149
|
-
if (size > MAX_SCAN_FILE_BYTES)
|
|
251
|
+
if (size > MAX_SCAN_FILE_BYTES) {
|
|
252
|
+
unread.push({ path: rel, reason: UnreadFileReason.OverReadCap, bytes: size });
|
|
150
253
|
continue;
|
|
254
|
+
}
|
|
151
255
|
try {
|
|
152
|
-
|
|
256
|
+
files.set(rel, readFileSync(abs, 'utf8'));
|
|
153
257
|
}
|
|
154
258
|
catch {
|
|
155
|
-
|
|
259
|
+
unread.push({ path: rel, reason: UnreadFileReason.Unreadable, bytes: size });
|
|
156
260
|
}
|
|
157
261
|
}
|
|
262
|
+
unread.sort((a, b) => a.path.localeCompare(b.path));
|
|
263
|
+
const result = { files, unread: Object.freeze(unread) };
|
|
158
264
|
if (key !== undefined)
|
|
159
|
-
READ_MEMO.set(key, new Map(
|
|
160
|
-
return
|
|
265
|
+
READ_MEMO.set(key, { files: new Map(files), unread: result.unread });
|
|
266
|
+
return result;
|
|
161
267
|
}
|