@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/roots.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The root set of one project: every declaration
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* that names a caller is live under reference counting as well, which the sweep
|
|
7
|
-
* decides; the published API only supposes a consumer, so a published export is live
|
|
8
|
-
* under reachability and still a candidate under reference counting.
|
|
2
|
+
* The root set of one project: every declaration kept live without a reference, and why.
|
|
3
|
+
* Every root seeds reachability and none counts as a reference; a root of a kind that
|
|
4
|
+
* names a caller is live under reference counting too, which the sweep decides, so a
|
|
5
|
+
* published export, which only supposes a consumer, stays a reference-counting candidate.
|
|
9
6
|
*/
|
|
10
7
|
|
|
11
8
|
import {
|
|
9
|
+
isArrowFunction,
|
|
10
|
+
isAssertionExpression,
|
|
12
11
|
isExpressionWithTypeArguments,
|
|
13
12
|
isFunctionLikeDeclaration,
|
|
13
|
+
isIdentifier,
|
|
14
|
+
isMethodDeclaration,
|
|
15
|
+
isObjectLiteralExpression,
|
|
16
|
+
isParenthesizedExpression,
|
|
17
|
+
isPropertyAssignment,
|
|
18
|
+
isReturnStatement,
|
|
19
|
+
isSatisfiesExpression,
|
|
14
20
|
isPropertyAccessExpression,
|
|
15
21
|
isQualifiedName,
|
|
16
22
|
isTypeQueryNode,
|
|
@@ -18,10 +24,14 @@ import {
|
|
|
18
24
|
isPropertyDeclaration,
|
|
19
25
|
isVariableDeclaration,
|
|
20
26
|
isVariableStatement,
|
|
27
|
+
SyntaxKind,
|
|
28
|
+
type Expression,
|
|
21
29
|
type Node,
|
|
30
|
+
type ObjectLiteralExpression,
|
|
22
31
|
type SourceFile,
|
|
23
32
|
} from "@typescript/native/ast";
|
|
24
33
|
import { aliasChains } from "./alias-chain.ts";
|
|
34
|
+
import { isGlobalBlock } from "./augmentations.ts";
|
|
25
35
|
import {
|
|
26
36
|
filesNamedBy,
|
|
27
37
|
outsideChain,
|
|
@@ -30,17 +40,21 @@ import {
|
|
|
30
40
|
} from "./configuration-files.ts";
|
|
31
41
|
import { conventionEntries, type Conventions } from "./conventions.ts";
|
|
32
42
|
import { entryPoints, type EntryRule } from "./entry-points.ts";
|
|
33
|
-
import { globExpression } from "./glob.ts";
|
|
34
43
|
import type { Host } from "./host.ts";
|
|
35
44
|
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
36
45
|
import type { Manifest, ManifestEntry } from "./manifest.ts";
|
|
37
|
-
import {
|
|
46
|
+
import { ancestors, dirnamePath, relativePath, resolvePath } from "./paths.ts";
|
|
38
47
|
import { byPosition } from "./position.ts";
|
|
39
48
|
import { UNANSWERED } from "./query.ts";
|
|
40
49
|
import type { ProjectView } from "./session.ts";
|
|
41
50
|
import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
|
|
42
51
|
import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
|
|
43
52
|
import { typeQueryAliases } from "./type-query-alias.ts";
|
|
53
|
+
import { bytewise } from "./text/compare.ts";
|
|
54
|
+
import { holdsManifest } from "./fs/manifest.ts";
|
|
55
|
+
import { globExpression } from "./text/glob.ts";
|
|
56
|
+
import { spelledFiles } from "./text/named-files.ts";
|
|
57
|
+
import { commandDirectories, workflowCommands } from "./workflow-steps.ts";
|
|
44
58
|
|
|
45
59
|
/** Why one declaration is a root. */
|
|
46
60
|
export type RootKind =
|
|
@@ -67,6 +81,8 @@ export type RootKind =
|
|
|
67
81
|
| "pattern"
|
|
68
82
|
/** A declaration of a function or statement a type error skipped, and what it may have selected. */
|
|
69
83
|
| "type-error"
|
|
84
|
+
/** An ambient global interface merging into one declared outside the target, and its holders. */
|
|
85
|
+
| "global-augmentation"
|
|
70
86
|
/** A file a tool or the platform enters by its own convention, and what it exports where the tool reads it. */
|
|
71
87
|
| EntryRule;
|
|
72
88
|
|
|
@@ -87,14 +103,9 @@ export interface Root {
|
|
|
87
103
|
/** One project's roots, and every configured string that named nothing in it. */
|
|
88
104
|
export interface Roots {
|
|
89
105
|
/**
|
|
90
|
-
* Every root of this project, ordered by the site of the
|
|
91
|
-
* then by kind, then by the string that named it. A declaration
|
|
92
|
-
*
|
|
93
|
-
* reason it is live.
|
|
94
|
-
*
|
|
95
|
-
* The member says which relation the set feeds: every declaration here is live
|
|
96
|
-
* under reachability. A root of a kind that names a caller is also live under
|
|
97
|
-
* reference counting, which the sweep decides.
|
|
106
|
+
* Every root of this project, live under reachability, ordered by the site of the
|
|
107
|
+
* declaration it names, then by kind, then by the string that named it. A declaration
|
|
108
|
+
* rooted for several reasons appears once per reason, so an explanation names each.
|
|
98
109
|
*/
|
|
99
110
|
readonly liveUnderReachability: readonly Root[];
|
|
100
111
|
/**
|
|
@@ -106,11 +117,22 @@ export interface Roots {
|
|
|
106
117
|
/**
|
|
107
118
|
* Every declaration of the project, where the checker left the module or export table
|
|
108
119
|
* of an entry or a published file unanswered, since which of them that file exports is
|
|
109
|
-
* unknown; otherwise each one an unanswered published type name may name.
|
|
120
|
+
* unknown; otherwise each one an unanswered published type name may name. Beside them, each
|
|
121
|
+
* file of {@link heldScripts} and every declaration its export table names.
|
|
110
122
|
*/
|
|
111
123
|
readonly unanswered: readonly string[];
|
|
112
124
|
/** Every manifest entry naming a file to import or run that reads back to no file of this project. */
|
|
113
125
|
readonly unmappedEntries: readonly ManifestEntry[];
|
|
126
|
+
/** Every package whose command strings name, as `spelledFiles` reads them, an own file no root enters with all it exports. */
|
|
127
|
+
readonly heldScripts: readonly HeldScripts[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** One package's command strings, and the own files they name that no root enters with all they export. */
|
|
131
|
+
export interface HeldScripts {
|
|
132
|
+
/** The package's directory, absolute. */
|
|
133
|
+
readonly dir: string;
|
|
134
|
+
/** Each file, by its path below the target root. */
|
|
135
|
+
readonly files: readonly string[];
|
|
114
136
|
}
|
|
115
137
|
|
|
116
138
|
/** What decides the roots beside the declarations themselves. */
|
|
@@ -141,7 +163,7 @@ export interface RootOptions {
|
|
|
141
163
|
* inside the target whose entries are published API whatever the target's kind.
|
|
142
164
|
*/
|
|
143
165
|
readonly publishedMembers?: ReadonlySet<string> | undefined;
|
|
144
|
-
/**
|
|
166
|
+
/** The files the configurations read from a file hold, so a file another configuration compiles is no outside file. */
|
|
145
167
|
readonly runFiles?: ReadonlySet<string> | undefined;
|
|
146
168
|
}
|
|
147
169
|
|
|
@@ -182,15 +204,8 @@ const SOURCE_EXTENSION = /\.(?:d\.)?[cm]?[jt]sx?$/u;
|
|
|
182
204
|
|
|
183
205
|
/** Whether no `package.json` sits between one file and the manifest's directory, which holds it. */
|
|
184
206
|
function ofPackage(host: Host, dir: string, file: string): boolean {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
relativePath(dir, at) === undefined ||
|
|
188
|
-
host.kindOf(joinPath(at, "package.json")) === "file"
|
|
189
|
-
) {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return true;
|
|
207
|
+
const between = ancestors(dirnamePath(file), dir);
|
|
208
|
+
return between.length > 0 && !between.slice(0, -1).some((at) => holdsManifest(host, at));
|
|
194
209
|
}
|
|
195
210
|
|
|
196
211
|
/**
|
|
@@ -316,14 +331,6 @@ export function matchRef(pattern: string, ref: string): boolean {
|
|
|
316
331
|
return matchGlob(Array.from(pattern), Array.from(ref));
|
|
317
332
|
}
|
|
318
333
|
|
|
319
|
-
/** Two strings ordered bytewise, which is the order every set of a run is read in. */
|
|
320
|
-
function compare(a: string, b: string): number {
|
|
321
|
-
if (a === b) {
|
|
322
|
-
return 0;
|
|
323
|
-
}
|
|
324
|
-
return a < b ? -1 : 1;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
334
|
/** The members of one declaration, indexed by the container that holds them. */
|
|
328
335
|
function childrenOf(held: Inventory): ReadonlyMap<string, readonly InventorySymbol[]> {
|
|
329
336
|
const byParent = new Map<string, InventorySymbol[]>();
|
|
@@ -339,15 +346,60 @@ function childrenOf(held: Inventory): ReadonlyMap<string, readonly InventorySymb
|
|
|
339
346
|
}
|
|
340
347
|
|
|
341
348
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
349
|
+
* Per package, the own files its command strings name as plain text, but those a root enters
|
|
350
|
+
* with all they export: a command can use no more of such a file than the root keeps live. A
|
|
351
|
+
* manifest's are its entries' whole command strings, and the target root's package has the
|
|
352
|
+
* workflow steps, git hooks and makefile recipes beside its own, with the directories its
|
|
353
|
+
* workflow steps and lefthook commands run in, where a `.` names that directory's index file.
|
|
354
|
+
*/
|
|
355
|
+
function scriptHolds(
|
|
356
|
+
entries: readonly ManifestEntry[],
|
|
357
|
+
files: SourceFiles,
|
|
358
|
+
entered: readonly {
|
|
359
|
+
readonly file: SourceFile;
|
|
360
|
+
readonly exports: boolean;
|
|
361
|
+
readonly names?: readonly string[];
|
|
362
|
+
}[],
|
|
363
|
+
host: Host,
|
|
364
|
+
targetRoot: string,
|
|
365
|
+
): readonly HeldScripts[] {
|
|
366
|
+
const rooted = new Set(
|
|
367
|
+
entered.filter((one) => one.exports && one.names === undefined).map((one) => one.file.fileName),
|
|
368
|
+
);
|
|
369
|
+
const spelled = [...files.byName.values()]
|
|
370
|
+
.filter((file) => !rooted.has(file.fileName))
|
|
371
|
+
.map((file) => ({ file, paths: [file.fileName, ...files.writtenAs(file)] }));
|
|
372
|
+
const cwd = host.workingDirectory();
|
|
373
|
+
const commands = new Map<string, string[]>([
|
|
374
|
+
[
|
|
375
|
+
resolvePath(cwd, targetRoot),
|
|
376
|
+
[...workflowCommands(host, targetRoot), ...commandDirectories(host, targetRoot)],
|
|
377
|
+
],
|
|
378
|
+
]);
|
|
379
|
+
for (const entry of entries) {
|
|
380
|
+
if (entry.command !== undefined) {
|
|
381
|
+
const dir = resolvePath(cwd, entry.dir);
|
|
382
|
+
commands.set(dir, [...(commands.get(dir) ?? []), entry.command]);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const held: HeldScripts[] = [];
|
|
386
|
+
for (const [dir, texts] of commands) {
|
|
387
|
+
const named = spelledFiles(texts.join("\n"), dir, spelled);
|
|
388
|
+
if (named.length > 0) {
|
|
389
|
+
const paths = named.map((file) => relativePath(targetRoot, file.fileName) ?? file.fileName);
|
|
390
|
+
held.push({ dir, files: paths });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return held;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* The roots one project holds, and every configured string that named nothing in it. A
|
|
398
|
+
* file a manifest member, an entry-file pattern or a tool's configuration names is a root
|
|
399
|
+
* with each name of its export table and every link of a re-export chain to the
|
|
400
|
+
* declaration it carries; in a library, the published API alone roots what a published
|
|
401
|
+
* file exports. A file a runtime executes without reading its exports, a test file or a
|
|
402
|
+
* worker, is a root by itself.
|
|
351
403
|
*/
|
|
352
404
|
export function roots<Brand>(
|
|
353
405
|
project: ProjectView<Brand>,
|
|
@@ -412,7 +464,7 @@ export function roots<Brand>(
|
|
|
412
464
|
})),
|
|
413
465
|
];
|
|
414
466
|
const scripted = entries.flatMap(({ entry }) =>
|
|
415
|
-
entry.role === "script" ? files.named(entry.path) : [],
|
|
467
|
+
entry.role === "script" && entry.command === undefined ? files.named(entry.path) : [],
|
|
416
468
|
);
|
|
417
469
|
const readBackTo = [
|
|
418
470
|
...scripted,
|
|
@@ -446,6 +498,12 @@ export function roots<Brand>(
|
|
|
446
498
|
published.push(...entryFiles(entry));
|
|
447
499
|
}
|
|
448
500
|
}
|
|
501
|
+
if (options.publishedAPI) {
|
|
502
|
+
// A library's bundle hands what its entry exports to every program that loads it.
|
|
503
|
+
published.push(
|
|
504
|
+
...points.filter((point) => point.rule === "bundler-entry").map((point) => point.file),
|
|
505
|
+
);
|
|
506
|
+
}
|
|
449
507
|
const publishedNames = new Set(published.map((file) => file.fileName));
|
|
450
508
|
const patterned: { file: SourceFile; pattern: string }[] = [];
|
|
451
509
|
for (const pattern of options.entryFiles) {
|
|
@@ -459,6 +517,9 @@ export function roots<Brand>(
|
|
|
459
517
|
const unmapped: ManifestEntry[] = [];
|
|
460
518
|
const mapped = patterned.map(({ file }) => file);
|
|
461
519
|
for (const { entry } of entries) {
|
|
520
|
+
if (entry.command !== undefined) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
462
523
|
const named = entryFiles(entry);
|
|
463
524
|
if (named.length === 0 && entry.role !== "script") {
|
|
464
525
|
unmapped.push(entry);
|
|
@@ -483,6 +544,16 @@ export function roots<Brand>(
|
|
|
483
544
|
for (const point of points) {
|
|
484
545
|
enter(point.file, point.rule, point.source, point.exports, point.names);
|
|
485
546
|
}
|
|
547
|
+
const heldScripts = scriptHolds(
|
|
548
|
+
entries.map(({ entry }) => entry),
|
|
549
|
+
files,
|
|
550
|
+
entered,
|
|
551
|
+
options.host,
|
|
552
|
+
targetRoot,
|
|
553
|
+
);
|
|
554
|
+
const heldFiles = [...new Set(heldScripts.flatMap((one) => one.files))].flatMap(
|
|
555
|
+
(path) => files.byPath.get(path) ?? [],
|
|
556
|
+
);
|
|
486
557
|
for (const file of files.byPath.values()) {
|
|
487
558
|
for (const alias of typeQueryAliases(file)) {
|
|
488
559
|
const source = alias.type.getText().replace(/\s+/gu, " ");
|
|
@@ -502,6 +573,7 @@ export function roots<Brand>(
|
|
|
502
573
|
} = exportTables(project, held, [
|
|
503
574
|
...entered.filter((entry) => entry.exports).map((entry) => entry.file),
|
|
504
575
|
...published,
|
|
576
|
+
...heldFiles,
|
|
505
577
|
]);
|
|
506
578
|
for (const entry of entered) {
|
|
507
579
|
const id = fileIds.get(entry.file.fileName);
|
|
@@ -540,14 +612,23 @@ export function roots<Brand>(
|
|
|
540
612
|
const left = sites.get(a.id);
|
|
541
613
|
const right = sites.get(b.id);
|
|
542
614
|
const site = left === undefined || right === undefined ? 0 : byPosition(left, right);
|
|
543
|
-
return site ||
|
|
615
|
+
return site || bytewise(a.kind, b.kind) || bytewise(a.source, b.source);
|
|
544
616
|
});
|
|
545
617
|
|
|
546
618
|
return {
|
|
547
619
|
liveUnderReachability: ordered,
|
|
548
620
|
unmatched,
|
|
549
|
-
unanswered:
|
|
621
|
+
unanswered: [
|
|
622
|
+
...(answered ? api.guessed : held.symbols.map((symbol) => symbol.id)),
|
|
623
|
+
// A command runs a held file and may read any export of it, so the file, which reaches its
|
|
624
|
+
// top level, and its exports are held; a declaration neither reaches is still dead.
|
|
625
|
+
...heldFiles.flatMap((file) => {
|
|
626
|
+
const id = fileIds.get(file.fileName);
|
|
627
|
+
return [...(id === undefined ? [] : [id]), ...(exportsOf.get(file.fileName) ?? [])];
|
|
628
|
+
}),
|
|
629
|
+
],
|
|
550
630
|
unmappedEntries,
|
|
631
|
+
heldScripts,
|
|
551
632
|
};
|
|
552
633
|
}
|
|
553
634
|
|
|
@@ -657,12 +738,12 @@ function releaseTagged(
|
|
|
657
738
|
const EXPORTING_CONTAINERS: ReadonlySet<string> = new Set(["file", "namespace"]);
|
|
658
739
|
|
|
659
740
|
/**
|
|
660
|
-
* Every declaration a consumer of the library can name
|
|
661
|
-
*
|
|
662
|
-
* declaration's public and protected ones
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
*
|
|
741
|
+
* Every declaration a consumer of the library can name, a published file itself excluded:
|
|
742
|
+
* what the published files export, with a namespace's exported members and any other
|
|
743
|
+
* declaration's public and protected ones; a declaration a published one's type names; an
|
|
744
|
+
* exported declaration a release tag marks; each declaration a `declare global` block holds
|
|
745
|
+
* in a published file or one holding a published declaration; and, as `guessed`, each
|
|
746
|
+
* declaration spelled as a type name the checker leaves unanswered.
|
|
666
747
|
*/
|
|
667
748
|
function publishedAPI<Brand>(
|
|
668
749
|
project: ProjectView<Brand>,
|
|
@@ -695,48 +776,84 @@ function publishedAPI<Brand>(
|
|
|
695
776
|
reach(id);
|
|
696
777
|
}
|
|
697
778
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
779
|
+
const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
|
|
780
|
+
const blocks = held.symbols.filter((one) => isGlobalBlock(one, held, files));
|
|
781
|
+
const fileOf = (id: string): string | undefined => {
|
|
782
|
+
let at = byId.get(id);
|
|
783
|
+
while (at !== undefined && at.kind !== "file") {
|
|
784
|
+
at = byId.get(at.parent);
|
|
785
|
+
}
|
|
786
|
+
return at?.id;
|
|
787
|
+
};
|
|
788
|
+
const publishedFiles = published.flatMap(
|
|
789
|
+
(file) => held.declarations.get(nodeKey(file, file)) ?? [],
|
|
790
|
+
);
|
|
791
|
+
const globalsReached = (): boolean => {
|
|
792
|
+
const typed = new Set([...publishedFiles, ...[...reached].flatMap((id) => fileOf(id) ?? [])]);
|
|
793
|
+
const before = pending.length;
|
|
794
|
+
for (const block of blocks.filter((one) => typed.has(one.parent))) {
|
|
795
|
+
reach(block.id);
|
|
796
|
+
for (const member of children.get(block.id) ?? []) {
|
|
797
|
+
if (member.kind !== "type-parameter") {
|
|
708
798
|
reach(member.id);
|
|
709
799
|
}
|
|
710
800
|
}
|
|
711
801
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
|
|
728
|
-
if (symbol === UNANSWERED) {
|
|
729
|
-
for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
|
|
730
|
-
guessed.add(id);
|
|
731
|
-
reach(id);
|
|
802
|
+
return pending.length > before;
|
|
803
|
+
};
|
|
804
|
+
do {
|
|
805
|
+
while (pending.length > 0) {
|
|
806
|
+
const round: string[] = [];
|
|
807
|
+
for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
|
|
808
|
+
round.push(next);
|
|
809
|
+
const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
|
|
810
|
+
for (const member of children.get(next) ?? []) {
|
|
811
|
+
const reachable = exporting
|
|
812
|
+
? member.exported
|
|
813
|
+
: member.visibility !== "private" && member.visibility !== "private-name";
|
|
814
|
+
if (member.kind !== "type-parameter" && reachable) {
|
|
815
|
+
reach(member.id);
|
|
816
|
+
}
|
|
732
817
|
}
|
|
733
|
-
}
|
|
818
|
+
}
|
|
819
|
+
const named = round.flatMap((id) => {
|
|
820
|
+
const symbol = byId.get(id);
|
|
821
|
+
return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
|
|
822
|
+
});
|
|
823
|
+
const inferred = inferredTypeSymbols(
|
|
824
|
+
project,
|
|
825
|
+
round.flatMap((id) => {
|
|
826
|
+
const symbol = byId.get(id);
|
|
827
|
+
return symbol === undefined ? [] : nodes.nodesOf(symbol);
|
|
828
|
+
}),
|
|
829
|
+
);
|
|
830
|
+
for (const symbol of inferred.types) {
|
|
734
831
|
for (const target of chains.chainOf(symbol)) {
|
|
735
832
|
reach(target.id);
|
|
736
833
|
}
|
|
737
834
|
}
|
|
738
|
-
|
|
739
|
-
|
|
835
|
+
for (const symbol of inferred.members) {
|
|
836
|
+
for (const target of chains.chainOf(symbol)) {
|
|
837
|
+
const member = byId.get(target.id);
|
|
838
|
+
if (member?.visibility === "public" || member?.visibility === "protected") {
|
|
839
|
+
reach(target.id);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
|
|
844
|
+
if (symbol === UNANSWERED) {
|
|
845
|
+
for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
|
|
846
|
+
guessed.add(id);
|
|
847
|
+
reach(id);
|
|
848
|
+
}
|
|
849
|
+
} else if (symbol !== undefined) {
|
|
850
|
+
for (const target of chains.chainOf(symbol)) {
|
|
851
|
+
reach(target.id);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
} while (globalsReached());
|
|
740
857
|
return { ids: [...reached], guessed: [...guessed] };
|
|
741
858
|
}
|
|
742
859
|
|
|
@@ -783,30 +900,139 @@ function declarationNodes(
|
|
|
783
900
|
const INFERRED_DEPTH = 4;
|
|
784
901
|
|
|
785
902
|
/**
|
|
786
|
-
* The
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
|
|
790
|
-
|
|
903
|
+
* The values one function body returns, each through the type assertions around it: the
|
|
904
|
+
* value a caller receives carries the operand's type whatever type the assertion or the
|
|
905
|
+
* signature names. A nested function's returns are its own.
|
|
906
|
+
*/
|
|
907
|
+
function returnedValues(body: Node): readonly Expression[] {
|
|
908
|
+
const found: Expression[] = [];
|
|
909
|
+
const add = (value: Expression): void => {
|
|
910
|
+
for (let at: Expression | undefined = value; at !== undefined;) {
|
|
911
|
+
found.push(at);
|
|
912
|
+
at =
|
|
913
|
+
isAssertionExpression(at) || isSatisfiesExpression(at) || isParenthesizedExpression(at)
|
|
914
|
+
? at.expression
|
|
915
|
+
: undefined;
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
if (!isFunctionLikeDeclaration(body.parent) || body.parent.body !== body) {
|
|
919
|
+
return found;
|
|
920
|
+
}
|
|
921
|
+
if (isArrowFunction(body.parent) && body.kind !== SyntaxKind.Block) {
|
|
922
|
+
add(body as Expression);
|
|
923
|
+
return found;
|
|
924
|
+
}
|
|
925
|
+
const visit = (node: Node): void => {
|
|
926
|
+
if (isFunctionLikeDeclaration(node)) {
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
if (isReturnStatement(node) && node.expression !== undefined) {
|
|
930
|
+
add(node.expression);
|
|
931
|
+
}
|
|
932
|
+
node.forEachChild(visit);
|
|
933
|
+
};
|
|
934
|
+
body.forEachChild(visit);
|
|
935
|
+
return found;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/** The object literal one returned value is, written there or as the initializer of a variable `body` declares. */
|
|
939
|
+
function returnedLiteral(value: Expression, body: Node): ObjectLiteralExpression | undefined {
|
|
940
|
+
if (isObjectLiteralExpression(value)) {
|
|
941
|
+
return value;
|
|
942
|
+
}
|
|
943
|
+
if (!isIdentifier(value)) {
|
|
944
|
+
return undefined;
|
|
945
|
+
}
|
|
946
|
+
let found: ObjectLiteralExpression | undefined;
|
|
947
|
+
const visit = (node: Node): void => {
|
|
948
|
+
if (
|
|
949
|
+
isVariableDeclaration(node) &&
|
|
950
|
+
isIdentifier(node.name) &&
|
|
951
|
+
node.name.text === value.text &&
|
|
952
|
+
node.initializer !== undefined
|
|
953
|
+
) {
|
|
954
|
+
let at: Expression = node.initializer;
|
|
955
|
+
while (
|
|
956
|
+
isAssertionExpression(at) ||
|
|
957
|
+
isSatisfiesExpression(at) ||
|
|
958
|
+
isParenthesizedExpression(at)
|
|
959
|
+
) {
|
|
960
|
+
at = at.expression;
|
|
961
|
+
}
|
|
962
|
+
found = isObjectLiteralExpression(at) ? at : found;
|
|
963
|
+
}
|
|
964
|
+
if (!isFunctionLikeDeclaration(node)) {
|
|
965
|
+
node.forEachChild(visit);
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
body.forEachChild(visit);
|
|
969
|
+
return found;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* The values one function body returns, and those each method of an object literal it
|
|
974
|
+
* returns returns, to a fixed depth: a caller holding the literal calls its methods, and
|
|
975
|
+
* receives what they return.
|
|
976
|
+
*/
|
|
977
|
+
function returnedThroughLiterals(body: Node, depth = 0): readonly Expression[] {
|
|
978
|
+
const values = returnedValues(body);
|
|
979
|
+
if (depth >= INFERRED_DEPTH) {
|
|
980
|
+
return values;
|
|
981
|
+
}
|
|
982
|
+
const found = [...values];
|
|
983
|
+
for (const value of values) {
|
|
984
|
+
for (const property of returnedLiteral(value, body)?.properties ?? []) {
|
|
985
|
+
const fn = isMethodDeclaration(property)
|
|
986
|
+
? property
|
|
987
|
+
: isPropertyAssignment(property) && isFunctionLikeDeclaration(property.initializer)
|
|
988
|
+
? property.initializer
|
|
989
|
+
: undefined;
|
|
990
|
+
if (fn?.body !== undefined) {
|
|
991
|
+
found.push(...returnedThroughLiterals(fn.body, depth + 1));
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
return found;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* The declarations of the types the compiler infers where a declaration writes none, and
|
|
1000
|
+
* of the values a function returns: the type of a variable or a property with no
|
|
1001
|
+
* annotation, the result of a function or a method with no result annotation, and the type
|
|
1002
|
+
* of each value its body returns, each with the types its type arguments and its union or
|
|
1003
|
+
* intersection constituents name, to a fixed depth; and as `members` the properties of each
|
|
1004
|
+
* returned value's type. A question the checker leaves unanswered names nothing.
|
|
791
1005
|
*/
|
|
792
1006
|
function inferredTypeSymbols<Brand>(
|
|
793
1007
|
project: ProjectView<Brand>,
|
|
794
1008
|
declarations: readonly Node[],
|
|
795
|
-
): readonly TSSymbol[] {
|
|
1009
|
+
): { readonly types: readonly TSSymbol[]; readonly members: readonly TSSymbol[] } {
|
|
796
1010
|
const { queries } = project;
|
|
797
|
-
const valued = declarations.flatMap((node) =>
|
|
1011
|
+
const valued: Node[] = declarations.flatMap((node) =>
|
|
798
1012
|
(isVariableDeclaration(node) || isPropertyDeclaration(node)) &&
|
|
799
1013
|
node.type === undefined &&
|
|
800
1014
|
!isFunctionLikeDeclaration(node.initializer ?? node)
|
|
801
1015
|
? [node.name]
|
|
802
1016
|
: [],
|
|
803
1017
|
);
|
|
1018
|
+
const returned = declarations.flatMap((node) => {
|
|
1019
|
+
const signature = isFunctionLikeDeclaration(node)
|
|
1020
|
+
? node
|
|
1021
|
+
: isVariableDeclaration(node) &&
|
|
1022
|
+
node.initializer !== undefined &&
|
|
1023
|
+
isFunctionLikeDeclaration(node.initializer)
|
|
1024
|
+
? node.initializer
|
|
1025
|
+
: undefined;
|
|
1026
|
+
return signature?.body === undefined ? [] : returnedThroughLiterals(signature.body);
|
|
1027
|
+
});
|
|
804
1028
|
const types: Type[] = [];
|
|
805
|
-
|
|
1029
|
+
const returnedTypes: Type[] = [];
|
|
1030
|
+
queries.typesAt([...valued, ...returned]).forEach((type, at) => {
|
|
806
1031
|
if (type !== UNANSWERED && type !== undefined) {
|
|
807
|
-
types.push(type);
|
|
1032
|
+
(at < valued.length ? types : returnedTypes).push(type);
|
|
808
1033
|
}
|
|
809
1034
|
});
|
|
1035
|
+
types.push(...returnedTypes);
|
|
810
1036
|
for (const node of declarations) {
|
|
811
1037
|
const signature = isFunctionLikeDeclaration(node)
|
|
812
1038
|
? node
|
|
@@ -849,7 +1075,19 @@ function inferredTypeSymbols<Brand>(
|
|
|
849
1075
|
types.forEach((type) => {
|
|
850
1076
|
visit(type, 0);
|
|
851
1077
|
});
|
|
852
|
-
|
|
1078
|
+
// A returned value carries each property of its type, one an anonymous type declares or
|
|
1079
|
+
// an intersection merges included, which no type name reaches.
|
|
1080
|
+
const members = new Map<number, TSSymbol>();
|
|
1081
|
+
for (const type of returnedTypes) {
|
|
1082
|
+
const parts = queries.constituents(type);
|
|
1083
|
+
for (const one of [type, ...(parts === UNANSWERED ? [] : parts)]) {
|
|
1084
|
+
const properties = queries.propertiesOf(one);
|
|
1085
|
+
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
1086
|
+
members.set(property.id, property);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return { types: [...found.values()], members: [...members.values()] };
|
|
853
1091
|
}
|
|
854
1092
|
|
|
855
1093
|
/**
|
|
@@ -911,13 +1149,10 @@ function configured(
|
|
|
911
1149
|
}
|
|
912
1150
|
|
|
913
1151
|
/**
|
|
914
|
-
* Every configured string that named no declaration in
|
|
915
|
-
* configuration order and each once.
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
* declaration, so there is one per run: a string that names a declaration of one
|
|
919
|
-
* project has named something, and the projects it named nothing in are not a
|
|
920
|
-
* finding. A run with no project names nothing with any of them.
|
|
1152
|
+
* Every configured string that named no declaration in any project of the run, in
|
|
1153
|
+
* configuration order and each once. The finding is about the configuration, so a string
|
|
1154
|
+
* naming a declaration of one project has named something, and a run with no project
|
|
1155
|
+
* names nothing with any of them.
|
|
921
1156
|
*/
|
|
922
1157
|
export function unmatchedEverywhere(
|
|
923
1158
|
patterns: readonly string[],
|