@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
|
@@ -1,12 +1,40 @@
|
|
|
1
1
|
import { type IImportContext } from '@shrkcrft/core';
|
|
2
|
-
import { type IBoundaryRule } from '../model/boundary-rule.js';
|
|
2
|
+
import { type IBoundaryRule, type IBoundaryRuleValidationIssue } from '../model/boundary-rule.js';
|
|
3
|
+
/** One rule a file declared that failed validation — dropped from the registry, never silently. */
|
|
4
|
+
export interface IBoundaryRuleInvalid {
|
|
5
|
+
/** Position in the file's rule array. */
|
|
6
|
+
readonly index: number;
|
|
7
|
+
/** The rule's id, when it had a string one. */
|
|
8
|
+
readonly ruleId?: string;
|
|
9
|
+
/** The export the rule array came from (`default`, `rules` or `boundaries`). */
|
|
10
|
+
readonly exportName?: string;
|
|
11
|
+
readonly issues: readonly IBoundaryRuleValidationIssue[];
|
|
12
|
+
}
|
|
3
13
|
export interface ILoadedBoundaryRulesFile {
|
|
4
14
|
source: string;
|
|
5
15
|
rules: IBoundaryRule[];
|
|
6
16
|
warnings: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Rules that failed validation (round 11, 1.3#boundary-invalid-rule). They
|
|
19
|
+
* used to be a warning string nobody rendered, and `check boundaries`
|
|
20
|
+
* reported green over the fence that had just vanished. The structured list
|
|
21
|
+
* lets every surface report them as ERRORED rules — never evaluated.
|
|
22
|
+
*/
|
|
23
|
+
invalid: IBoundaryRuleInvalid[];
|
|
24
|
+
/** The file could not be imported, or exported no rule array. Nothing in it was evaluated. */
|
|
25
|
+
loadError?: string;
|
|
26
|
+
/** The file does not exist. */
|
|
27
|
+
missing?: boolean;
|
|
7
28
|
}
|
|
8
29
|
export interface ILoadBoundaryRulesOptions {
|
|
9
30
|
importContext?: IImportContext;
|
|
31
|
+
/**
|
|
32
|
+
* The pack that contributed the file (round 13): stamped onto every
|
|
33
|
+
* `expectEmpty` marker of its rules (`stampUnitMarks`), so a pack marker that
|
|
34
|
+
* went live is reported as INFO and never fails the consumer, who cannot edit
|
|
35
|
+
* it. Unset for local rules and `--rule-file` / `--diff-against` candidates.
|
|
36
|
+
*/
|
|
37
|
+
packageName?: string;
|
|
10
38
|
}
|
|
11
39
|
export declare function loadBoundaryRulesFromFile(absPath: string, options?: ILoadBoundaryRulesOptions): Promise<ILoadedBoundaryRulesFile>;
|
|
12
40
|
//# sourceMappingURL=load-boundary-rules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-boundary-rules.d.ts","sourceRoot":"","sources":["../../src/registry/load-boundary-rules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"load-boundary-rules.d.ts","sourceRoot":"","sources":["../../src/registry/load-boundary-rules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAA8B,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,4BAA4B,EAClC,MAAM,2BAA2B,CAAC;AAInC,mGAAmG;AACnG,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,4BAA4B,EAAE,CAAC;CAC1D;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;OAKG;IACH,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAwEnC"}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { safeImport } from '@shrkcrft/core';
|
|
3
|
-
import { validateBoundaryRule } from "../model/boundary-rule.js";
|
|
2
|
+
import { safeImport, unitProblemsOf } from '@shrkcrft/core';
|
|
3
|
+
import { validateBoundaryRule, } from "../model/boundary-rule.js";
|
|
4
|
+
import { normalizeBoundaryRule } from "../model/normalize-boundary-rule.js";
|
|
4
5
|
export async function loadBoundaryRulesFromFile(absPath, options = {}) {
|
|
5
6
|
const out = {
|
|
6
7
|
source: absPath,
|
|
7
8
|
rules: [],
|
|
8
9
|
warnings: [],
|
|
10
|
+
invalid: [],
|
|
9
11
|
};
|
|
10
12
|
if (!existsSync(absPath)) {
|
|
11
13
|
out.warnings.push(`boundary rules file not found: ${absPath}`);
|
|
14
|
+
out.missing = true;
|
|
12
15
|
return out;
|
|
13
16
|
}
|
|
14
17
|
const result = options.importContext
|
|
@@ -21,20 +24,47 @@ export async function loadBoundaryRulesFromFile(absPath, options = {}) {
|
|
|
21
24
|
? 'timed out loading boundary rules from'
|
|
22
25
|
: 'failed to load boundary rules from';
|
|
23
26
|
out.warnings.push(`${label} ${absPath}: ${result.error.message}`);
|
|
27
|
+
out.loadError = result.error.message;
|
|
24
28
|
return out;
|
|
25
29
|
}
|
|
26
30
|
const candidates = pickArray(result.module.default) ??
|
|
27
31
|
pickArray(result.module.rules) ??
|
|
28
|
-
pickArray(result.module.boundaries)
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
pickArray(result.module.boundaries);
|
|
33
|
+
if (candidates === null) {
|
|
34
|
+
// A listed rule file that exports no array loads NOTHING — the same silent
|
|
35
|
+
// vanishing act as an invalid rule, one level up.
|
|
36
|
+
const message = 'exports no rule array (expected a default export, `rules` or `boundaries` array)';
|
|
37
|
+
out.warnings.push(`${absPath}: ${message}`);
|
|
38
|
+
out.loadError = message;
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
// The export the rule array came from, so every rejection surface prints
|
|
42
|
+
// `(default[1])` — one wording with every other contribution kind.
|
|
43
|
+
const exportName = pickArray(result.module.default) !== null
|
|
44
|
+
? 'default'
|
|
45
|
+
: pickArray(result.module.rules) !== null
|
|
46
|
+
? 'rules'
|
|
47
|
+
: 'boundaries';
|
|
48
|
+
candidates.forEach((c, index) => {
|
|
31
49
|
const v = validateBoundaryRule(c);
|
|
50
|
+
const id = c?.id;
|
|
32
51
|
if (!v.valid) {
|
|
33
52
|
out.warnings.push(`${absPath}: skipping invalid boundary rule (${v.issues.map((i) => i.field).join(', ')})`);
|
|
34
|
-
|
|
53
|
+
out.invalid.push({ index, exportName, ...(typeof id === 'string' ? { ruleId: id } : {}), issues: v.issues });
|
|
54
|
+
return;
|
|
35
55
|
}
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
// Round 13: the LOADED rule — plain string lists plus the expectEmptyUnits
|
|
57
|
+
// ledger, each marker stamped with the contributing pack. Validation above
|
|
58
|
+
// ran the same parser, so this cannot fail for a valid rule; if it ever
|
|
59
|
+
// did, the rule is an errored rule, never a crash.
|
|
60
|
+
const n = normalizeBoundaryRule(c, options.packageName);
|
|
61
|
+
if (!n.ok) {
|
|
62
|
+
const issues = unitProblemsOf(n.error).map((message) => ({ field: '<markers>', message }));
|
|
63
|
+
out.invalid.push({ index, exportName, ...(typeof id === 'string' ? { ruleId: id } : {}), issues });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
out.rules.push(n.value);
|
|
67
|
+
});
|
|
38
68
|
return out;
|
|
39
69
|
}
|
|
40
70
|
function pickArray(v) {
|
package/dist/scan/glob.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type IGlobList } from '@shrkcrft/core';
|
|
2
|
+
import type { IGlobUnitMeasure } from './i-glob-unit-measure.js';
|
|
1
3
|
/**
|
|
2
4
|
* Minimal glob matcher tuned for the patterns boundary rules use:
|
|
3
5
|
* - `**` matches any number of path segments (including zero)
|
|
@@ -7,7 +9,95 @@
|
|
|
7
9
|
*
|
|
8
10
|
* Patterns are matched against the literal string (file path or import
|
|
9
11
|
* specifier) — no I/O, no resolution. The function is pure and deterministic.
|
|
12
|
+
*
|
|
13
|
+
* Compiled patterns are MEMOISED (round 11, 6.3): `matchesAny` used to
|
|
14
|
+
* recompile a pattern on every test, once per edge × pattern, and that was the
|
|
15
|
+
* dominant cost of a boundary run at scale (1.5 s → 0.09 s at 300 rules). The
|
|
16
|
+
* returned RegExp has no `g`/`y` flag, so sharing one instance is stateless.
|
|
17
|
+
* The cache is bounded ({@link GLOB_REGEX_CACHE_LIMIT}) and simply cleared when
|
|
18
|
+
* full — a pattern set larger than that is recompiled, never wrong.
|
|
10
19
|
*/
|
|
11
20
|
export declare function globToRegex(pattern: string): RegExp;
|
|
21
|
+
/** The most compiled globs {@link globToRegex} keeps before it starts over. */
|
|
22
|
+
export declare const GLOB_REGEX_CACHE_LIMIT = 10000;
|
|
23
|
+
/** How many compiled globs are memoised right now (bounded by {@link GLOB_REGEX_CACHE_LIMIT}). */
|
|
24
|
+
export declare function globRegexCacheSize(): number;
|
|
25
|
+
/**
|
|
26
|
+
* The raw OR primitive: does ANY pattern match `value`? A `!` is a literal
|
|
27
|
+
* character here, so it is right for import specifiers and plain pattern sets,
|
|
28
|
+
* and WRONG for a user-written glob list — use {@link globListSelects} there.
|
|
29
|
+
*/
|
|
12
30
|
export declare function matchesAny(value: string, patterns: readonly string[]): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* A list's inclusion globs and negations (`!` stripped), through core's one
|
|
33
|
+
* parser. Memoised by the list's TEXT (its JSON, so no two lists share a key),
|
|
34
|
+
* never by array identity — a caller that builds a list and then pushes to it
|
|
35
|
+
* must not read a stale parse.
|
|
36
|
+
*/
|
|
37
|
+
export declare function globListParts(globs: readonly string[]): IGlobList;
|
|
38
|
+
/**
|
|
39
|
+
* THE scope test for a user-written glob list, on every gate plane: `path` is
|
|
40
|
+
* selected iff one of the list's inclusion globs matches it and none of its
|
|
41
|
+
* negations does.
|
|
42
|
+
*
|
|
43
|
+
* Order-independent (a later glob never re-includes) and LIST-LOCAL: a
|
|
44
|
+
* negation subtracts from its own list's positive set only. That is why a walk
|
|
45
|
+
* is never negation-aware ({@link globListWalkGlobs}): planes walk the UNION of
|
|
46
|
+
* many lists once, and a negation applied there would delete rule A's `!x`
|
|
47
|
+
* from rule B's scope. Selection happens per list, after the walk.
|
|
48
|
+
*/
|
|
49
|
+
export declare function globListSelects(path: string, globs: readonly string[]): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The globs a WALK over `globs` must match: the inclusion globs only. A walk
|
|
52
|
+
* is a positive union primitive (see {@link globListSelects}); what a list's
|
|
53
|
+
* negations remove is decided per list, over what the walk returned.
|
|
54
|
+
*/
|
|
55
|
+
export declare function globListWalkGlobs(globs: readonly string[]): readonly string[];
|
|
56
|
+
/**
|
|
57
|
+
* What each glob of ONE list did to `paths`, in first-seen order (a glob listed
|
|
58
|
+
* twice is measured once). See {@link IGlobUnitMeasure}: an inclusion glob's
|
|
59
|
+
* `effective` count is what it selected that survives the list's negations; a
|
|
60
|
+
* negation's is what it removed from the list's positive set.
|
|
61
|
+
*
|
|
62
|
+
* `paths` may be a wider walk than this list's own (a union over many rules):
|
|
63
|
+
* only the paths one of THIS list's inclusion globs matches are its positive
|
|
64
|
+
* set, so another list's files never count as excluded here.
|
|
65
|
+
*/
|
|
66
|
+
export declare function measureGlobList(paths: readonly string[], globs: readonly string[]): readonly IGlobUnitMeasure[];
|
|
67
|
+
/**
|
|
68
|
+
* Could `glob` match some path strictly BENEATH directory `dir` (project-
|
|
69
|
+
* relative, `/`-separated; a trailing `/` is optional and `./` is the root)?
|
|
70
|
+
*
|
|
71
|
+
* The question an unlistable directory asks: its files were never enumerated,
|
|
72
|
+
* so a rule is in front of it whenever a path under it COULD be in the rule's
|
|
73
|
+
* scope. Segment-wise and deliberately one-directional: a `**` absorbs the
|
|
74
|
+
* rest, so a false "yes" is possible (a gap reported that a full listing might
|
|
75
|
+
* have ruled out) and a false "no" is not — `src/app/*.ts` cannot reach
|
|
76
|
+
* `src/app/sub/`, `src/app/**` and `**\/*.ts` can.
|
|
77
|
+
*/
|
|
78
|
+
export declare function globMayMatchUnder(glob: string, dir: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Does `glob` match EVERY path beneath directory `dir`? True for `**`, and for
|
|
81
|
+
* a glob ending in `/**` whose base matches the directory or one of its
|
|
82
|
+
* ancestors (`src/generated/**` covers `src/generated/` and
|
|
83
|
+
* `src/generated/deep/`). The test an exemption must pass to take an
|
|
84
|
+
* unlistable directory out of a rule's scope: exempting PART of it leaves the
|
|
85
|
+
* rest a gap. Conservative — any other shape answers `false`.
|
|
86
|
+
*/
|
|
87
|
+
export declare function globCoversAllUnder(glob: string, dir: string): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* How many of `paths` each glob of ONE list effectively counts for, keyed by
|
|
90
|
+
* glob in first-seen order (a glob listed twice is counted once): an inclusion
|
|
91
|
+
* glob's selected files (those surviving the list's negations), a negation's
|
|
92
|
+
* EXCLUDED files. The `glob → effective` projection of {@link measureGlobList}.
|
|
93
|
+
*
|
|
94
|
+
* `globListSelects` collapses a glob LIST into one yes/no, which is right for
|
|
95
|
+
* "is this file in scope" and exactly wrong for "is every glob still live": one
|
|
96
|
+
* glob of a rule's `files[]` can match nothing after a directory rename while
|
|
97
|
+
* its siblings keep the rule connected, and the rule reads green forever. A
|
|
98
|
+
* zero here is that dead unit — and, since round 12, a negation is judged by
|
|
99
|
+
* what it EXCLUDES, so a load-bearing `!**\/*.spec.ts` is never a zero. Pure —
|
|
100
|
+
* callers pass the file list their own walk produced.
|
|
101
|
+
*/
|
|
102
|
+
export declare function countMatchesPerGlob(paths: readonly string[], globs: readonly string[]): ReadonlyMap<string, number>;
|
|
13
103
|
//# sourceMappingURL=glob.d.ts.map
|
package/dist/scan/glob.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../src/scan/glob.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../src/scan/glob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAI7C,kGAAkG;AAClG,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAuCD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAK9E;AAUD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CASjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAI/E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAE7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,gBAAgB,EAAE,CA2B/G;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAapE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAWrE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAE7B"}
|
package/dist/scan/glob.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNegatedGlob, parseGlobList } from '@shrkcrft/core';
|
|
1
2
|
/**
|
|
2
3
|
* Minimal glob matcher tuned for the patterns boundary rules use:
|
|
3
4
|
* - `**` matches any number of path segments (including zero)
|
|
@@ -7,8 +8,32 @@
|
|
|
7
8
|
*
|
|
8
9
|
* Patterns are matched against the literal string (file path or import
|
|
9
10
|
* specifier) — no I/O, no resolution. The function is pure and deterministic.
|
|
11
|
+
*
|
|
12
|
+
* Compiled patterns are MEMOISED (round 11, 6.3): `matchesAny` used to
|
|
13
|
+
* recompile a pattern on every test, once per edge × pattern, and that was the
|
|
14
|
+
* dominant cost of a boundary run at scale (1.5 s → 0.09 s at 300 rules). The
|
|
15
|
+
* returned RegExp has no `g`/`y` flag, so sharing one instance is stateless.
|
|
16
|
+
* The cache is bounded ({@link GLOB_REGEX_CACHE_LIMIT}) and simply cleared when
|
|
17
|
+
* full — a pattern set larger than that is recompiled, never wrong.
|
|
10
18
|
*/
|
|
11
19
|
export function globToRegex(pattern) {
|
|
20
|
+
const cached = GLOB_REGEX_CACHE.get(pattern);
|
|
21
|
+
if (cached)
|
|
22
|
+
return cached;
|
|
23
|
+
if (GLOB_REGEX_CACHE.size >= GLOB_REGEX_CACHE_LIMIT)
|
|
24
|
+
GLOB_REGEX_CACHE.clear();
|
|
25
|
+
const compiled = compileGlob(pattern);
|
|
26
|
+
GLOB_REGEX_CACHE.set(pattern, compiled);
|
|
27
|
+
return compiled;
|
|
28
|
+
}
|
|
29
|
+
/** The most compiled globs {@link globToRegex} keeps before it starts over. */
|
|
30
|
+
export const GLOB_REGEX_CACHE_LIMIT = 10_000;
|
|
31
|
+
const GLOB_REGEX_CACHE = new Map();
|
|
32
|
+
/** How many compiled globs are memoised right now (bounded by {@link GLOB_REGEX_CACHE_LIMIT}). */
|
|
33
|
+
export function globRegexCacheSize() {
|
|
34
|
+
return GLOB_REGEX_CACHE.size;
|
|
35
|
+
}
|
|
36
|
+
function compileGlob(pattern) {
|
|
12
37
|
let r = '';
|
|
13
38
|
for (let i = 0; i < pattern.length; i += 1) {
|
|
14
39
|
const ch = pattern[i];
|
|
@@ -46,6 +71,11 @@ export function globToRegex(pattern) {
|
|
|
46
71
|
}
|
|
47
72
|
return new RegExp('^' + r + '$');
|
|
48
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* The raw OR primitive: does ANY pattern match `value`? A `!` is a literal
|
|
76
|
+
* character here, so it is right for import specifiers and plain pattern sets,
|
|
77
|
+
* and WRONG for a user-written glob list — use {@link globListSelects} there.
|
|
78
|
+
*/
|
|
49
79
|
export function matchesAny(value, patterns) {
|
|
50
80
|
for (const p of patterns) {
|
|
51
81
|
if (globToRegex(p).test(value))
|
|
@@ -53,3 +83,178 @@ export function matchesAny(value, patterns) {
|
|
|
53
83
|
}
|
|
54
84
|
return false;
|
|
55
85
|
}
|
|
86
|
+
/** Parsed lists that carry a negation, keyed by the list's text (bounded like the regex cache). */
|
|
87
|
+
const GLOB_LIST_CACHE = new Map();
|
|
88
|
+
function hasNegation(globs) {
|
|
89
|
+
for (const g of globs)
|
|
90
|
+
if (isNegatedGlob(g))
|
|
91
|
+
return true;
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A list's inclusion globs and negations (`!` stripped), through core's one
|
|
96
|
+
* parser. Memoised by the list's TEXT (its JSON, so no two lists share a key),
|
|
97
|
+
* never by array identity — a caller that builds a list and then pushes to it
|
|
98
|
+
* must not read a stale parse.
|
|
99
|
+
*/
|
|
100
|
+
export function globListParts(globs) {
|
|
101
|
+
if (!hasNegation(globs))
|
|
102
|
+
return { include: globs, exclude: [] };
|
|
103
|
+
const key = JSON.stringify(globs);
|
|
104
|
+
const cached = GLOB_LIST_CACHE.get(key);
|
|
105
|
+
if (cached)
|
|
106
|
+
return cached;
|
|
107
|
+
if (GLOB_LIST_CACHE.size >= GLOB_REGEX_CACHE_LIMIT)
|
|
108
|
+
GLOB_LIST_CACHE.clear();
|
|
109
|
+
const parsed = parseGlobList(globs);
|
|
110
|
+
GLOB_LIST_CACHE.set(key, parsed);
|
|
111
|
+
return parsed;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* THE scope test for a user-written glob list, on every gate plane: `path` is
|
|
115
|
+
* selected iff one of the list's inclusion globs matches it and none of its
|
|
116
|
+
* negations does.
|
|
117
|
+
*
|
|
118
|
+
* Order-independent (a later glob never re-includes) and LIST-LOCAL: a
|
|
119
|
+
* negation subtracts from its own list's positive set only. That is why a walk
|
|
120
|
+
* is never negation-aware ({@link globListWalkGlobs}): planes walk the UNION of
|
|
121
|
+
* many lists once, and a negation applied there would delete rule A's `!x`
|
|
122
|
+
* from rule B's scope. Selection happens per list, after the walk.
|
|
123
|
+
*/
|
|
124
|
+
export function globListSelects(path, globs) {
|
|
125
|
+
if (!hasNegation(globs))
|
|
126
|
+
return matchesAny(path, globs);
|
|
127
|
+
const list = globListParts(globs);
|
|
128
|
+
return matchesAny(path, list.include) && !matchesAny(path, list.exclude);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The globs a WALK over `globs` must match: the inclusion globs only. A walk
|
|
132
|
+
* is a positive union primitive (see {@link globListSelects}); what a list's
|
|
133
|
+
* negations remove is decided per list, over what the walk returned.
|
|
134
|
+
*/
|
|
135
|
+
export function globListWalkGlobs(globs) {
|
|
136
|
+
return globListParts(globs).include;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* What each glob of ONE list did to `paths`, in first-seen order (a glob listed
|
|
140
|
+
* twice is measured once). See {@link IGlobUnitMeasure}: an inclusion glob's
|
|
141
|
+
* `effective` count is what it selected that survives the list's negations; a
|
|
142
|
+
* negation's is what it removed from the list's positive set.
|
|
143
|
+
*
|
|
144
|
+
* `paths` may be a wider walk than this list's own (a union over many rules):
|
|
145
|
+
* only the paths one of THIS list's inclusion globs matches are its positive
|
|
146
|
+
* set, so another list's files never count as excluded here.
|
|
147
|
+
*/
|
|
148
|
+
export function measureGlobList(paths, globs) {
|
|
149
|
+
const list = globListParts(globs);
|
|
150
|
+
const positive = list.exclude.length === 0 ? undefined : paths.filter((p) => matchesAny(p, list.include));
|
|
151
|
+
const selected = positive?.filter((p) => !matchesAny(p, list.exclude));
|
|
152
|
+
const out = [];
|
|
153
|
+
const seen = new Set();
|
|
154
|
+
for (const glob of globs) {
|
|
155
|
+
if (seen.has(glob))
|
|
156
|
+
continue;
|
|
157
|
+
seen.add(glob);
|
|
158
|
+
if (isNegatedGlob(glob)) {
|
|
159
|
+
const re = globToRegex(glob.slice(1));
|
|
160
|
+
let excluded = 0;
|
|
161
|
+
for (const p of positive ?? [])
|
|
162
|
+
if (re.test(p))
|
|
163
|
+
excluded += 1;
|
|
164
|
+
out.push({ glob, negation: true, matched: excluded, effective: excluded });
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const re = globToRegex(glob);
|
|
168
|
+
let matched = 0;
|
|
169
|
+
for (const p of paths)
|
|
170
|
+
if (re.test(p))
|
|
171
|
+
matched += 1;
|
|
172
|
+
let effective = matched;
|
|
173
|
+
if (selected !== undefined) {
|
|
174
|
+
effective = 0;
|
|
175
|
+
for (const p of selected)
|
|
176
|
+
if (re.test(p))
|
|
177
|
+
effective += 1;
|
|
178
|
+
}
|
|
179
|
+
out.push({ glob, negation: false, matched, effective });
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
/** A project-relative directory path without `./` or trailing slashes (`''` is the root). */
|
|
184
|
+
function normaliseDirPath(dir) {
|
|
185
|
+
let d = dir;
|
|
186
|
+
if (d === '.' || d === './')
|
|
187
|
+
return '';
|
|
188
|
+
if (d.startsWith('./'))
|
|
189
|
+
d = d.slice(2);
|
|
190
|
+
return d.replace(/\/+$/, '');
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Could `glob` match some path strictly BENEATH directory `dir` (project-
|
|
194
|
+
* relative, `/`-separated; a trailing `/` is optional and `./` is the root)?
|
|
195
|
+
*
|
|
196
|
+
* The question an unlistable directory asks: its files were never enumerated,
|
|
197
|
+
* so a rule is in front of it whenever a path under it COULD be in the rule's
|
|
198
|
+
* scope. Segment-wise and deliberately one-directional: a `**` absorbs the
|
|
199
|
+
* rest, so a false "yes" is possible (a gap reported that a full listing might
|
|
200
|
+
* have ruled out) and a false "no" is not — `src/app/*.ts` cannot reach
|
|
201
|
+
* `src/app/sub/`, `src/app/**` and `**\/*.ts` can.
|
|
202
|
+
*/
|
|
203
|
+
export function globMayMatchUnder(glob, dir) {
|
|
204
|
+
const d = normaliseDirPath(dir);
|
|
205
|
+
const dirSegs = d === '' ? [] : d.split('/');
|
|
206
|
+
const globSegs = glob.split('/');
|
|
207
|
+
for (let i = 0; i < globSegs.length; i += 1) {
|
|
208
|
+
const segment = globSegs[i];
|
|
209
|
+
if (segment.includes('**'))
|
|
210
|
+
return true;
|
|
211
|
+
// The directory is consumed and the glob has segments left for a file beneath it.
|
|
212
|
+
if (i >= dirSegs.length)
|
|
213
|
+
return true;
|
|
214
|
+
if (!globToRegex(segment).test(dirSegs[i]))
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
// The glob ended at (or above) the directory: nothing strictly beneath it matches.
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Does `glob` match EVERY path beneath directory `dir`? True for `**`, and for
|
|
222
|
+
* a glob ending in `/**` whose base matches the directory or one of its
|
|
223
|
+
* ancestors (`src/generated/**` covers `src/generated/` and
|
|
224
|
+
* `src/generated/deep/`). The test an exemption must pass to take an
|
|
225
|
+
* unlistable directory out of a rule's scope: exempting PART of it leaves the
|
|
226
|
+
* rest a gap. Conservative — any other shape answers `false`.
|
|
227
|
+
*/
|
|
228
|
+
export function globCoversAllUnder(glob, dir) {
|
|
229
|
+
if (glob === '**')
|
|
230
|
+
return true;
|
|
231
|
+
if (!glob.endsWith('/**'))
|
|
232
|
+
return false;
|
|
233
|
+
const base = glob.slice(0, -3);
|
|
234
|
+
const d = normaliseDirPath(dir);
|
|
235
|
+
if (d === '')
|
|
236
|
+
return false;
|
|
237
|
+
const segs = d.split('/');
|
|
238
|
+
for (let n = segs.length; n >= 1; n -= 1) {
|
|
239
|
+
if (globToRegex(base).test(segs.slice(0, n).join('/')))
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* How many of `paths` each glob of ONE list effectively counts for, keyed by
|
|
246
|
+
* glob in first-seen order (a glob listed twice is counted once): an inclusion
|
|
247
|
+
* glob's selected files (those surviving the list's negations), a negation's
|
|
248
|
+
* EXCLUDED files. The `glob → effective` projection of {@link measureGlobList}.
|
|
249
|
+
*
|
|
250
|
+
* `globListSelects` collapses a glob LIST into one yes/no, which is right for
|
|
251
|
+
* "is this file in scope" and exactly wrong for "is every glob still live": one
|
|
252
|
+
* glob of a rule's `files[]` can match nothing after a directory rename while
|
|
253
|
+
* its siblings keep the rule connected, and the rule reads green forever. A
|
|
254
|
+
* zero here is that dead unit — and, since round 12, a negation is judged by
|
|
255
|
+
* what it EXCLUDES, so a load-bearing `!**\/*.spec.ts` is never a zero. Pure —
|
|
256
|
+
* callers pass the file list their own walk produced.
|
|
257
|
+
*/
|
|
258
|
+
export function countMatchesPerGlob(paths, globs) {
|
|
259
|
+
return new Map(measureGlobList(paths, globs).map((m) => [m.glob, m.effective]));
|
|
260
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What ONE glob of a list did to a set of paths (`measureGlobList`).
|
|
3
|
+
*
|
|
4
|
+
* An inclusion glob's `effective` count is the paths it matched that SURVIVE
|
|
5
|
+
* the list's negations; a negation's is the paths it REMOVED from what the
|
|
6
|
+
* list's inclusion globs selected. So "is this unit live?" is `effective > 0`
|
|
7
|
+
* for both kinds, and a negation is never "dead" merely for matching no path
|
|
8
|
+
* on its own — a negation matches nothing by itself; it subtracts.
|
|
9
|
+
*/
|
|
10
|
+
export interface IGlobUnitMeasure {
|
|
11
|
+
/** The glob as written (a negation keeps its `!`). */
|
|
12
|
+
readonly glob: string;
|
|
13
|
+
readonly negation: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Inclusion glob: every path it matches. Negation: every path it matches
|
|
16
|
+
* among those the list's inclusion globs select (its raw reach).
|
|
17
|
+
*/
|
|
18
|
+
readonly matched: number;
|
|
19
|
+
/** Inclusion glob: matched paths that are selected. Negation: paths it excludes. */
|
|
20
|
+
readonly effective: number;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=i-glob-unit-measure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i-glob-unit-measure.d.ts","sourceRoot":"","sources":["../../src/scan/i-glob-unit-measure.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { BoundaryMatchKind, ForbiddenMatchMode } from '../model/boundary-rule.js';
|
|
2
|
+
/**
|
|
3
|
+
* How a forbidden-import (or exception-target) pattern matches an import
|
|
4
|
+
* specifier — the pattern language of the boundary plane (round 11, 1.5).
|
|
5
|
+
*
|
|
6
|
+
* The generic glob matcher keeps `*` = "any chars except `/`" (glob.test.ts
|
|
7
|
+
* pins that), so a bare package pattern like `@scope/package-a` or
|
|
8
|
+
* `@scope/package-*` used to match ONLY the package entrypoint: every subpath
|
|
9
|
+
* import of the very same forbidden package (`@scope/package-a/sub`) escaped
|
|
10
|
+
* the fence, and the gate reported green over it. Package semantics live HERE,
|
|
11
|
+
* in the boundary evaluator's matcher, not in glob.ts.
|
|
12
|
+
*
|
|
13
|
+
* The rule, in one sentence: a pattern with no `**` and no trailing `/` is a
|
|
14
|
+
* PACKAGE pattern — it matches the specifier itself and everything under it
|
|
15
|
+
* (`<pattern>/**`), at a segment boundary, so `@scope/pkg` covers
|
|
16
|
+
* `@scope/pkg/deep` but never `@scope/pkg-legacy`. A pattern containing `**`
|
|
17
|
+
* already says how deep it reaches and is matched exactly as written. A rule
|
|
18
|
+
* that means "the entrypoint only" (barrel avoidance: forbid `lodash`, allow
|
|
19
|
+
* `lodash/get`) opts out with `forbiddenMatch: 'exact'`.
|
|
20
|
+
*/
|
|
21
|
+
/** Does `pattern` take package semantics (entrypoint + every subpath)? */
|
|
22
|
+
export declare function isPackagePattern(pattern: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Match `specifier` against `pattern`.
|
|
25
|
+
*
|
|
26
|
+
* Returns `'exact'` when the pattern matches the specifier as written,
|
|
27
|
+
* `'subpath'` when only the package semantics reached it (a deeper import of a
|
|
28
|
+
* package the pattern names), and `null` otherwise. `mode: 'exact'` disables
|
|
29
|
+
* the subpath half. The verdict label travels on the violation (`matchKind`)
|
|
30
|
+
* so a newly-reported subpath edge explains itself.
|
|
31
|
+
*/
|
|
32
|
+
export declare function matchImportPattern(specifier: string, pattern: string, mode?: ForbiddenMatchMode): BoundaryMatchKind | null;
|
|
33
|
+
/**
|
|
34
|
+
* Why `pattern` cannot mean what its author wrote in a SPECIFIER list
|
|
35
|
+
* (`forbiddenImports`, `allowedImports`, `exceptions[].target`) matched under
|
|
36
|
+
* `mode` — or `undefined` for a well-formed pattern (round 12, R12-5.2). The
|
|
37
|
+
* one predicate the rule validator (every local AND pack rule file) and the
|
|
38
|
+
* evaluator's dead-unit reach both read.
|
|
39
|
+
*
|
|
40
|
+
* - `''` names no import.
|
|
41
|
+
* - A leading `!` is the NEGATION syntax of every glob list in this repo, but
|
|
42
|
+
* a specifier list takes none: compiled as written it matches only an
|
|
43
|
+
* import that itself starts with `!` (a webpack inline loader), so the
|
|
44
|
+
* carve-out an author meant silently matched nothing. (A literal
|
|
45
|
+
* inline-loader specifier is still reachable: start the pattern with `?`.)
|
|
46
|
+
* - Under package semantics a trailing `/` is the one spelling left literal
|
|
47
|
+
* ({@link isPackagePattern}): it matches only an import written WITH that
|
|
48
|
+
* slash (`'buffer/'`, the userland-polyfill idiom) — never the package, never
|
|
49
|
+
* its subpaths — while the gate reported ✓ over both. `mode: 'exact'` (and
|
|
50
|
+
* `allowedImports`, always exact) keeps it as the literal it then plainly is.
|
|
51
|
+
*
|
|
52
|
+
* Deliberately NOT "can never match": `'buffer/'` is a real import specifier.
|
|
53
|
+
* The trailing-slash fix it names is lossless — the package spelling covers
|
|
54
|
+
* the slash spelling too (`matchImportPattern('buffer/', 'buffer')` is
|
|
55
|
+
* `subpath`), so following the advice never narrows a fence.
|
|
56
|
+
*/
|
|
57
|
+
export declare function importPatternDefect(pattern: string, mode?: ForbiddenMatchMode): string | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Does every specifier `inner` matches also match `outer`? (round 12, R12-5.3 /
|
|
60
|
+
* R12-5.6). `outerMode` is how `outer` matches (the rule's `forbiddenMatch`);
|
|
61
|
+
* `innerMode` is how `inner` does — the same mode for a sibling forbidden
|
|
62
|
+
* pattern, `exact` for an `allowedImports` entry (never widened).
|
|
63
|
+
*
|
|
64
|
+
* Deliberately CONSERVATIVE — `true` only when the literal shape proves it,
|
|
65
|
+
* because a false "covered" would tell an author to delete a live pattern (or
|
|
66
|
+
* call a working allowance dead). Two proofs:
|
|
67
|
+
*
|
|
68
|
+
* 1. `inner` is a literal (no `*` / `?`) that `outer` matches — and, when
|
|
69
|
+
* `inner` itself widens to its subpaths (a package pattern under package
|
|
70
|
+
* semantics), `outer` widens too.
|
|
71
|
+
* 2. `outer` is a package pattern under package semantics, and it matches
|
|
72
|
+
* `inner`'s literal prefix up to one of its `/`: every specifier `inner`
|
|
73
|
+
* can match starts with that prefix plus `/`, a subpath `outer` covers.
|
|
74
|
+
*
|
|
75
|
+
* A pattern with an {@link importPatternDefect} proves nothing either way.
|
|
76
|
+
*/
|
|
77
|
+
export declare function importPatternSubsumes(outer: string, inner: string, outerMode?: ForbiddenMatchMode, innerMode?: ForbiddenMatchMode): boolean;
|
|
78
|
+
/** Why the dead-unit judge never calls a relative specifier pattern dead — its went-live evidence and the marker refusal both quote it. */
|
|
79
|
+
export declare const RELATIVE_PATTERN_NEVER_DEAD = "a relative pattern is never judged dead (it cannot be judged without an importing file)";
|
|
80
|
+
/** Why the dead-unit judge never calls a leading-`*` specifier pattern dead — its went-live evidence and the marker refusal both quote it. */
|
|
81
|
+
export declare const LEADING_WILDCARD_NEVER_DEAD = "a leading wildcard could match any known package name, so it is never judged dead";
|
|
82
|
+
/**
|
|
83
|
+
* Could `pattern` match an import of package `name` (or one of its subpaths)?
|
|
84
|
+
* Deliberately permissive — a false "resolvable" costs a missed warning, a false
|
|
85
|
+
* "dead" would cry wolf on a legitimate guard. The evaluator's reach
|
|
86
|
+
* (`resolvedBy`) and {@link importPatternNeverJudgedDead} both read it.
|
|
87
|
+
*/
|
|
88
|
+
export declare function couldMatchPackageName(pattern: string, name: string, mode: ForbiddenMatchMode): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Why the boundary dead-unit judge can NEVER call `pattern` dead, matched
|
|
91
|
+
* under `mode` — or `undefined` when it can (round 13, K5). Proved from the
|
|
92
|
+
* pattern alone, through the judge's own reach rules (`resolvedBy` in the
|
|
93
|
+
* evaluator): the judge calls a specifier pattern dead only when it resolves to
|
|
94
|
+
* nothing anywhere, and the orchestrator ALWAYS supplies the runtime builtins
|
|
95
|
+
* as known packages (`nodeBuiltinPackageNames`), so these always resolve:
|
|
96
|
+
*
|
|
97
|
+
* - a relative pattern (`./x`, `../legacy/**`) — never judged without an
|
|
98
|
+
* importing file;
|
|
99
|
+
* - a leading `*` — it could match any known package name, and a builtin is
|
|
100
|
+
* always known;
|
|
101
|
+
* - a pattern a runtime builtin module matches (`fs`, `node:*`,
|
|
102
|
+
* `fs/promises`) — that package always exists.
|
|
103
|
+
*
|
|
104
|
+
* A `{ pattern, expectEmpty: true }` marker on such a pattern could only ever
|
|
105
|
+
* read went-live, so the rule validator refuses it at load. A pattern with an
|
|
106
|
+
* {@link importPatternDefect} is answered by the defect instead (`undefined`).
|
|
107
|
+
*/
|
|
108
|
+
export declare function importPatternNeverJudgedDead(pattern: string, mode?: ForbiddenMatchMode): string | undefined;
|
|
109
|
+
//# sourceMappingURL=import-pattern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-pattern.d.ts","sourceRoot":"","sources":["../../src/scan/import-pattern.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIvF;;;;;;;;;;;;;;;;;;GAkBG;AAEH,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,kBAA8B,GACnC,iBAAiB,GAAG,IAAI,CAM1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,kBAA8B,GAAG,MAAM,GAAG,SAAS,CAoB7G;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,kBAA8B,EACzC,SAAS,GAAE,kBAA8B,GACxC,OAAO,CAgBT;AAED,2IAA2I;AAC3I,eAAO,MAAM,2BAA2B,4FACmD,CAAC;AAE5F,8IAA8I;AAC9I,eAAO,MAAM,2BAA2B,sFAC6C,CAAC;AAEtF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAkBtG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,kBAA8B,GAAG,MAAM,GAAG,SAAS,CAWtH"}
|