@cplieger/deadset-ts 7.2.0-dev.4 → 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 +245 -309
- package/dist/src/augmentations.js +124 -18
- package/dist/src/automatic-types.js +170 -0
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +9 -9
- package/dist/src/config.js +6 -81
- package/dist/src/configuration-files.js +78 -188
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +2 -2
- 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 +146 -1
- package/dist/src/conventions.js +136 -70
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +280 -62
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +9 -18
- package/dist/src/entry-points.js +106 -21
- 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 +225 -63
- package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
- package/dist/src/findings/emitters.js +6 -6
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +53 -19
- package/dist/src/findings/non-code-artifacts.js +71 -39
- 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 +57 -28
- package/dist/src/findings/visibility-narrowing.js +40 -18
- 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 +18 -4
- package/dist/src/git-ignore.js +99 -0
- package/dist/src/graph.js +9 -4
- package/dist/src/html-entries.js +6 -33
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +341 -69
- package/dist/src/inventory.js +54 -29
- 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} +88 -61
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
- 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 +94 -13
- package/dist/src/mdx-entries.js +174 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +41 -10
- 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 -78
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +11 -0
- package/dist/src/references.js +52 -31
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +59 -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 -727
- package/dist/src/roots.js +374 -116
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -342
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +649 -29
- package/dist/src/session.js +41 -29
- package/dist/src/source-files.js +40 -15
- package/dist/src/suppress-file.js +34 -29
- package/dist/src/suppress-inline.js +21 -4
- package/dist/src/suppress.js +40 -56
- package/dist/src/sweep.js +32 -17
- package/dist/src/symbol-keys.js +105 -6
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +119 -18
- 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 +27 -221
- package/dist/src/type-parameter-reads.js +172 -0
- 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 +181 -125
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +14 -9
- 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 +128 -33
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +333 -457
- package/src/augmentations.ts +152 -23
- package/src/automatic-types.ts +228 -0
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +9 -10
- package/src/config.ts +31 -299
- package/src/configuration-files.ts +100 -191
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +2 -2
- 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 +186 -1
- package/src/conventions.ts +166 -90
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +356 -78
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +11 -20
- package/src/entry-points.ts +124 -28
- 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 +24 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +297 -77
- package/src/findings/dependencies-and-module-machinery.ts +90 -31
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +8 -8
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +64 -20
- package/src/findings/non-code-artifacts.ts +88 -50
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +65 -28
- package/src/findings/visibility-narrowing.ts +51 -19
- 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 +23 -4
- package/src/git-ignore.ts +122 -0
- package/src/graph.ts +11 -3
- package/src/host.ts +17 -14
- package/src/html-entries.ts +6 -33
- package/src/import-bindings.ts +128 -0
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +152 -30
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +449 -85
- package/src/inventory.ts +73 -27
- 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} +127 -80
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
- 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 +154 -18
- package/src/mdx-entries.ts +218 -0
- package/src/module-declarations.ts +11 -13
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +51 -12
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -91
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +13 -0
- package/src/references.ts +62 -32
- package/src/report-schema.ts +68 -0
- package/src/report.ts +105 -215
- 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 -1111
- package/src/roots.ts +490 -137
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -387
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +818 -26
- package/src/session.ts +77 -54
- package/src/source-files.ts +67 -17
- package/src/suppress-file.ts +36 -34
- package/src/suppress-inline.ts +20 -3
- package/src/suppress.ts +52 -63
- package/src/sweep.ts +53 -18
- package/src/symbol-keys.ts +131 -7
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +155 -18
- 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 +35 -273
- package/src/type-parameter-reads.ts +227 -0
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +245 -161
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +15 -13
- 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 +149 -34
- package/dist/src/discover.js +0 -347
- 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/discover.ts +0 -510
- 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,10 @@ 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;
|
|
35
|
+
/** The ids of the ambient declarations, which describe code the target does not implement. */
|
|
36
|
+
readonly ambient?: ReadonlySet<string> | undefined;
|
|
33
37
|
/**
|
|
34
38
|
* The paths of the workspace-package files the configuration holds but does not
|
|
35
39
|
* compile itself. Their declarations count in its graph and its verdict on them is no
|
|
@@ -58,6 +62,11 @@ export interface Matrix {
|
|
|
58
62
|
* whose project holds it, in the run's order.
|
|
59
63
|
*/
|
|
60
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[];
|
|
61
70
|
/** Each declaration's dependents, by id, as the first configuration holding it states them. */
|
|
62
71
|
readonly dependents: ReadonlyMap<string, readonly string[]>;
|
|
63
72
|
}
|
|
@@ -79,6 +88,17 @@ export interface MatrixCandidate extends Candidate {
|
|
|
79
88
|
* shape nothing references would be.
|
|
80
89
|
*/
|
|
81
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;
|
|
82
102
|
}
|
|
83
103
|
|
|
84
104
|
/** One run's sweep. */
|
|
@@ -156,11 +176,12 @@ export function matrixOf(per: readonly Configured[]): Matrix {
|
|
|
156
176
|
}
|
|
157
177
|
});
|
|
158
178
|
symbols.sort((a, b) => byPosition(a.position, b.position));
|
|
179
|
+
const testUsed = new Set(per.flatMap((one) => one.testUsedFiles ?? []));
|
|
159
180
|
|
|
160
181
|
return {
|
|
161
182
|
configurations: per.map((one) => one.configuration),
|
|
162
183
|
graphs: per.map((one) =>
|
|
163
|
-
graphOf(one.symbols, one.references, one.roots, one.testFiles, one.supportFiles),
|
|
184
|
+
graphOf(one.symbols, one.references, one.roots, one.testFiles, one.supportFiles, one.ambient),
|
|
164
185
|
),
|
|
165
186
|
union: graphOf(
|
|
166
187
|
symbols,
|
|
@@ -168,8 +189,10 @@ export function matrixOf(per: readonly Configured[]): Matrix {
|
|
|
168
189
|
per.flatMap((one) => one.roots),
|
|
169
190
|
[...new Set(per.flatMap((one) => one.testFiles))],
|
|
170
191
|
[...new Set(per.flatMap((one) => one.supportFiles ?? []))],
|
|
192
|
+
new Set(per.flatMap((one) => [...(one.ambient ?? [])])),
|
|
171
193
|
),
|
|
172
194
|
heldIn: symbols.map((symbol) => holders.get(symbol.id) ?? []),
|
|
195
|
+
testUsed: symbols.map((symbol) => testUsed.has(symbol.position.path)),
|
|
173
196
|
dependents,
|
|
174
197
|
};
|
|
175
198
|
}
|
|
@@ -199,22 +222,33 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
199
222
|
);
|
|
200
223
|
|
|
201
224
|
const union = matrix.union;
|
|
202
|
-
const dead = union.symbols.map(() => false);
|
|
203
|
-
const testOfDeadCode = union.symbols.map(() => false);
|
|
204
225
|
const heldBy: (string | undefined)[] = union.symbols.map(() => undefined);
|
|
205
|
-
const
|
|
206
|
-
const testReached = reachedWithTheTests(union, [
|
|
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.
|
|
@@ -223,9 +257,18 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
223
257
|
(edge) =>
|
|
224
258
|
union.test[edge.to] === true ||
|
|
225
259
|
union.support[edge.to] === true ||
|
|
260
|
+
union.ambient[edge.to] === true ||
|
|
226
261
|
union.subject[edge.to] !== true ||
|
|
227
262
|
judged[edge.to] !== undefined,
|
|
228
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
|
+
);
|
|
229
272
|
const candidates: MatrixCandidate[] = [];
|
|
230
273
|
union.symbols.forEach((symbol, at) => {
|
|
231
274
|
const all = judged[at];
|
|
@@ -233,16 +276,13 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
233
276
|
return;
|
|
234
277
|
}
|
|
235
278
|
const held = matrix.heldIn[at] ?? [];
|
|
236
|
-
const admitted =
|
|
279
|
+
const admitted = admittedAt(at);
|
|
237
280
|
const testRefs = union.made[at]?.test ?? 0;
|
|
238
281
|
const heldByTests =
|
|
239
282
|
heldBy[union.parent[at] ?? OUTSIDE] ??
|
|
240
283
|
(union.support[at] === true && testRefs > 0 && !admitted ? symbol.id : undefined);
|
|
241
284
|
const unreferenced = union.test[at] === true && !admitted && testReached[at] !== true;
|
|
242
|
-
dead[at] = true;
|
|
243
|
-
testOfDeadCode[at] = admitted;
|
|
244
285
|
heldBy[at] = heldByTests;
|
|
245
|
-
unreferencedTest[at] = unreferenced;
|
|
246
286
|
const productionRefs = union.made[at]?.production ?? 0;
|
|
247
287
|
const counted = all.every((candidate) => candidate.relation === "reference-counting");
|
|
248
288
|
candidates.push({
|
|
@@ -257,6 +297,8 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
257
297
|
configurations: held,
|
|
258
298
|
heldByTests,
|
|
259
299
|
unreferencedTest: unreferenced,
|
|
300
|
+
testUsed: testUsed[at] === true,
|
|
301
|
+
keptByTests: kept[at] === true,
|
|
260
302
|
});
|
|
261
303
|
});
|
|
262
304
|
|
|
@@ -264,16 +306,110 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
|
|
|
264
306
|
return {
|
|
265
307
|
liveUnder,
|
|
266
308
|
candidates,
|
|
267
|
-
components:
|
|
268
|
-
union,
|
|
269
|
-
dead.map((one, at) => one && heldBy[at] === undefined),
|
|
270
|
-
testOfDeadCode,
|
|
271
|
-
unreferencedTest,
|
|
272
|
-
positionsOf(union, matrix.dependents, liveUnder),
|
|
273
|
-
),
|
|
309
|
+
components: deadComponents(matrix, { liveUnder, candidates }, new Set()),
|
|
274
310
|
};
|
|
275
311
|
}
|
|
276
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
|
+
|
|
387
|
+
/**
|
|
388
|
+
* The dead components of one sweep over the union, with the file declarations at `files`
|
|
389
|
+
* dead as well: a file that is deleted whole takes the declarations it holds and every
|
|
390
|
+
* dead declaration only its top level references with it.
|
|
391
|
+
*/
|
|
392
|
+
export function deadComponents(
|
|
393
|
+
matrix: Matrix,
|
|
394
|
+
result: Pick<SweepResult, "liveUnder" | "candidates">,
|
|
395
|
+
files: ReadonlySet<number>,
|
|
396
|
+
): readonly Component[] {
|
|
397
|
+
const union = matrix.union;
|
|
398
|
+
const byId = new Map(result.candidates.map((candidate) => [candidate.id, candidate]));
|
|
399
|
+
const candidateAt = (at: number): MatrixCandidate | undefined =>
|
|
400
|
+
byId.get(union.symbols[at]?.id ?? "");
|
|
401
|
+
return componentsOf(
|
|
402
|
+
union,
|
|
403
|
+
union.symbols.map((_symbol, at) => {
|
|
404
|
+
const candidate = candidateAt(at);
|
|
405
|
+
return files.has(at) || (candidate !== undefined && candidate.heldByTests === undefined);
|
|
406
|
+
}),
|
|
407
|
+
union.symbols.map((_symbol, at) => candidateAt(at)?.testOfDeadCode === true),
|
|
408
|
+
union.symbols.map((_symbol, at) => candidateAt(at)?.unreferencedTest === true),
|
|
409
|
+
positionsOf(union, matrix.dependents, result.liveUnder),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
277
413
|
/**
|
|
278
414
|
* The file declarations at `paths`, below the target root, that one sweep holds live
|
|
279
415
|
* under both relations, the rule a judged declaration is live by.
|