@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/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,24 +24,37 @@ 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";
|
|
25
|
-
import
|
|
34
|
+
import { isGlobalBlock } from "./augmentations.ts";
|
|
35
|
+
import {
|
|
36
|
+
filesNamedBy,
|
|
37
|
+
outsideChain,
|
|
38
|
+
relativeSpecifiers,
|
|
39
|
+
type ParseFile,
|
|
40
|
+
} from "./configuration-files.ts";
|
|
26
41
|
import { conventionEntries, type Conventions } from "./conventions.ts";
|
|
27
42
|
import { entryPoints, type EntryRule } from "./entry-points.ts";
|
|
28
|
-
import { globExpression } from "./glob.ts";
|
|
29
43
|
import type { Host } from "./host.ts";
|
|
30
44
|
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
31
45
|
import type { Manifest, ManifestEntry } from "./manifest.ts";
|
|
32
|
-
import { relativePath, resolvePath } from "./paths.ts";
|
|
46
|
+
import { ancestors, dirnamePath, relativePath, resolvePath } from "./paths.ts";
|
|
33
47
|
import { byPosition } from "./position.ts";
|
|
34
48
|
import { UNANSWERED } from "./query.ts";
|
|
35
49
|
import type { ProjectView } from "./session.ts";
|
|
36
50
|
import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
|
|
37
|
-
import { sourceFilesOf } from "./source-files.ts";
|
|
51
|
+
import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
|
|
38
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";
|
|
39
58
|
|
|
40
59
|
/** Why one declaration is a root. */
|
|
41
60
|
export type RootKind =
|
|
@@ -62,6 +81,8 @@ export type RootKind =
|
|
|
62
81
|
| "pattern"
|
|
63
82
|
/** A declaration of a function or statement a type error skipped, and what it may have selected. */
|
|
64
83
|
| "type-error"
|
|
84
|
+
/** An ambient global interface merging into one declared outside the target, and its holders. */
|
|
85
|
+
| "global-augmentation"
|
|
65
86
|
/** A file a tool or the platform enters by its own convention, and what it exports where the tool reads it. */
|
|
66
87
|
| EntryRule;
|
|
67
88
|
|
|
@@ -82,14 +103,9 @@ export interface Root {
|
|
|
82
103
|
/** One project's roots, and every configured string that named nothing in it. */
|
|
83
104
|
export interface Roots {
|
|
84
105
|
/**
|
|
85
|
-
* Every root of this project, ordered by the site of the
|
|
86
|
-
* then by kind, then by the string that named it. A declaration
|
|
87
|
-
*
|
|
88
|
-
* reason it is live.
|
|
89
|
-
*
|
|
90
|
-
* The member says which relation the set feeds: every declaration here is live
|
|
91
|
-
* under reachability. A root of a kind that names a caller is also live under
|
|
92
|
-
* 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.
|
|
93
109
|
*/
|
|
94
110
|
readonly liveUnderReachability: readonly Root[];
|
|
95
111
|
/**
|
|
@@ -101,9 +117,22 @@ export interface Roots {
|
|
|
101
117
|
/**
|
|
102
118
|
* Every declaration of the project, where the checker left the module or export table
|
|
103
119
|
* of an entry or a published file unanswered, since which of them that file exports is
|
|
104
|
-
* 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.
|
|
105
122
|
*/
|
|
106
123
|
readonly unanswered: readonly string[];
|
|
124
|
+
/** Every manifest entry naming a file to import or run that reads back to no file of this project. */
|
|
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[];
|
|
107
136
|
}
|
|
108
137
|
|
|
109
138
|
/** What decides the roots beside the declarations themselves. */
|
|
@@ -134,6 +163,8 @@ export interface RootOptions {
|
|
|
134
163
|
* inside the target whose entries are published API whatever the target's kind.
|
|
135
164
|
*/
|
|
136
165
|
readonly publishedMembers?: ReadonlySet<string> | undefined;
|
|
166
|
+
/** The files the configurations read from a file hold, so a file another configuration compiles is no outside file. */
|
|
167
|
+
readonly runFiles?: ReadonlySet<string> | undefined;
|
|
137
168
|
}
|
|
138
169
|
|
|
139
170
|
/** Whether one declaration is written below the directory of a published workspace member. */
|
|
@@ -171,11 +202,18 @@ const OUTPUT_EXTENSION = /\.(?:d\.ts|d\.mts|d\.cts|js|mjs|cjs|jsx)$/u;
|
|
|
171
202
|
/** The extension a source file's name drops. */
|
|
172
203
|
const SOURCE_EXTENSION = /\.(?:d\.)?[cm]?[jt]sx?$/u;
|
|
173
204
|
|
|
205
|
+
/** Whether no `package.json` sits between one file and the manifest's directory, which holds it. */
|
|
206
|
+
function ofPackage(host: Host, dir: string, file: string): boolean {
|
|
207
|
+
const between = ancestors(dirnamePath(file), dir);
|
|
208
|
+
return between.length > 0 && !between.slice(0, -1).some((at) => holdsManifest(host, at));
|
|
209
|
+
}
|
|
210
|
+
|
|
174
211
|
/**
|
|
175
212
|
* The rooted files one manifest target reads back to by name: its path below the
|
|
176
213
|
* manifest's directory, past its first directory where it names one, without its output
|
|
177
|
-
* extension, is the name, and a rooted file
|
|
178
|
-
* without its source extension, ends with that name at a
|
|
214
|
+
* extension, is the name, and a rooted file of the manifest's own package is one whose
|
|
215
|
+
* path below the same directory, without its source extension, ends with that name at a
|
|
216
|
+
* directory boundary.
|
|
179
217
|
*/
|
|
180
218
|
function readBackByName(
|
|
181
219
|
host: Host,
|
|
@@ -195,13 +233,52 @@ function readBackByName(
|
|
|
195
233
|
const found = new Map<string, SourceFile>();
|
|
196
234
|
for (const file of rooted) {
|
|
197
235
|
const path = relativePath(dir, file.fileName)?.replace(SOURCE_EXTENSION, "");
|
|
198
|
-
if (
|
|
236
|
+
if (
|
|
237
|
+
path !== undefined &&
|
|
238
|
+
(path === name || path.endsWith(`/${name}`)) &&
|
|
239
|
+
ofPackage(host, dir, file.fileName)
|
|
240
|
+
) {
|
|
199
241
|
found.set(file.fileName, file);
|
|
200
242
|
}
|
|
201
243
|
}
|
|
202
244
|
return [...found.values()];
|
|
203
245
|
}
|
|
204
246
|
|
|
247
|
+
/**
|
|
248
|
+
* The program files a manifest target no program holds reaches: the target is a file
|
|
249
|
+
* read as a configuration file outside the program is, and each file of the chain it
|
|
250
|
+
* starts loads the program files its relative specifiers name, read through the emit
|
|
251
|
+
* mapping, as a command's shim loads the compiled entry it runs.
|
|
252
|
+
*/
|
|
253
|
+
function throughOutside(
|
|
254
|
+
options: RootOptions,
|
|
255
|
+
targetRoot: string,
|
|
256
|
+
files: SourceFiles,
|
|
257
|
+
entry: ManifestEntry,
|
|
258
|
+
): readonly SourceFile[] {
|
|
259
|
+
const cwd = options.host.workingDirectory();
|
|
260
|
+
const path = resolvePath(cwd, entry.path);
|
|
261
|
+
if (
|
|
262
|
+
options.parse === undefined ||
|
|
263
|
+
options.runFiles?.has(path) === true ||
|
|
264
|
+
options.host.kindOf(path) !== "file"
|
|
265
|
+
) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
const chain = outsideChain(options.host, targetRoot, files, options.parse, [
|
|
269
|
+
{ path, manifestDir: resolvePath(cwd, entry.dir) },
|
|
270
|
+
]);
|
|
271
|
+
const found = new Map<string, SourceFile>();
|
|
272
|
+
for (const { file } of chain) {
|
|
273
|
+
for (const specifier of relativeSpecifiers(file)) {
|
|
274
|
+
for (const named of filesNamedBy(files, dirnamePath(file.fileName), specifier)) {
|
|
275
|
+
found.set(named.fileName, named);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return [...found.values()];
|
|
280
|
+
}
|
|
281
|
+
|
|
205
282
|
/** Whether a configured string carries either special character. */
|
|
206
283
|
function isPattern(text: string): boolean {
|
|
207
284
|
return text.includes("*") || text.includes("?");
|
|
@@ -254,14 +331,6 @@ export function matchRef(pattern: string, ref: string): boolean {
|
|
|
254
331
|
return matchGlob(Array.from(pattern), Array.from(ref));
|
|
255
332
|
}
|
|
256
333
|
|
|
257
|
-
/** Two strings ordered bytewise, which is the order every set of a run is read in. */
|
|
258
|
-
function compare(a: string, b: string): number {
|
|
259
|
-
if (a === b) {
|
|
260
|
-
return 0;
|
|
261
|
-
}
|
|
262
|
-
return a < b ? -1 : 1;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
334
|
/** The members of one declaration, indexed by the container that holds them. */
|
|
266
335
|
function childrenOf(held: Inventory): ReadonlyMap<string, readonly InventorySymbol[]> {
|
|
267
336
|
const byParent = new Map<string, InventorySymbol[]>();
|
|
@@ -277,15 +346,60 @@ function childrenOf(held: Inventory): ReadonlyMap<string, readonly InventorySymb
|
|
|
277
346
|
}
|
|
278
347
|
|
|
279
348
|
/**
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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.
|
|
289
403
|
*/
|
|
290
404
|
export function roots<Brand>(
|
|
291
405
|
project: ProjectView<Brand>,
|
|
@@ -307,9 +421,21 @@ export function roots<Brand>(
|
|
|
307
421
|
found.set([id, kind, source].join("\u0000"), { id, kind, source });
|
|
308
422
|
};
|
|
309
423
|
|
|
310
|
-
const entered: {
|
|
311
|
-
|
|
312
|
-
|
|
424
|
+
const entered: {
|
|
425
|
+
file: SourceFile;
|
|
426
|
+
kind: RootKind;
|
|
427
|
+
source: string;
|
|
428
|
+
exports: boolean;
|
|
429
|
+
names?: readonly string[];
|
|
430
|
+
}[] = [];
|
|
431
|
+
const enter = (
|
|
432
|
+
file: SourceFile,
|
|
433
|
+
kind: RootKind,
|
|
434
|
+
source: string,
|
|
435
|
+
exports: boolean,
|
|
436
|
+
names?: readonly string[],
|
|
437
|
+
): void => {
|
|
438
|
+
entered.push({ file, kind, source, exports, ...(names === undefined ? {} : { names }) });
|
|
313
439
|
};
|
|
314
440
|
|
|
315
441
|
const points = entryPoints(
|
|
@@ -338,46 +464,96 @@ export function roots<Brand>(
|
|
|
338
464
|
})),
|
|
339
465
|
];
|
|
340
466
|
const scripted = entries.flatMap(({ entry }) =>
|
|
341
|
-
entry.role === "script" ? files.named(entry.path) : [],
|
|
467
|
+
entry.role === "script" && entry.command === undefined ? files.named(entry.path) : [],
|
|
342
468
|
);
|
|
343
469
|
const readBackTo = [
|
|
344
470
|
...scripted,
|
|
345
471
|
...points.filter((point) => READ_BACK_RULES.has(point.rule)).map((point) => point.file),
|
|
346
472
|
];
|
|
473
|
+
const entryFilesOf = new Map<ManifestEntry, readonly SourceFile[]>();
|
|
347
474
|
const entryFiles = (entry: ManifestEntry): readonly SourceFile[] => {
|
|
475
|
+
const known = entryFilesOf.get(entry);
|
|
476
|
+
if (known !== undefined) {
|
|
477
|
+
return known;
|
|
478
|
+
}
|
|
479
|
+
const found = readEntryFiles(entry);
|
|
480
|
+
entryFilesOf.set(entry, found);
|
|
481
|
+
return found;
|
|
482
|
+
};
|
|
483
|
+
const readEntryFiles = (entry: ManifestEntry): readonly SourceFile[] => {
|
|
348
484
|
const named = files.named(entry.path);
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
485
|
+
if (named.length > 0 || entry.role === "script") {
|
|
486
|
+
return named;
|
|
487
|
+
}
|
|
488
|
+
const cwd = options.host.workingDirectory();
|
|
489
|
+
const shipped = files.namedAsOutput(resolvePath(cwd, entry.dir), resolvePath(cwd, entry.path));
|
|
490
|
+
if (shipped.length > 0) {
|
|
491
|
+
return shipped;
|
|
492
|
+
}
|
|
493
|
+
const byName = readBackByName(options.host, entry, readBackTo);
|
|
494
|
+
return byName.length > 0 ? byName : throughOutside(options, targetRoot, files, entry);
|
|
352
495
|
};
|
|
353
496
|
for (const { entry, publishes } of entries) {
|
|
354
497
|
if (publishes) {
|
|
355
498
|
published.push(...entryFiles(entry));
|
|
356
499
|
}
|
|
357
500
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
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
|
+
);
|
|
364
506
|
}
|
|
507
|
+
const publishedNames = new Set(published.map((file) => file.fileName));
|
|
508
|
+
const patterned: { file: SourceFile; pattern: string }[] = [];
|
|
365
509
|
for (const pattern of options.entryFiles) {
|
|
366
510
|
const expression = globExpression(pattern);
|
|
367
511
|
for (const [path, file] of files.byPath) {
|
|
368
512
|
if (expression.test(path)) {
|
|
369
|
-
|
|
513
|
+
patterned.push({ file, pattern });
|
|
370
514
|
}
|
|
371
515
|
}
|
|
372
516
|
}
|
|
517
|
+
const unmapped: ManifestEntry[] = [];
|
|
518
|
+
const mapped = patterned.map(({ file }) => file);
|
|
519
|
+
for (const { entry } of entries) {
|
|
520
|
+
if (entry.command !== undefined) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const named = entryFiles(entry);
|
|
524
|
+
if (named.length === 0 && entry.role !== "script") {
|
|
525
|
+
unmapped.push(entry);
|
|
526
|
+
}
|
|
527
|
+
mapped.push(...named);
|
|
528
|
+
for (const file of named) {
|
|
529
|
+
const kind = ENTRY_KINDS[entry.role];
|
|
530
|
+
enter(file, kind, entry.member, !publishedNames.has(file.fileName));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const unmappedEntries = unmapped.filter(
|
|
534
|
+
(entry) => readBackByName(options.host, entry, mapped).length === 0,
|
|
535
|
+
);
|
|
536
|
+
for (const { file, pattern } of patterned) {
|
|
537
|
+
enter(file, "entry-file", pattern, true);
|
|
538
|
+
}
|
|
373
539
|
for (const { file, row } of options.conventions === undefined
|
|
374
540
|
? []
|
|
375
541
|
: conventionEntries(options.conventions, files)) {
|
|
376
542
|
enter(file, "convention", row, true);
|
|
377
543
|
}
|
|
378
544
|
for (const point of points) {
|
|
379
|
-
enter(point.file, point.rule, point.source, point.exports);
|
|
545
|
+
enter(point.file, point.rule, point.source, point.exports, point.names);
|
|
380
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
|
+
);
|
|
381
557
|
for (const file of files.byPath.values()) {
|
|
382
558
|
for (const alias of typeQueryAliases(file)) {
|
|
383
559
|
const source = alias.type.getText().replace(/\s+/gu, " ");
|
|
@@ -390,9 +566,14 @@ export function roots<Brand>(
|
|
|
390
566
|
}
|
|
391
567
|
}
|
|
392
568
|
|
|
393
|
-
const {
|
|
569
|
+
const {
|
|
570
|
+
tables: exportsOf,
|
|
571
|
+
named,
|
|
572
|
+
answered,
|
|
573
|
+
} = exportTables(project, held, [
|
|
394
574
|
...entered.filter((entry) => entry.exports).map((entry) => entry.file),
|
|
395
575
|
...published,
|
|
576
|
+
...heldFiles,
|
|
396
577
|
]);
|
|
397
578
|
for (const entry of entered) {
|
|
398
579
|
const id = fileIds.get(entry.file.fileName);
|
|
@@ -401,8 +582,13 @@ export function roots<Brand>(
|
|
|
401
582
|
}
|
|
402
583
|
add(id, entry.kind, entry.source);
|
|
403
584
|
if (entry.exports) {
|
|
404
|
-
|
|
405
|
-
|
|
585
|
+
const byName = named.get(entry.file.fileName);
|
|
586
|
+
const exported =
|
|
587
|
+
entry.names === undefined || byName === undefined
|
|
588
|
+
? (exportsOf.get(entry.file.fileName) ?? [])
|
|
589
|
+
: entry.names.flatMap((name) => byName.get(name) ?? []);
|
|
590
|
+
for (const one of exported) {
|
|
591
|
+
add(one, entry.kind, entry.source);
|
|
406
592
|
}
|
|
407
593
|
}
|
|
408
594
|
}
|
|
@@ -426,13 +612,23 @@ export function roots<Brand>(
|
|
|
426
612
|
const left = sites.get(a.id);
|
|
427
613
|
const right = sites.get(b.id);
|
|
428
614
|
const site = left === undefined || right === undefined ? 0 : byPosition(left, right);
|
|
429
|
-
return site ||
|
|
615
|
+
return site || bytewise(a.kind, b.kind) || bytewise(a.source, b.source);
|
|
430
616
|
});
|
|
431
617
|
|
|
432
618
|
return {
|
|
433
619
|
liveUnderReachability: ordered,
|
|
434
620
|
unmatched,
|
|
435
|
-
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
|
+
],
|
|
630
|
+
unmappedEntries,
|
|
631
|
+
heldScripts,
|
|
436
632
|
};
|
|
437
633
|
}
|
|
438
634
|
|
|
@@ -448,12 +644,18 @@ function exportTables<Brand>(
|
|
|
448
644
|
project: ProjectView<Brand>,
|
|
449
645
|
held: Inventory,
|
|
450
646
|
wanted: readonly SourceFile[],
|
|
451
|
-
): {
|
|
647
|
+
): {
|
|
648
|
+
readonly tables: ReadonlyMap<string, readonly string[]>;
|
|
649
|
+
/** Per file whose table the checker answered, the declarations each export name reaches. */
|
|
650
|
+
readonly named: ReadonlyMap<string, ReadonlyMap<string, readonly string[]>>;
|
|
651
|
+
readonly answered: boolean;
|
|
652
|
+
} {
|
|
452
653
|
const unique = [...new Map(wanted.map((file) => [file.fileName, file])).values()];
|
|
453
654
|
const tables = new Map<string, readonly string[]>();
|
|
655
|
+
const named = new Map<string, Map<string, string[]>>();
|
|
454
656
|
let answered = true;
|
|
455
657
|
if (unique.length === 0) {
|
|
456
|
-
return { tables, answered };
|
|
658
|
+
return { tables, named, answered };
|
|
457
659
|
}
|
|
458
660
|
const modules = project.symbolsAt(unique.map((file) => project.handle(file)));
|
|
459
661
|
const chains = aliasChains(project, held);
|
|
@@ -475,15 +677,18 @@ function exportTables<Brand>(
|
|
|
475
677
|
return;
|
|
476
678
|
}
|
|
477
679
|
const ids = new Set<string>();
|
|
680
|
+
const byName = new Map<string, string[]>();
|
|
478
681
|
for (const exported of table) {
|
|
479
682
|
for (const target of chains.chainOf(exported)) {
|
|
480
683
|
answered &&= !target.guessed;
|
|
481
684
|
ids.add(target.id);
|
|
685
|
+
byName.set(exported.name, [...(byName.get(exported.name) ?? []), target.id]);
|
|
482
686
|
}
|
|
483
687
|
}
|
|
484
688
|
tables.set(file.fileName, [...ids]);
|
|
689
|
+
named.set(file.fileName, byName);
|
|
485
690
|
});
|
|
486
|
-
return { tables, answered };
|
|
691
|
+
return { tables, named, answered };
|
|
487
692
|
}
|
|
488
693
|
|
|
489
694
|
/** A release tag of a documentation comment: `@` and its name, between white space or the ends. */
|
|
@@ -533,12 +738,12 @@ function releaseTagged(
|
|
|
533
738
|
const EXPORTING_CONTAINERS: ReadonlySet<string> = new Set(["file", "namespace"]);
|
|
534
739
|
|
|
535
740
|
/**
|
|
536
|
-
* Every declaration a consumer of the library can name
|
|
537
|
-
*
|
|
538
|
-
* declaration's public and protected ones
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
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.
|
|
542
747
|
*/
|
|
543
748
|
function publishedAPI<Brand>(
|
|
544
749
|
project: ProjectView<Brand>,
|
|
@@ -553,11 +758,13 @@ function publishedAPI<Brand>(
|
|
|
553
758
|
const nodes = declarationNodes(held, byPath);
|
|
554
759
|
const chains = aliasChains(project, held);
|
|
555
760
|
const reached = new Set<string>();
|
|
761
|
+
const entered = new Set<string>();
|
|
556
762
|
const guessed = new Set<string>();
|
|
557
763
|
const pending: string[] = [];
|
|
558
764
|
const reach = (id: string): void => {
|
|
559
|
-
|
|
560
|
-
|
|
765
|
+
const file = byId.get(id)?.kind === "file";
|
|
766
|
+
if (!(file ? entered : reached).has(id)) {
|
|
767
|
+
(file ? entered : reached).add(id);
|
|
561
768
|
pending.push(id);
|
|
562
769
|
}
|
|
563
770
|
};
|
|
@@ -566,56 +773,87 @@ function publishedAPI<Brand>(
|
|
|
566
773
|
}
|
|
567
774
|
for (const file of published) {
|
|
568
775
|
for (const id of exportsOf.get(file.fileName) ?? []) {
|
|
569
|
-
|
|
570
|
-
pending.push(id);
|
|
571
|
-
} else {
|
|
572
|
-
reach(id);
|
|
573
|
-
}
|
|
776
|
+
reach(id);
|
|
574
777
|
}
|
|
575
778
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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") {
|
|
586
798
|
reach(member.id);
|
|
587
799
|
}
|
|
588
800
|
}
|
|
589
801
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
606
|
-
project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
|
|
607
|
-
if (symbol === UNANSWERED) {
|
|
608
|
-
for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
|
|
609
|
-
guessed.add(id);
|
|
610
|
-
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
|
+
}
|
|
611
817
|
}
|
|
612
|
-
}
|
|
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) {
|
|
613
831
|
for (const target of chains.chainOf(symbol)) {
|
|
614
832
|
reach(target.id);
|
|
615
833
|
}
|
|
616
834
|
}
|
|
617
|
-
|
|
618
|
-
|
|
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());
|
|
619
857
|
return { ids: [...reached], guessed: [...guessed] };
|
|
620
858
|
}
|
|
621
859
|
|
|
@@ -625,24 +863,22 @@ function declarationNodes(
|
|
|
625
863
|
byPath: ReadonlyMap<string, SourceFile>,
|
|
626
864
|
): {
|
|
627
865
|
typeNamesOf(symbol: InventorySymbol): readonly Node[];
|
|
628
|
-
|
|
866
|
+
nodesOf(symbol: InventorySymbol): readonly Node[];
|
|
629
867
|
} {
|
|
630
|
-
const walked = new Map<string, { byId: Map<string, Node>; nodes: Set<Node> }>();
|
|
631
|
-
const nodesIn = (path: string): { byId: Map<string, Node>; nodes: Set<Node> } => {
|
|
868
|
+
const walked = new Map<string, { byId: Map<string, Node[]>; nodes: Set<Node> }>();
|
|
869
|
+
const nodesIn = (path: string): { byId: Map<string, Node[]>; nodes: Set<Node> } => {
|
|
632
870
|
const known = walked.get(path);
|
|
633
871
|
if (known !== undefined) {
|
|
634
872
|
return known;
|
|
635
873
|
}
|
|
636
|
-
const found = { byId: new Map<string, Node>(), nodes: new Set<Node>() };
|
|
874
|
+
const found = { byId: new Map<string, Node[]>(), nodes: new Set<Node>() };
|
|
637
875
|
const file = byPath.get(path);
|
|
638
876
|
if (file !== undefined) {
|
|
639
877
|
const visit = (node: Node): void => {
|
|
640
878
|
const id = held.declarations.get(nodeKey(file, node));
|
|
641
879
|
if (id !== undefined) {
|
|
642
880
|
found.nodes.add(node);
|
|
643
|
-
|
|
644
|
-
found.byId.set(id, node);
|
|
645
|
-
}
|
|
881
|
+
found.byId.set(id, [...(found.byId.get(id) ?? []), node]);
|
|
646
882
|
}
|
|
647
883
|
node.forEachChild(visit);
|
|
648
884
|
};
|
|
@@ -654,10 +890,9 @@ function declarationNodes(
|
|
|
654
890
|
return {
|
|
655
891
|
typeNamesOf: (symbol) => {
|
|
656
892
|
const { byId, nodes } = nodesIn(symbol.position.path);
|
|
657
|
-
|
|
658
|
-
return declaration === undefined ? [] : typeNamesOf(declaration, nodes);
|
|
893
|
+
return (byId.get(symbol.id) ?? []).flatMap((declaration) => typeNamesOf(declaration, nodes));
|
|
659
894
|
},
|
|
660
|
-
|
|
895
|
+
nodesOf: (symbol) => nodesIn(symbol.position.path).byId.get(symbol.id) ?? [],
|
|
661
896
|
};
|
|
662
897
|
}
|
|
663
898
|
|
|
@@ -665,30 +900,139 @@ function declarationNodes(
|
|
|
665
900
|
const INFERRED_DEPTH = 4;
|
|
666
901
|
|
|
667
902
|
/**
|
|
668
|
-
* The
|
|
669
|
-
*
|
|
670
|
-
*
|
|
671
|
-
|
|
672
|
-
|
|
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.
|
|
673
1005
|
*/
|
|
674
1006
|
function inferredTypeSymbols<Brand>(
|
|
675
1007
|
project: ProjectView<Brand>,
|
|
676
1008
|
declarations: readonly Node[],
|
|
677
|
-
): readonly TSSymbol[] {
|
|
1009
|
+
): { readonly types: readonly TSSymbol[]; readonly members: readonly TSSymbol[] } {
|
|
678
1010
|
const { queries } = project;
|
|
679
|
-
const valued = declarations.flatMap((node) =>
|
|
1011
|
+
const valued: Node[] = declarations.flatMap((node) =>
|
|
680
1012
|
(isVariableDeclaration(node) || isPropertyDeclaration(node)) &&
|
|
681
1013
|
node.type === undefined &&
|
|
682
1014
|
!isFunctionLikeDeclaration(node.initializer ?? node)
|
|
683
1015
|
? [node.name]
|
|
684
1016
|
: [],
|
|
685
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
|
+
});
|
|
686
1028
|
const types: Type[] = [];
|
|
687
|
-
|
|
1029
|
+
const returnedTypes: Type[] = [];
|
|
1030
|
+
queries.typesAt([...valued, ...returned]).forEach((type, at) => {
|
|
688
1031
|
if (type !== UNANSWERED && type !== undefined) {
|
|
689
|
-
types.push(type);
|
|
1032
|
+
(at < valued.length ? types : returnedTypes).push(type);
|
|
690
1033
|
}
|
|
691
1034
|
});
|
|
1035
|
+
types.push(...returnedTypes);
|
|
692
1036
|
for (const node of declarations) {
|
|
693
1037
|
const signature = isFunctionLikeDeclaration(node)
|
|
694
1038
|
? node
|
|
@@ -731,7 +1075,19 @@ function inferredTypeSymbols<Brand>(
|
|
|
731
1075
|
types.forEach((type) => {
|
|
732
1076
|
visit(type, 0);
|
|
733
1077
|
});
|
|
734
|
-
|
|
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()] };
|
|
735
1091
|
}
|
|
736
1092
|
|
|
737
1093
|
/**
|
|
@@ -793,13 +1149,10 @@ function configured(
|
|
|
793
1149
|
}
|
|
794
1150
|
|
|
795
1151
|
/**
|
|
796
|
-
* Every configured string that named no declaration in
|
|
797
|
-
* configuration order and each once.
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
* declaration, so there is one per run: a string that names a declaration of one
|
|
801
|
-
* project has named something, and the projects it named nothing in are not a
|
|
802
|
-
* 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.
|
|
803
1156
|
*/
|
|
804
1157
|
export function unmatchedEverywhere(
|
|
805
1158
|
patterns: readonly string[],
|