@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.10
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 +4 -3
- 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/manifest.ts
CHANGED
|
@@ -7,45 +7,47 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Host } from "./host.ts";
|
|
9
9
|
import { isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
10
|
+
import { isRecord } from "./json-document.ts";
|
|
11
|
+
import { MANIFEST, manifestAt, readJSON } from "./fs/manifest.ts";
|
|
12
|
+
import { scriptTokens } from "./text/shell.ts";
|
|
10
13
|
|
|
11
14
|
/** The manifest's own name. */
|
|
12
|
-
const MANIFEST = "package.json";
|
|
13
15
|
|
|
14
16
|
/** The three members that name an entry point beside `exports` and `bin`. */
|
|
15
17
|
const LEGACY_MEMBERS = ["main", "module", "types"] as const;
|
|
16
18
|
|
|
17
|
-
/** What reaches one entry
|
|
19
|
+
/** What reaches one entry the manifest names. */
|
|
18
20
|
type EntryRole =
|
|
19
21
|
/** A consumer importing the package, which is a caller the analysis cannot see. */
|
|
20
22
|
| "import"
|
|
21
23
|
/** A shell running the package's command, which is a caller that exists. */
|
|
22
24
|
| "run"
|
|
23
|
-
/** A token of one of the package's scripts,
|
|
25
|
+
/** A token of one of the package's scripts, or the whole of a command string the package runs. */
|
|
24
26
|
| "script";
|
|
25
27
|
|
|
26
|
-
/** One entry point the manifest names. */
|
|
28
|
+
/** One entry point the manifest names, or the whole of one command string the package runs. */
|
|
27
29
|
export interface ManifestEntry {
|
|
28
30
|
/**
|
|
29
|
-
* The member that named it
|
|
30
|
-
* `
|
|
31
|
-
* edits, so it is what a root reports as the string that named it.
|
|
31
|
+
* The member that named it as a maintainer edits it, `main`, `bin["deadset-ts"]` or
|
|
32
|
+
* `exports["."]["import"]`: what a root reports as the string that named it.
|
|
32
33
|
*/
|
|
33
34
|
readonly member: string;
|
|
34
|
-
/** The target the member carries, resolved against the manifest's directory. */
|
|
35
|
+
/** The target the member carries, resolved against the manifest's directory, or that directory for a whole command string. */
|
|
35
36
|
readonly path: string;
|
|
36
37
|
readonly role: EntryRole;
|
|
37
38
|
/**
|
|
38
|
-
* Whether the member declares what a consumer may import,
|
|
39
|
-
*
|
|
40
|
-
* `exports` is the surface a library's published API is read from.
|
|
39
|
+
* Whether the member declares what a consumer may import, the surface a library's published
|
|
40
|
+
* API is read from: `exports`, `browser` and the three members that preceded `exports`.
|
|
41
41
|
*/
|
|
42
42
|
readonly published: boolean;
|
|
43
43
|
/** The directory of the manifest that names it, which `path` was resolved against. */
|
|
44
44
|
readonly dir: string;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
/**
|
|
46
|
+
* On the entry of a whole command string, which names no entry point and whose path is the
|
|
47
|
+
* manifest's directory, that string: rooting enters no file for it and holds each file
|
|
48
|
+
* `spelledFiles` reads it to name.
|
|
49
|
+
*/
|
|
50
|
+
readonly command?: string;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -60,35 +62,45 @@ function isTarget(value: string, requireDot: boolean): boolean {
|
|
|
60
62
|
return !requireDot || value.startsWith("./");
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
/** Where a script's command line splits into tokens: white space and the shell's operators. */
|
|
64
|
-
const SCRIPT_SEPARATORS = /\s+|&&|\|\||;|\|/u;
|
|
65
|
-
|
|
66
|
-
/** A token written inside one pair of quotes. */
|
|
67
|
-
const QUOTED = /^(["'])(.*)\1$/su;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Every token one script's command line holds, each with one pair of enclosing quotes
|
|
71
|
-
* removed. A token holding a wildcard is a pattern rather than a path, and names no
|
|
72
|
-
* entry point.
|
|
73
|
-
*/
|
|
74
|
-
export function scriptTokens(command: string): readonly string[] {
|
|
75
|
-
return command
|
|
76
|
-
.split(SCRIPT_SEPARATORS)
|
|
77
|
-
.map((token) => token.replace(QUOTED, "$2"))
|
|
78
|
-
.filter((token) => token !== "" && !token.includes("*"));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
65
|
/** One member's spelling below another: `exports` then `exports["."]`. */
|
|
82
66
|
function below(member: string, key: string): string {
|
|
83
67
|
return `${member}[${JSON.stringify(key)}]`;
|
|
84
68
|
}
|
|
85
69
|
|
|
86
70
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
|
|
71
|
+
* The command strings one manifest runs, each at the member it is written under: each of its
|
|
72
|
+
* `scripts`, and the `exec` command of nodemon's configuration, in the manifest's `nodemonConfig`
|
|
73
|
+
* or the `nodemon.json` beside it, which nodemon runs the watched script with:
|
|
74
|
+
* https://github.com/remy/nodemon#config-files
|
|
75
|
+
*/
|
|
76
|
+
export function manifestCommands(
|
|
77
|
+
host: Host,
|
|
78
|
+
dir: string,
|
|
79
|
+
manifest: Record<string, unknown>,
|
|
80
|
+
): readonly { readonly member: string; readonly command: string }[] {
|
|
81
|
+
const scripts = manifest["scripts"];
|
|
82
|
+
const nodemon = {
|
|
83
|
+
nodemonConfig: manifest["nodemonConfig"],
|
|
84
|
+
"nodemon.json": readJSON(host, joinPath(dir, "nodemon.json")),
|
|
85
|
+
};
|
|
86
|
+
const written = [
|
|
87
|
+
...Object.entries(isRecord(scripts) ? scripts : {}).map(
|
|
88
|
+
([name, value]) => [below("scripts", name), value] as const,
|
|
89
|
+
),
|
|
90
|
+
...Object.entries(nodemon).map(
|
|
91
|
+
([member, config]) =>
|
|
92
|
+
[below(member, "exec"), isRecord(config) ? config["exec"] : undefined] as const,
|
|
93
|
+
),
|
|
94
|
+
];
|
|
95
|
+
return written.flatMap(([member, command]) =>
|
|
96
|
+
typeof command === "string" ? [{ member, command }] : [],
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Every target one `exports` value carries, at the member each is written under. Every level of
|
|
102
|
+
* strings, subpath or condition objects and alternative arrays is walked, because each condition
|
|
103
|
+
* names its own entry file; a `null` blocks a subpath, and a key's meaning is never read.
|
|
92
104
|
*/
|
|
93
105
|
function exportTargets(value: unknown, member: string, found: Omit<ManifestEntry, "dir">[]): void {
|
|
94
106
|
if (typeof value === "string") {
|
|
@@ -111,41 +123,18 @@ function exportTargets(value: unknown, member: string, found: Omit<ManifestEntry
|
|
|
111
123
|
}
|
|
112
124
|
}
|
|
113
125
|
|
|
114
|
-
/** The manifest at the target root, decoded, or undefined where there is none to read. */
|
|
115
|
-
function manifestOf(host: Host, targetRoot: string): Record<string, unknown> | undefined {
|
|
116
|
-
let text: string;
|
|
117
|
-
try {
|
|
118
|
-
text = host.readFile(joinPath(targetRoot, MANIFEST));
|
|
119
|
-
} catch {
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
let value: unknown;
|
|
123
|
-
try {
|
|
124
|
-
value = JSON.parse(text);
|
|
125
|
-
} catch {
|
|
126
|
-
// A manifest this program cannot read names no entry point, which is the same
|
|
127
|
-
// answer as an absent one. What a manifest's contents say is not refused here:
|
|
128
|
-
// the package manager owns that refusal, and an analysis of a tree it rejects
|
|
129
|
-
// is not the run's subject.
|
|
130
|
-
return undefined;
|
|
131
|
-
}
|
|
132
|
-
return isRecord(value) ? value : undefined;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
126
|
/** What one manifest declares about where its package is entered. */
|
|
136
127
|
export interface Manifest {
|
|
137
128
|
/**
|
|
138
|
-
* Every entry
|
|
139
|
-
* `module`, `types`,
|
|
140
|
-
*
|
|
141
|
-
*
|
|
129
|
+
* Every entry the manifest names, by absolute path, in the order read: the entry points `main`,
|
|
130
|
+
* `module`, `types`, `browser` and each file it maps, each `bin` command, every `exports` target
|
|
131
|
+
* and per script each token, which names a file only where one is at its path; and per command
|
|
132
|
+
* string {@link manifestCommands} reads one entry of its whole text, which is no entry point.
|
|
142
133
|
*/
|
|
143
134
|
readonly entries: readonly ManifestEntry[];
|
|
144
135
|
/**
|
|
145
|
-
* Whether an `exports` member is written
|
|
146
|
-
*
|
|
147
|
-
* reaches; a package that declares none publishes whatever the members that
|
|
148
|
-
* preceded it name.
|
|
136
|
+
* Whether an `exports` member is written: then the published API is what `exports` reaches,
|
|
137
|
+
* and otherwise whatever the members that preceded it name.
|
|
149
138
|
*/
|
|
150
139
|
readonly declaresExports: boolean;
|
|
151
140
|
}
|
|
@@ -158,7 +147,7 @@ export interface Manifest {
|
|
|
158
147
|
* it and there is one such configuration per project, while there is one manifest.
|
|
159
148
|
*/
|
|
160
149
|
export function readManifest(host: Host, targetRoot: string): Manifest {
|
|
161
|
-
const manifest =
|
|
150
|
+
const manifest = manifestAt(host, targetRoot);
|
|
162
151
|
if (manifest === undefined) {
|
|
163
152
|
return { entries: [], declaresExports: false };
|
|
164
153
|
}
|
|
@@ -171,6 +160,24 @@ export function readManifest(host: Host, targetRoot: string): Manifest {
|
|
|
171
160
|
}
|
|
172
161
|
}
|
|
173
162
|
|
|
163
|
+
// A `browser` string replaces `main` in a browser bundle, and an object replaces each
|
|
164
|
+
// module it maps with the file it names: https://github.com/defunctzombie/package-browser-field-spec
|
|
165
|
+
const browser = manifest["browser"];
|
|
166
|
+
if (typeof browser === "string" && isTarget(browser, false)) {
|
|
167
|
+
found.push({ member: "browser", path: browser, role: "import", published: true });
|
|
168
|
+
} else if (isRecord(browser)) {
|
|
169
|
+
for (const [replaced, value] of Object.entries(browser)) {
|
|
170
|
+
if (typeof value === "string" && isTarget(value, true)) {
|
|
171
|
+
found.push({
|
|
172
|
+
member: below("browser", replaced),
|
|
173
|
+
path: value,
|
|
174
|
+
role: "import",
|
|
175
|
+
published: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
174
181
|
const bin = manifest["bin"];
|
|
175
182
|
if (typeof bin === "string" && isTarget(bin, false)) {
|
|
176
183
|
found.push({ member: "bin", path: bin, role: "run", published: false });
|
|
@@ -190,16 +197,15 @@ export function readManifest(host: Host, targetRoot: string): Manifest {
|
|
|
190
197
|
if (typeof command !== "string") {
|
|
191
198
|
continue;
|
|
192
199
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
path: token,
|
|
197
|
-
role: "script",
|
|
198
|
-
published: false,
|
|
199
|
-
});
|
|
200
|
+
const member = below("scripts", name);
|
|
201
|
+
for (const path of new Set(scriptTokens(command))) {
|
|
202
|
+
found.push({ member, path, role: "script", published: false });
|
|
200
203
|
}
|
|
201
204
|
}
|
|
202
205
|
}
|
|
206
|
+
for (const { member, command } of manifestCommands(host, targetRoot, manifest)) {
|
|
207
|
+
found.push({ member, path: ".", role: "script", published: false, command });
|
|
208
|
+
}
|
|
203
209
|
|
|
204
210
|
return {
|
|
205
211
|
entries: found.map((entry) => ({
|
package/src/matrix.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface Configured {
|
|
|
30
30
|
readonly testFiles: readonly string[];
|
|
31
31
|
/** The paths of the test-support files, which only test code imports. */
|
|
32
32
|
readonly supportFiles?: readonly string[] | undefined;
|
|
33
|
+
/** The paths of the files only tests reach that are no test-support file. */
|
|
34
|
+
readonly testUsedFiles?: readonly string[] | undefined;
|
|
33
35
|
/** The ids of the ambient declarations, which describe code the target does not implement. */
|
|
34
36
|
readonly ambient?: ReadonlySet<string> | undefined;
|
|
35
37
|
/**
|
|
@@ -60,6 +62,11 @@ export interface Matrix {
|
|
|
60
62
|
* whose project holds it, in the run's order.
|
|
61
63
|
*/
|
|
62
64
|
readonly heldIn: readonly (readonly string[])[];
|
|
65
|
+
/**
|
|
66
|
+
* Per declaration of {@link Matrix.union}, whether a file only tests reach that is no
|
|
67
|
+
* test-support file holds it in some configuration: production code only tests use.
|
|
68
|
+
*/
|
|
69
|
+
readonly testUsed: readonly boolean[];
|
|
63
70
|
/** Each declaration's dependents, by id, as the first configuration holding it states them. */
|
|
64
71
|
readonly dependents: ReadonlyMap<string, readonly string[]>;
|
|
65
72
|
}
|
|
@@ -81,6 +88,17 @@ export interface MatrixCandidate extends Candidate {
|
|
|
81
88
|
* shape nothing references would be.
|
|
82
89
|
*/
|
|
83
90
|
readonly unreferencedTest: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Whether it is dead only because code only tests use is: production code only tests
|
|
93
|
+
* use holds it and the run that counts test references reaches it, or such a
|
|
94
|
+
* declaration reaches it through dead declarations.
|
|
95
|
+
*/
|
|
96
|
+
readonly testUsed: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Whether test code the run that counts test references reaches, and that is no test of
|
|
99
|
+
* dead code, references it: a test that stays keeps it.
|
|
100
|
+
*/
|
|
101
|
+
readonly keptByTests: boolean;
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
/** One run's sweep. */
|
|
@@ -158,6 +176,7 @@ export function matrixOf(per: readonly Configured[]): Matrix {
|
|
|
158
176
|
}
|
|
159
177
|
});
|
|
160
178
|
symbols.sort((a, b) => byPosition(a.position, b.position));
|
|
179
|
+
const testUsed = new Set(per.flatMap((one) => one.testUsedFiles ?? []));
|
|
161
180
|
|
|
162
181
|
return {
|
|
163
182
|
configurations: per.map((one) => one.configuration),
|
|
@@ -173,6 +192,7 @@ export function matrixOf(per: readonly Configured[]): Matrix {
|
|
|
173
192
|
new Set(per.flatMap((one) => [...(one.ambient ?? [])])),
|
|
174
193
|
),
|
|
175
194
|
heldIn: symbols.map((symbol) => holders.get(symbol.id) ?? []),
|
|
195
|
+
testUsed: symbols.map((symbol) => testUsed.has(symbol.position.path)),
|
|
176
196
|
dependents,
|
|
177
197
|
};
|
|
178
198
|
}
|
|
@@ -203,18 +223,32 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
203
223
|
|
|
204
224
|
const union = matrix.union;
|
|
205
225
|
const heldBy: (string | undefined)[] = union.symbols.map(() => undefined);
|
|
206
|
-
const
|
|
226
|
+
const held = [
|
|
207
227
|
...input.marked,
|
|
208
228
|
...(input.exempt ?? []).map((record) => record.id),
|
|
209
229
|
...(input.unanswered ?? []),
|
|
210
|
-
]
|
|
211
|
-
const
|
|
230
|
+
];
|
|
231
|
+
const testReached = reachedWithTheTests(union, held);
|
|
232
|
+
// A test uses what it names: a file a test reads through a star re-export or a
|
|
233
|
+
// namespace keeps the exports nothing names at their own class.
|
|
234
|
+
const testNamed = reachedWithTheTests(union, held, false);
|
|
235
|
+
const everywhere = union.symbols.map((symbol, at): readonly Candidate[] | undefined => {
|
|
212
236
|
const held = matrix.heldIn[at] ?? [];
|
|
213
237
|
const found = held.map((configuration) => byConfiguration.get(configuration)?.get(symbol.id));
|
|
214
238
|
return held.length === 0 || found.some((candidate) => candidate === undefined)
|
|
215
239
|
? undefined
|
|
216
240
|
: found.filter((candidate): candidate is Candidate => candidate !== undefined);
|
|
217
241
|
});
|
|
242
|
+
const compiled = compiledReferences(
|
|
243
|
+
matrix,
|
|
244
|
+
new Set(
|
|
245
|
+
union.symbols.flatMap((symbol, at) => (everywhere[at] === undefined ? [] : [symbol.id])),
|
|
246
|
+
),
|
|
247
|
+
input.mode,
|
|
248
|
+
);
|
|
249
|
+
const judged = everywhere.map((found, at) =>
|
|
250
|
+
compiled.has(union.symbols[at]?.id ?? "") ? undefined : found,
|
|
251
|
+
);
|
|
218
252
|
// A configuration that does not hold a test's subject's caller finds the subject dead,
|
|
219
253
|
// so a test is of dead code only where every production subject it names is dead in
|
|
220
254
|
// every configuration that holds it.
|
|
@@ -227,6 +261,14 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
227
261
|
union.subject[edge.to] !== true ||
|
|
228
262
|
judged[edge.to] !== undefined,
|
|
229
263
|
);
|
|
264
|
+
const admittedAt = (at: number): boolean =>
|
|
265
|
+
judged[at]?.every((candidate) => candidate.testOfDeadCode) === true && subjectsDead(at);
|
|
266
|
+
const kept = keptByTests(union, testReached, admittedAt);
|
|
267
|
+
const testUsed = testUsedReach(
|
|
268
|
+
union,
|
|
269
|
+
union.symbols.map((_symbol, at) => matrix.testUsed[at] === true && testNamed[at] === true),
|
|
270
|
+
judged.map((one) => one !== undefined),
|
|
271
|
+
);
|
|
230
272
|
const candidates: MatrixCandidate[] = [];
|
|
231
273
|
union.symbols.forEach((symbol, at) => {
|
|
232
274
|
const all = judged[at];
|
|
@@ -234,7 +276,7 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
234
276
|
return;
|
|
235
277
|
}
|
|
236
278
|
const held = matrix.heldIn[at] ?? [];
|
|
237
|
-
const admitted =
|
|
279
|
+
const admitted = admittedAt(at);
|
|
238
280
|
const testRefs = union.made[at]?.test ?? 0;
|
|
239
281
|
const heldByTests =
|
|
240
282
|
heldBy[union.parent[at] ?? OUTSIDE] ??
|
|
@@ -255,6 +297,8 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
255
297
|
configurations: held,
|
|
256
298
|
heldByTests,
|
|
257
299
|
unreferencedTest: unreferenced,
|
|
300
|
+
testUsed: testUsed[at] === true,
|
|
301
|
+
keptByTests: kept[at] === true,
|
|
258
302
|
});
|
|
259
303
|
});
|
|
260
304
|
|
|
@@ -266,6 +310,80 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
266
310
|
};
|
|
267
311
|
}
|
|
268
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Every declaration of `dead`, those dead in every configuration that holds them, that a
|
|
315
|
+
* configuration's graph references from a judged declaration outside `dead` or already
|
|
316
|
+
* kept, until none joins: deleting one would break that configuration's compile. A
|
|
317
|
+
* production run counts no reference a test file made.
|
|
318
|
+
*/
|
|
319
|
+
function compiledReferences(
|
|
320
|
+
matrix: Matrix,
|
|
321
|
+
dead: ReadonlySet<string>,
|
|
322
|
+
mode: SweepInput["mode"],
|
|
323
|
+
): ReadonlySet<string> {
|
|
324
|
+
const kept = new Set<string>();
|
|
325
|
+
for (let changed = true; changed;) {
|
|
326
|
+
changed = false;
|
|
327
|
+
for (const graph of matrix.graphs) {
|
|
328
|
+
graph.out.forEach((edges, from) => {
|
|
329
|
+
const id = graph.symbols[from]?.id ?? "";
|
|
330
|
+
if (graph.subject[from] !== true || (dead.has(id) && !kept.has(id))) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
for (const edge of edges) {
|
|
334
|
+
const to = graph.symbols[edge.to]?.id ?? "";
|
|
335
|
+
if ((!mode.production || !edge.test) && dead.has(to) && !kept.has(to)) {
|
|
336
|
+
kept.add(to);
|
|
337
|
+
changed = true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return kept;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Per declaration of the union, whether one of `seeds` is it or reaches it through `dead` declarations. */
|
|
347
|
+
function testUsedReach(
|
|
348
|
+
union: Graph,
|
|
349
|
+
seeds: readonly boolean[],
|
|
350
|
+
dead: readonly boolean[],
|
|
351
|
+
): readonly boolean[] {
|
|
352
|
+
// A file is no candidate, and its top level is what reaches what it alone calls.
|
|
353
|
+
const reached = seeds.map((seed, at) => seed && dead[at] === true);
|
|
354
|
+
const queue = seeds.flatMap((one, at) => (one ? [at] : []));
|
|
355
|
+
for (let at = queue.pop(); at !== undefined; at = queue.pop()) {
|
|
356
|
+
for (const edge of union.out[at] ?? []) {
|
|
357
|
+
if (dead[edge.to] === true && reached[edge.to] !== true) {
|
|
358
|
+
reached[edge.to] = true;
|
|
359
|
+
queue.push(edge.to);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return reached;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Per declaration of the union, whether test code `reached` holds and `admitted` rejects references it. */
|
|
367
|
+
function keptByTests(
|
|
368
|
+
union: Graph,
|
|
369
|
+
reached: readonly boolean[],
|
|
370
|
+
admitted: (at: number) => boolean,
|
|
371
|
+
): readonly boolean[] {
|
|
372
|
+
const kept = union.symbols.map(() => false);
|
|
373
|
+
union.out.forEach((edges, from) => {
|
|
374
|
+
const testCode = union.test[from] === true || union.support[from] === true;
|
|
375
|
+
if (!testCode || reached[from] !== true || admitted(from)) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
for (const edge of edges) {
|
|
379
|
+
if (edge.test) {
|
|
380
|
+
kept[edge.to] = true;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
return kept;
|
|
385
|
+
}
|
|
386
|
+
|
|
269
387
|
/**
|
|
270
388
|
* The dead components of one sweep over the union, with the file declarations at `files`
|
|
271
389
|
* dead as well: a file that is deleted whole takes the declarations it holds and every
|
package/src/mdx-entries.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { resolveRelativeSpecifier } from "./configuration-files.ts";
|
|
|
9
9
|
import type { Host } from "./host.ts";
|
|
10
10
|
import { dirnamePath, joinPath, resolvePath } from "./paths.ts";
|
|
11
11
|
import type { SourceFiles } from "./source-files.ts";
|
|
12
|
+
import { filesBelow } from "./fs/tree.ts";
|
|
12
13
|
|
|
13
14
|
/** One file a document imports, the specifier that named it, and the export names it reads. */
|
|
14
15
|
interface DocumentEntry {
|
|
@@ -18,9 +19,6 @@ interface DocumentEntry {
|
|
|
18
19
|
readonly names: readonly string[] | undefined;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
/** The directories no document is read below. */
|
|
22
|
-
const SKIPPED: ReadonlySet<string> = new Set(["node_modules", ".git"]);
|
|
23
|
-
|
|
24
22
|
/** The extensions of a document whose imports an MDX compiler reads. */
|
|
25
23
|
const DOCUMENT = /\.mdx?$/u;
|
|
26
24
|
|
|
@@ -129,28 +127,6 @@ function clauseNames(clause: string): readonly string[] | undefined {
|
|
|
129
127
|
return names;
|
|
130
128
|
}
|
|
131
129
|
|
|
132
|
-
/** Every document below `dir`, outside every skipped directory, by absolute path. */
|
|
133
|
-
function documents(host: Host, dir: string): readonly string[] {
|
|
134
|
-
let entries;
|
|
135
|
-
try {
|
|
136
|
-
entries = host.readDirectory(dir);
|
|
137
|
-
} catch {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
const found: string[] = [];
|
|
141
|
-
for (const entry of entries) {
|
|
142
|
-
const path = joinPath(dir, entry.name);
|
|
143
|
-
if (entry.directory) {
|
|
144
|
-
if (!SKIPPED.has(entry.name)) {
|
|
145
|
-
found.push(...documents(host, path));
|
|
146
|
-
}
|
|
147
|
-
} else if (DOCUMENT.test(entry.name)) {
|
|
148
|
-
found.push(path);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return found.sort();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
130
|
/** The relative specifiers one document's import lines name, its fenced code blocks skipped. */
|
|
155
131
|
function importedBy(
|
|
156
132
|
text: string,
|
|
@@ -205,7 +181,8 @@ function documentImports(host: Host, root: string): readonly DocumentImports[] {
|
|
|
205
181
|
if (known !== undefined) {
|
|
206
182
|
return known;
|
|
207
183
|
}
|
|
208
|
-
const found =
|
|
184
|
+
const found = filesBelow(host, root, (name) => DOCUMENT.test(name)).flatMap((below) => {
|
|
185
|
+
const path = joinPath(root, below);
|
|
209
186
|
try {
|
|
210
187
|
return [{ path, imports: importedBy(host.readFile(path)) }];
|
|
211
188
|
} catch {
|
package/src/outside-tests.ts
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
type SourceFile,
|
|
21
21
|
} from "@typescript/native/ast";
|
|
22
22
|
import { RESOLVED_EXTENSIONS, type ParseFile } from "./configuration-files.ts";
|
|
23
|
-
import { emittedFrom } from "./emit-map.ts";
|
|
23
|
+
import { emittedFrom } from "./load/emit-map.ts";
|
|
24
24
|
import type { Host } from "./host.ts";
|
|
25
25
|
import type { Inventory } from "./inventory.ts";
|
|
26
26
|
import { namesImported } from "./module-declarations.ts";
|
package/src/partial.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { NotBuilt } from "./discover.ts";
|
|
1
|
+
import type { NotBuilt } from "./load/discover.ts";
|
|
2
2
|
import { relativePath } from "./paths.ts";
|
|
3
3
|
import type { UnansweredQuestion } from "./query.ts";
|
|
4
|
+
import { bytewise } from "./text/compare.ts";
|
|
4
5
|
|
|
5
6
|
/** What a run read only in part. */
|
|
6
7
|
interface Partial {
|
|
@@ -24,7 +25,7 @@ function counted(n: number, one: string, many: string): string {
|
|
|
24
25
|
*/
|
|
25
26
|
export function partialNotes(partial: Partial): readonly string[] {
|
|
26
27
|
const notes = [...partial.notBuilt]
|
|
27
|
-
.sort((a, b) => (a.id
|
|
28
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
28
29
|
.map(
|
|
29
30
|
(one) =>
|
|
30
31
|
`the derived configuration ${one.id} was not built and is not analyzed: ${one.error}`,
|
package/src/paths.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Filesystem paths
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* They are written here rather than taken from the platform's own library because
|
|
6
|
-
* every module below the command line stays free of the platform, and because the
|
|
7
|
-
* supported platform set is one, so a second spelling would be a rule nothing
|
|
8
|
-
* exercises.
|
|
2
|
+
* Filesystem paths as the supported platform spells them: a solidus between segments, a
|
|
3
|
+
* leading one for an absolute path. They are written here rather than taken from the
|
|
4
|
+
* platform's library so every module below the command line stays free of the platform.
|
|
9
5
|
*/
|
|
10
6
|
|
|
11
7
|
const SEPARATOR = "/";
|
|
@@ -67,6 +63,25 @@ export function dirnamePath(path: string): string {
|
|
|
67
63
|
return cut === 0 ? SEPARATOR : normalized.slice(0, cut);
|
|
68
64
|
}
|
|
69
65
|
|
|
66
|
+
/**
|
|
67
|
+
* `dir` and every directory above it, nearest first, normalized. With `top`, the list ends
|
|
68
|
+
* at `top` and is empty where `dir` is neither `top` nor below it; without, it ends at the
|
|
69
|
+
* root of the path.
|
|
70
|
+
*/
|
|
71
|
+
export function ancestors(dir: string, top?: string): readonly string[] {
|
|
72
|
+
const end = top === undefined ? undefined : normalizePath(top);
|
|
73
|
+
const found: string[] = [];
|
|
74
|
+
for (let at = normalizePath(dir); ; at = dirnamePath(at)) {
|
|
75
|
+
if (end !== undefined && at !== end && relativePath(end, at) === undefined) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
found.push(at);
|
|
79
|
+
if (at === end || dirnamePath(at) === at) {
|
|
80
|
+
return found;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
/**
|
|
71
86
|
* The extension of one path's last segment, from its last full stop, or the empty string
|
|
72
87
|
* where it has none. A segment that only starts with a full stop has none.
|
|
@@ -101,14 +116,11 @@ export function queriedModulePath(specifier: string): string | undefined {
|
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
/**
|
|
104
|
-
* One path as it reads below a root, or undefined when it is not below the root
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* The caller resolves both against the same directory before calling: a relative
|
|
111
|
-
* root and an absolute path are two spellings this cannot reconcile.
|
|
119
|
+
* One path as it reads below a root, or undefined when it is not strictly below the root:
|
|
120
|
+
* the root itself is not below itself. Both are normalized, then compared lexically, so a
|
|
121
|
+
* root and a path spelled through different symbolic links name nothing in common. The
|
|
122
|
+
* caller resolves both against the same directory first: a relative root and an absolute
|
|
123
|
+
* path cannot be reconciled.
|
|
112
124
|
*/
|
|
113
125
|
export function relativePath(root: string, path: string): string | undefined {
|
|
114
126
|
const at = normalizePath(root);
|
package/src/position.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SourceFile } from "@typescript/native/ast";
|
|
2
2
|
import { relativePath } from "./paths.ts";
|
|
3
|
+
import { bytewise } from "./text/compare.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Where one declaration is written, as every report renders it.
|
|
@@ -87,7 +88,7 @@ export function positionKey(position: Position): string {
|
|
|
87
88
|
*/
|
|
88
89
|
export function byPosition(a: Position, b: Position): number {
|
|
89
90
|
if (a.path !== b.path) {
|
|
90
|
-
return a.path
|
|
91
|
+
return bytewise(a.path, b.path);
|
|
91
92
|
}
|
|
92
93
|
if (a.line !== b.line) {
|
|
93
94
|
return a.line - b.line;
|