@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +181 -341
- package/dist/src/augmentations.js +106 -13
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +6 -82
- package/dist/src/configuration-files.js +28 -176
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +73 -3
- package/dist/src/conventions.js +36 -66
- package/dist/src/dependencies.js +103 -40
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +11 -27
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +139 -72
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +5 -5
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +13 -10
- package/dist/src/findings/non-code-artifacts.js +7 -8
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +41 -32
- package/dist/src/findings/visibility-narrowing.js +19 -16
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +7 -4
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/graph.js +5 -3
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory.js +2 -21
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +76 -4
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +1 -0
- package/dist/src/references.js +48 -30
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -729
- package/dist/src/roots.js +273 -87
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +595 -21
- package/dist/src/session.js +34 -29
- package/dist/src/source-files.js +1 -1
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +33 -51
- package/dist/src/sweep.js +7 -9
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +8 -11
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +166 -126
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +10 -7
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +62 -64
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +245 -502
- package/src/augmentations.ts +135 -17
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +31 -302
- package/src/configuration-files.ts +34 -179
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +85 -3
- package/src/conventions.ts +46 -84
- package/src/dependencies.ts +118 -46
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +12 -38
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +14 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +181 -84
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +7 -7
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +13 -11
- package/src/findings/non-code-artifacts.ts +9 -11
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +45 -32
- package/src/findings/visibility-narrowing.ts +20 -17
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +7 -4
- package/src/git-ignore.ts +50 -26
- package/src/graph.ts +5 -3
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory.ts +2 -20
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +122 -4
- package/src/mdx-entries.ts +3 -26
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +1 -0
- package/src/references.ts +52 -31
- package/src/report-schema.ts +68 -0
- package/src/report.ts +101 -224
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1114
- package/src/roots.ts +339 -104
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +763 -22
- package/src/session.ts +70 -54
- package/src/source-files.ts +1 -1
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +43 -58
- package/src/sweep.ts +23 -9
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +17 -13
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +18 -288
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +224 -162
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +10 -11
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +74 -64
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -468
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
package/src/symbol-keys.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
isIdentifier,
|
|
13
13
|
isNoSubstitutionTemplateLiteral,
|
|
14
14
|
isNumericLiteral,
|
|
15
|
-
isParenthesizedExpression,
|
|
16
15
|
isPropertyAccessExpression,
|
|
17
16
|
isSpreadElement,
|
|
18
17
|
isStringLiteral,
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
type Node,
|
|
22
21
|
type SourceFile,
|
|
23
22
|
} from "@typescript/native/ast";
|
|
23
|
+
import { parenthesesRemoved } from "./unwrap.ts";
|
|
24
24
|
import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
|
|
25
25
|
import type { AliasChains } from "./alias-chain.ts";
|
|
26
26
|
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
@@ -52,10 +52,7 @@ interface KeyedUse {
|
|
|
52
52
|
|
|
53
53
|
/** The identifier an element access's argument names its key by, where it is a name. */
|
|
54
54
|
export function keyNameOf(argument: Node): Node | undefined {
|
|
55
|
-
|
|
56
|
-
while (isParenthesizedExpression(at)) {
|
|
57
|
-
at = at.expression;
|
|
58
|
-
}
|
|
55
|
+
const at = parenthesesRemoved(argument);
|
|
59
56
|
if (isIdentifier(at)) {
|
|
60
57
|
return at;
|
|
61
58
|
}
|
package/src/template-field.ts
CHANGED
|
@@ -13,13 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { readComponent } from "./component-files.ts";
|
|
16
|
-
import {
|
|
16
|
+
import type { ResolvedConfig, TemplateDelimiters } from "./config.ts";
|
|
17
17
|
import type { DetectorInput, Evidence } from "./exempt.ts";
|
|
18
18
|
import type { Host } from "./host.ts";
|
|
19
19
|
import type { SymbolKind } from "./inventory.ts";
|
|
20
20
|
import { memberNames } from "./member-names.ts";
|
|
21
21
|
import { extensionOf, isAbsolutePath, joinPath, normalizePath, relativePath } from "./paths.ts";
|
|
22
22
|
import { isComponentFile, type Position } from "./position.ts";
|
|
23
|
+
import { bytewise } from "./text/compare.ts";
|
|
24
|
+
import { filesBelow } from "./fs/tree.ts";
|
|
25
|
+
import { UsageError } from "./usage.ts";
|
|
23
26
|
|
|
24
27
|
/** One template, by its path below the target root. */
|
|
25
28
|
interface TemplateFile {
|
|
@@ -50,14 +53,6 @@ const READ_BY_MARKUP: ReadonlySet<SymbolKind> = new Set([...READ_BY_A_TEMPLATE,
|
|
|
50
53
|
/** One identifier, as the grammar of the analyzed language spells one. */
|
|
51
54
|
const IDENTIFIER = /[\p{ID_Start}$_][\p{ID_Continue}$\u200C\u200D]*/uy;
|
|
52
55
|
|
|
53
|
-
/** Two strings ordered bytewise. */
|
|
54
|
-
function compare(a: string, b: string): number {
|
|
55
|
-
if (a === b) {
|
|
56
|
-
return 0;
|
|
57
|
-
}
|
|
58
|
-
return a < b ? -1 : 1;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
56
|
/**
|
|
62
57
|
* Every template under the configured directories, read once for the run, in path
|
|
63
58
|
* order and each file once. A directory is named relative to the target root, and
|
|
@@ -66,25 +61,22 @@ function compare(a: string, b: string): number {
|
|
|
66
61
|
* exemption it was configured for off. A symbolic link to a directory is not
|
|
67
62
|
* followed.
|
|
68
63
|
*/
|
|
69
|
-
export function readTemplates(
|
|
64
|
+
export function readTemplates(
|
|
65
|
+
host: Host,
|
|
66
|
+
targetRoot: string,
|
|
67
|
+
analysis: ResolvedConfig["analysis"],
|
|
68
|
+
): Templates {
|
|
70
69
|
const files = new Map<string, string>();
|
|
71
70
|
const read = (dir: string): void => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
);
|
|
75
|
-
for (const entry of entries) {
|
|
76
|
-
const path = joinPath(dir, entry.name);
|
|
77
|
-
if (entry.directory) {
|
|
78
|
-
read(path);
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
71
|
+
for (const below of filesBelow(host, joinPath(targetRoot, dir), () => true)) {
|
|
72
|
+
const path = joinPath(dir, below);
|
|
81
73
|
const absolute = joinPath(targetRoot, path);
|
|
82
74
|
if (!files.has(path) && host.kindOf(absolute) === "file") {
|
|
83
75
|
files.set(path, host.readFile(absolute));
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
78
|
};
|
|
87
|
-
for (const configured of analysis.
|
|
79
|
+
for (const configured of analysis.template_dirs) {
|
|
88
80
|
const dir = normalizePath(configured);
|
|
89
81
|
if (
|
|
90
82
|
isAbsolutePath(dir) ||
|
|
@@ -92,19 +84,17 @@ export function readTemplates(host: Host, targetRoot: string, analysis: Analysis
|
|
|
92
84
|
dir.startsWith("../") ||
|
|
93
85
|
host.kindOf(joinPath(targetRoot, dir)) !== "directory"
|
|
94
86
|
) {
|
|
95
|
-
throw new
|
|
96
|
-
"malformed",
|
|
97
|
-
"analysis.template_dirs",
|
|
87
|
+
throw new UsageError(
|
|
98
88
|
`analysis.template_dirs: ${JSON.stringify(configured)} is not a directory below the target root`,
|
|
99
89
|
);
|
|
100
90
|
}
|
|
101
91
|
read(dir);
|
|
102
92
|
}
|
|
103
93
|
return {
|
|
104
|
-
delimiters: analysis.
|
|
94
|
+
delimiters: analysis.template_delimiters,
|
|
105
95
|
files: [...files]
|
|
106
96
|
.map(([path, text]) => ({ path, text }))
|
|
107
|
-
.sort((a, b) =>
|
|
97
|
+
.sort((a, b) => bytewise(a.path, b.path)),
|
|
108
98
|
};
|
|
109
99
|
}
|
|
110
100
|
|
package/src/test-support.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* test code.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { CONVENTION_ROWS } from "./convention-rows.ts";
|
|
9
|
+
import { CONVENTION_ROWS, TEST_LIBRARIES, TEST_LIBRARY_SCOPES } from "./convention-rows.ts";
|
|
10
10
|
import { packageOfSpecifier } from "./dependencies.ts";
|
|
11
11
|
import type { InventorySymbol } from "./inventory.ts";
|
|
12
12
|
import type { Reference } from "./references.ts";
|
|
@@ -17,17 +17,13 @@ const SKIP_ROOT = "type-error";
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* The test runners and test libraries an import of which is test evidence: each runner a
|
|
20
|
-
* convention row reads test files for, the
|
|
20
|
+
* convention row reads test files for, and the convention table's test libraries.
|
|
21
21
|
*/
|
|
22
22
|
const TEST_PACKAGES: ReadonlySet<string> = new Set([
|
|
23
23
|
...CONVENTION_ROWS.filter((row) => row.testFiles !== undefined).map((row) => row.package),
|
|
24
|
-
|
|
25
|
-
"@playwright/test",
|
|
24
|
+
...TEST_LIBRARIES,
|
|
26
25
|
]);
|
|
27
26
|
|
|
28
|
-
/** The scopes every package of which is a test library. */
|
|
29
|
-
const TEST_SCOPES = ["@testing-library/"] as const;
|
|
30
|
-
|
|
31
27
|
/**
|
|
32
28
|
* The Node.js built-in modules a bare specifier names, which are no package. The set is
|
|
33
29
|
* closed: Node.js adds a new built-in under the `node:` scheme alone
|
|
@@ -94,7 +90,7 @@ function namesTestPackage(specifier: string): boolean {
|
|
|
94
90
|
const name = specifier.startsWith("node:") ? specifier : packageOfSpecifier(specifier);
|
|
95
91
|
return (
|
|
96
92
|
name !== undefined &&
|
|
97
|
-
(TEST_PACKAGES.has(name) ||
|
|
93
|
+
(TEST_PACKAGES.has(name) || TEST_LIBRARY_SCOPES.some((scope) => name.startsWith(scope)))
|
|
98
94
|
);
|
|
99
95
|
}
|
|
100
96
|
|
|
@@ -134,13 +130,21 @@ function evidenceReader(
|
|
|
134
130
|
};
|
|
135
131
|
}
|
|
136
132
|
|
|
133
|
+
/** The files only tests reach, split by whether each carries test evidence of its own. */
|
|
134
|
+
interface TestReached {
|
|
135
|
+
/** The test-support files: test code. */
|
|
136
|
+
readonly support: ReadonlySet<string>;
|
|
137
|
+
/** The files only tests reach that are no test-support file: production code only tests use. */
|
|
138
|
+
readonly testUsed: ReadonlySet<string>;
|
|
139
|
+
}
|
|
140
|
+
|
|
137
141
|
/**
|
|
138
142
|
* The files of one project that are test-support code, below the target root: the
|
|
139
143
|
* largest set of files a test file reaches, none of them a test file or holding a root,
|
|
140
144
|
* each carrying test evidence of its own, whose every user outside the set is a test
|
|
141
|
-
* file
|
|
142
|
-
*
|
|
143
|
-
* the test-file rules classified.
|
|
145
|
+
* file; and the files only tests reach that are not among them. A root has a caller
|
|
146
|
+
* outside the reference graph: a runtime, a tool, a configuration, or a consumer of a
|
|
147
|
+
* library's published API. `testFiles` are the files the test-file rules classified.
|
|
144
148
|
*/
|
|
145
149
|
export function testSupportFiles(
|
|
146
150
|
symbols: readonly InventorySymbol[],
|
|
@@ -148,7 +152,7 @@ export function testSupportFiles(
|
|
|
148
152
|
roots: readonly Root[],
|
|
149
153
|
testFiles: readonly string[],
|
|
150
154
|
evidence: TestEvidence,
|
|
151
|
-
):
|
|
155
|
+
): TestReached {
|
|
152
156
|
const fileOf = new Map(symbols.map((symbol) => [symbol.id, symbol.position.path]));
|
|
153
157
|
const users = new Map<string, Set<string>>();
|
|
154
158
|
const uses = new Map<string, Set<string>>();
|
|
@@ -203,7 +207,7 @@ export function testSupportFiles(
|
|
|
203
207
|
const support = withoutOutsideUsers(
|
|
204
208
|
new Set([...onlyTests].filter((path) => carries(onlyTests, path))),
|
|
205
209
|
);
|
|
206
|
-
return support;
|
|
210
|
+
return { support, testUsed: new Set([...onlyTests].filter((path) => !support.has(path))) };
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
/** The references with every one a test-support file makes counted as a test reference. */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two strings in the order of their UTF-8 bytes, the order every set of a run is read in.
|
|
3
|
+
* That is code point order, which UTF-16 code unit order (`<` on strings) breaks above
|
|
4
|
+
* U+FFFF: a surrogate unit is below U+E000 while the code point it encodes is above U+FFFF.
|
|
5
|
+
*/
|
|
6
|
+
export function bytewise(a: string, b: string): number {
|
|
7
|
+
if (a === b) {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
const length = Math.min(a.length, b.length);
|
|
11
|
+
for (let at = 0; at < length; at += 1) {
|
|
12
|
+
const x = a.charCodeAt(at);
|
|
13
|
+
const y = b.charCodeAt(at);
|
|
14
|
+
if (x !== y) {
|
|
15
|
+
return ranked(x) < ranked(y) ? -1 : 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return a.length < b.length ? -1 : 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** One UTF-16 unit moved so that a surrogate ranks above every unit of U+E000 to U+FFFF. */
|
|
22
|
+
function ranked(unit: number): number {
|
|
23
|
+
if (unit >= 0xe000) {
|
|
24
|
+
return unit - 0x800;
|
|
25
|
+
}
|
|
26
|
+
return unit >= 0xd800 ? unit + 0x2000 : unit;
|
|
27
|
+
}
|
package/src/text/glob.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob patterns over paths in two forms. The configured form (the configuration, convention
|
|
3
|
+
* rows, bundler entries, workspace member patterns) has `*` and `?` within a segment, `**`
|
|
4
|
+
* across segments and brace lists. A `.gitignore` line has git's form: bracket expressions
|
|
5
|
+
* and backslash escapes, no brace lists (https://git-scm.com/docs/gitignore#_pattern_format).
|
|
6
|
+
* A configured root is not a glob: it names a symbol reference, and is read where it is used.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
type GlobForm = "configured" | "gitignore";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One character of a pattern as the expression syntax reads it literally. The set escaped
|
|
13
|
+
* is closed: under the unicode flag an escape of any other character is refused outright.
|
|
14
|
+
*/
|
|
15
|
+
function quoted(character: string): string {
|
|
16
|
+
return /[.*+?^${}()|[\]\\/]/u.test(character) ? `\\${character}` : character;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The ASCII ranges each POSIX class a bracket expression may name holds, as git's own
|
|
21
|
+
* character table reads them, each range written as its first and last character.
|
|
22
|
+
*/
|
|
23
|
+
const CLASSES: ReadonlyMap<string, string> = new Map([
|
|
24
|
+
["alnum", "09AZaz"],
|
|
25
|
+
["alpha", "AZaz"],
|
|
26
|
+
["blank", "\t\t "],
|
|
27
|
+
["cntrl", "\0\x1f\x7f\x7f"],
|
|
28
|
+
["digit", "09"],
|
|
29
|
+
["graph", "!~"],
|
|
30
|
+
["lower", "az"],
|
|
31
|
+
["print", " ~"],
|
|
32
|
+
["punct", "!/:@[`{~"],
|
|
33
|
+
["space", "\t\n\r\r "],
|
|
34
|
+
["upper", "AZ"],
|
|
35
|
+
["xdigit", "09AFaf"],
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
/** What a pattern git cannot read matches: nothing, as git aborts the whole match. */
|
|
39
|
+
const NOTHING = "(?!)";
|
|
40
|
+
|
|
41
|
+
/** One code point as a member of an expression class. */
|
|
42
|
+
function member(point: number): string {
|
|
43
|
+
return `\\u{${point.toString(16)}}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The class a bracket expression opening at `open` denotes, and the index past its `]`,
|
|
48
|
+
* read as git's wildmatch reads it: a leading `!` or `^` negates it, a `]` first is a
|
|
49
|
+
* member, `\` escapes one character, `x-y` is a range (one whose `y` is below `x` holds
|
|
50
|
+
* `x` alone) and `[:name:]` a POSIX class. An unclosed bracket or an unknown class name
|
|
51
|
+
* matches nothing. Neither form matches the solidus.
|
|
52
|
+
*/
|
|
53
|
+
function bracket(pattern: string, open: number): { source: string; end: number } {
|
|
54
|
+
let at = open + 1;
|
|
55
|
+
const negated = pattern[at] === "!" || pattern[at] === "^";
|
|
56
|
+
if (negated) {
|
|
57
|
+
at += 1;
|
|
58
|
+
}
|
|
59
|
+
const next = (): number | undefined => {
|
|
60
|
+
const point = pattern.codePointAt(at);
|
|
61
|
+
at += point !== undefined && point > 0xffff ? 2 : 1;
|
|
62
|
+
return point;
|
|
63
|
+
};
|
|
64
|
+
let members = "";
|
|
65
|
+
let low: number | undefined;
|
|
66
|
+
for (let first = true; ; first = false) {
|
|
67
|
+
let point = next();
|
|
68
|
+
if (point === undefined) {
|
|
69
|
+
return { source: NOTHING, end: pattern.length };
|
|
70
|
+
}
|
|
71
|
+
if (point === 0x5d && !first) {
|
|
72
|
+
return { source: negated ? `[^/${members}]` : `(?!/)[${members}]`, end: at };
|
|
73
|
+
}
|
|
74
|
+
if (point === 0x2d && low !== undefined && at < pattern.length && pattern[at] !== "]") {
|
|
75
|
+
let high = next();
|
|
76
|
+
if (high === 0x5c) {
|
|
77
|
+
high = next();
|
|
78
|
+
}
|
|
79
|
+
if (high === undefined) {
|
|
80
|
+
return { source: NOTHING, end: pattern.length };
|
|
81
|
+
}
|
|
82
|
+
members += high >= low ? `${member(low)}-${member(high)}` : "";
|
|
83
|
+
low = undefined;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (point === 0x5b && pattern[at] === ":") {
|
|
87
|
+
const close = pattern.indexOf("]", at + 1);
|
|
88
|
+
if (close < 0) {
|
|
89
|
+
return { source: NOTHING, end: pattern.length };
|
|
90
|
+
}
|
|
91
|
+
if (close > at + 1 && pattern[close - 1] === ":") {
|
|
92
|
+
const ranges = CLASSES.get(pattern.slice(at + 1, close - 1));
|
|
93
|
+
if (ranges === undefined) {
|
|
94
|
+
return { source: NOTHING, end: pattern.length };
|
|
95
|
+
}
|
|
96
|
+
for (let one = 0; one < ranges.length; one += 2) {
|
|
97
|
+
members += `${member(ranges.charCodeAt(one))}-${member(ranges.charCodeAt(one + 1))}`;
|
|
98
|
+
}
|
|
99
|
+
low = undefined;
|
|
100
|
+
at = close + 1;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (point === 0x5c) {
|
|
105
|
+
point = next();
|
|
106
|
+
if (point === undefined) {
|
|
107
|
+
return { source: NOTHING, end: pattern.length };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
members += member(point);
|
|
111
|
+
low = point;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** The expression source of one pattern with no brace list. */
|
|
116
|
+
function source(pattern: string, form: GlobForm): string {
|
|
117
|
+
let text = "";
|
|
118
|
+
let at = 0;
|
|
119
|
+
while (at < pattern.length) {
|
|
120
|
+
if (pattern.startsWith("**/", at)) {
|
|
121
|
+
text += "(?:[^/]*/)*";
|
|
122
|
+
at += 3;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (pattern.startsWith("**", at)) {
|
|
126
|
+
text += ".*";
|
|
127
|
+
at += 2;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const character = pattern[at] ?? "";
|
|
131
|
+
if (form === "gitignore" && character === "[") {
|
|
132
|
+
const read = bracket(pattern, at);
|
|
133
|
+
text += read.source;
|
|
134
|
+
at = read.end;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (form === "gitignore" && character === "\\" && at + 1 < pattern.length) {
|
|
138
|
+
text += quoted(pattern[at + 1] ?? "");
|
|
139
|
+
at += 2;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
at += 1;
|
|
143
|
+
text += character === "*" ? "[^/]*" : character === "?" ? "[^/]" : quoted(character);
|
|
144
|
+
}
|
|
145
|
+
return text;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** A pattern with every brace list expanded into its alternatives; a lone `{` stands for itself. */
|
|
149
|
+
export function braceExpanded(pattern: string): readonly string[] {
|
|
150
|
+
const open = pattern.indexOf("{");
|
|
151
|
+
const close = open < 0 ? -1 : pattern.indexOf("}", open);
|
|
152
|
+
if (close < 0) {
|
|
153
|
+
return [pattern];
|
|
154
|
+
}
|
|
155
|
+
const before = pattern.slice(0, open);
|
|
156
|
+
const after = pattern.slice(close + 1);
|
|
157
|
+
return pattern
|
|
158
|
+
.slice(open + 1, close)
|
|
159
|
+
.split(",")
|
|
160
|
+
.flatMap((member) => braceExpanded(before + member + after));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The expression one whole-path pattern of the configured form denotes. */
|
|
164
|
+
export function globExpression(pattern: string): RegExp {
|
|
165
|
+
const alternatives = braceExpanded(pattern).map((one) => source(one, "configured"));
|
|
166
|
+
return new RegExp(`^(?:${alternatives.join("|")})$`, "u");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The expression one `.gitignore` pattern denotes over a path below its file's directory. */
|
|
170
|
+
export function gitignoreExpression(pattern: string): RegExp {
|
|
171
|
+
return new RegExp(`^${source(pattern, "gitignore")}$`, "u");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const SEGMENTS = new Map<string, RegExp>();
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Whether one directory name matches one segment of a workspace's member pattern, its brace
|
|
178
|
+
* lists already expanded. Neither `*` nor `?` matches a leading dot, as npm's workspaces read
|
|
179
|
+
* them: https://docs.npmjs.com/cli/using-npm/workspaces
|
|
180
|
+
*/
|
|
181
|
+
export function segmentMatches(segment: string, name: string): boolean {
|
|
182
|
+
if (name.startsWith(".") && !segment.startsWith(".")) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
let expression = SEGMENTS.get(segment);
|
|
186
|
+
if (expression === undefined) {
|
|
187
|
+
expression = globExpression(segment);
|
|
188
|
+
SEGMENTS.set(segment, expression);
|
|
189
|
+
}
|
|
190
|
+
return expression.test(name);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Whether one path's segments match a member pattern's, `**` standing for any run of segments
|
|
195
|
+
* that starts with no dot.
|
|
196
|
+
*/
|
|
197
|
+
export function pathMatches(pattern: readonly string[], path: readonly string[]): boolean {
|
|
198
|
+
const [head, ...rest] = pattern;
|
|
199
|
+
if (head === undefined) {
|
|
200
|
+
return path.length === 0;
|
|
201
|
+
}
|
|
202
|
+
if (head === "**") {
|
|
203
|
+
return (
|
|
204
|
+
pathMatches(rest, path) ||
|
|
205
|
+
(path.length > 0 && !(path[0] ?? "").startsWith(".") && pathMatches(pattern, path.slice(1)))
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
const [name, ...below] = path;
|
|
209
|
+
return name !== undefined && segmentMatches(head, name) && pathMatches(rest, below);
|
|
210
|
+
}
|