@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/dependencies.ts
CHANGED
|
@@ -8,15 +8,19 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
+
isArrayLiteralExpression,
|
|
11
12
|
isCallExpression,
|
|
12
13
|
isExportDeclaration,
|
|
14
|
+
isExpressionStatement,
|
|
13
15
|
isExternalModuleReference,
|
|
14
16
|
isIdentifier,
|
|
15
17
|
isImportDeclaration,
|
|
16
18
|
isImportEqualsDeclaration,
|
|
17
19
|
isMetaProperty,
|
|
18
20
|
isNamespaceExport,
|
|
21
|
+
isObjectLiteralExpression,
|
|
19
22
|
isPropertyAccessExpression,
|
|
23
|
+
isPropertyAssignment,
|
|
20
24
|
isShorthandPropertyAssignment,
|
|
21
25
|
isStringLiteral,
|
|
22
26
|
isNoSubstitutionTemplateLiteral,
|
|
@@ -25,29 +29,44 @@ import {
|
|
|
25
29
|
type Node,
|
|
26
30
|
type SourceFile,
|
|
27
31
|
} from "@typescript/native/ast";
|
|
32
|
+
import { automaticTypes, type AutomaticTypes } from "./automatic-types.ts";
|
|
28
33
|
import {
|
|
29
34
|
configurationFiles,
|
|
30
35
|
declaredIn as declaredBeside,
|
|
31
36
|
dependenciesNamed,
|
|
32
37
|
moduleStrings,
|
|
38
|
+
outsideChain,
|
|
39
|
+
type OutsideModule,
|
|
33
40
|
type ParseFile,
|
|
34
41
|
} from "./configuration-files.ts";
|
|
35
42
|
import type { FindingPosition } from "./finding.ts";
|
|
36
43
|
import type { Host } from "./host.ts";
|
|
37
44
|
import { nodeKey, packageScope, type Inventory } from "./inventory.ts";
|
|
38
|
-
import {
|
|
39
|
-
|
|
45
|
+
import {
|
|
46
|
+
ancestors,
|
|
47
|
+
dirnamePath,
|
|
48
|
+
isAbsolutePath,
|
|
49
|
+
joinPath,
|
|
50
|
+
relativePath,
|
|
51
|
+
resolvePath,
|
|
52
|
+
} from "./paths.ts";
|
|
40
53
|
import type { DependencySection, Module } from "./ref.ts";
|
|
41
54
|
import { UNANSWERED } from "./query.ts";
|
|
42
55
|
import type { Handle, ProjectView } from "./session.ts";
|
|
43
|
-
import { sourceFilesOf } from "./source-files.ts";
|
|
44
|
-
import {
|
|
56
|
+
import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
|
|
57
|
+
import { manifestCommands } from "./manifest.ts";
|
|
58
|
+
import { workflowCommands, workflowTokens } from "./workflow-steps.ts";
|
|
59
|
+
import { bytewise } from "./text/compare.ts";
|
|
60
|
+
import { isRecord } from "./json-document.ts";
|
|
61
|
+
import { inInstalledPackages, INSTALLED_PACKAGES, MANIFEST, readDocument } from "./fs/manifest.ts";
|
|
62
|
+
import { commandsOf, scriptTokens } from "./text/shell.ts";
|
|
45
63
|
|
|
46
|
-
const
|
|
47
|
-
const MODULES_DIR = "node_modules";
|
|
48
|
-
const MODULES_SEGMENT = `/${MODULES_DIR}/`;
|
|
64
|
+
const MODULES_SEGMENT = `/${INSTALLED_PACKAGES}/`;
|
|
49
65
|
const TYPES_SCOPE = "@types/";
|
|
50
66
|
|
|
67
|
+
/** A script token naming a JavaScript file by a path no expansion changes. */
|
|
68
|
+
const SCRIPT_FILE = /^[^$`{*?[~]+\.(?:js|mjs|cjs)$/u;
|
|
69
|
+
|
|
51
70
|
/** The manifest sections a dependency is declared in, each by the selector its reference spells. */
|
|
52
71
|
const SECTIONS: readonly (readonly [string, DependencySection])[] = [
|
|
53
72
|
["dependencies", "dependency"],
|
|
@@ -72,7 +91,7 @@ interface DeclaredDependency {
|
|
|
72
91
|
export function configurationNeeds(packages: readonly string[]): ProjectNeeds {
|
|
73
92
|
return {
|
|
74
93
|
uses: new Map([[PROJECT_OWNER, new Set(packages)]]),
|
|
75
|
-
at: new Map([[ANYWHERE, new
|
|
94
|
+
at: new Map([[ANYWHERE, new Map(packages.map((one) => [one, new Set([PROJECT_OWNER])]))]]),
|
|
76
95
|
unanswered: [],
|
|
77
96
|
};
|
|
78
97
|
}
|
|
@@ -98,10 +117,12 @@ export interface ProjectNeeds {
|
|
|
98
117
|
readonly uses: ReadonlyMap<string, ReadonlySet<string>>;
|
|
99
118
|
/**
|
|
100
119
|
* Per absolute path of the file a use is written in, the packages it names, which a
|
|
101
|
-
* dependency
|
|
102
|
-
* the
|
|
120
|
+
* dependency of the nearest manifest at or above that file declaring the name serves,
|
|
121
|
+
* each with the owners {@link uses} holds it by; an import whose bindings nothing in the
|
|
122
|
+
* file uses holds its package by none. The empty key holds the uses no one file holds,
|
|
123
|
+
* which every manifest's dependency of that name serves.
|
|
103
124
|
*/
|
|
104
|
-
readonly at: ReadonlyMap<string, ReadonlySet<string
|
|
125
|
+
readonly at: ReadonlyMap<string, ReadonlyMap<string, ReadonlySet<string>>>;
|
|
105
126
|
/**
|
|
106
127
|
* The files writing a module specifier the checker left unanswered, each of which may
|
|
107
128
|
* use any dependency a manifest at or above it declares.
|
|
@@ -134,25 +155,18 @@ export interface Dependencies {
|
|
|
134
155
|
* written inside it, sorted bytewise; a candidate holding no last use has no entry.
|
|
135
156
|
*/
|
|
136
157
|
readonly lastUses: ReadonlyMap<string, readonly string[]>;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
try {
|
|
152
|
-
return { text, value: JSON.parse(text) };
|
|
153
|
-
} catch {
|
|
154
|
-
return undefined;
|
|
155
|
-
}
|
|
158
|
+
/**
|
|
159
|
+
* Per file, by its path below the target root, the declared dependencies whose every use
|
|
160
|
+
* in the target is written in it, sorted bytewise, which deleting the file removes.
|
|
161
|
+
*/
|
|
162
|
+
readonly fileLastUses: ReadonlyMap<string, readonly string[]>;
|
|
163
|
+
/**
|
|
164
|
+
* The needed declared dependencies, by {@link declarationKey}, each with the owners
|
|
165
|
+
* {@link ProjectNeeds.uses} holds its uses by, sorted bytewise: none that a command runs,
|
|
166
|
+
* that an unanswered specifier may need, that an import none of whose bindings is used
|
|
167
|
+
* names, or that a dependency so needed names as a peer.
|
|
168
|
+
*/
|
|
169
|
+
readonly heldBy: ReadonlyMap<string, readonly string[]>;
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
/**
|
|
@@ -202,6 +216,34 @@ function typePackages(name: string, programPackages: ReadonlySet<string>): reado
|
|
|
202
216
|
return [typed, named];
|
|
203
217
|
}
|
|
204
218
|
|
|
219
|
+
/**
|
|
220
|
+
* The strings the `extends` member of one compiler configuration file holds, read as the
|
|
221
|
+
* compiler reads the file, comments and trailing commas allowed; none where it cannot be read.
|
|
222
|
+
*/
|
|
223
|
+
function extendsOf(host: Host, path: string, parse: ParseFile): readonly string[] {
|
|
224
|
+
let text: string;
|
|
225
|
+
try {
|
|
226
|
+
text = host.readFile(path);
|
|
227
|
+
} catch {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
const [statement] = parse(path, text).statements;
|
|
231
|
+
const document =
|
|
232
|
+
statement !== undefined && isExpressionStatement(statement) ? statement.expression : undefined;
|
|
233
|
+
if (document === undefined || !isObjectLiteralExpression(document)) {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
const value = document.properties
|
|
237
|
+
.filter(isPropertyAssignment)
|
|
238
|
+
.findLast(
|
|
239
|
+
(property) => isLiteralText(property.name) && property.name.text === "extends",
|
|
240
|
+
)?.initializer;
|
|
241
|
+
const entries = value !== undefined && isArrayLiteralExpression(value) ? value.elements : [value];
|
|
242
|
+
return entries.flatMap((entry) =>
|
|
243
|
+
entry !== undefined && isLiteralText(entry) ? [entry.text] : [],
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
205
247
|
/**
|
|
206
248
|
* The packages the compiler configuration of one project names: its type packages,
|
|
207
249
|
* its JSX import source, the helper library it imports, its plugins, and every
|
|
@@ -212,6 +254,7 @@ function configurationPackages(
|
|
|
212
254
|
configFile: string,
|
|
213
255
|
options: Readonly<Record<string, unknown>>,
|
|
214
256
|
programPackages: ReadonlySet<string>,
|
|
257
|
+
parse: ParseFile,
|
|
215
258
|
): Set<string> {
|
|
216
259
|
const found = new Set<string>();
|
|
217
260
|
const add = (name: unknown): void => {
|
|
@@ -248,12 +291,7 @@ function configurationPackages(
|
|
|
248
291
|
continue;
|
|
249
292
|
}
|
|
250
293
|
seen.add(file);
|
|
251
|
-
const
|
|
252
|
-
const extended = isRecord(read?.value) ? read.value["extends"] : undefined;
|
|
253
|
-
for (const entry of Array.isArray(extended) ? extended : [extended]) {
|
|
254
|
-
if (typeof entry !== "string") {
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
294
|
+
for (const entry of extendsOf(host, file, parse)) {
|
|
257
295
|
if (entry.startsWith(".") || isAbsolutePath(entry)) {
|
|
258
296
|
const local = resolvePath(dirnamePath(file), entry);
|
|
259
297
|
pending.push(local.endsWith(".json") ? local : `${local}.json`);
|
|
@@ -318,7 +356,7 @@ export function projectNeeds<Brand>(
|
|
|
318
356
|
held: Inventory,
|
|
319
357
|
host: Host,
|
|
320
358
|
targetRoot: string,
|
|
321
|
-
parse
|
|
359
|
+
parse: ParseFile,
|
|
322
360
|
): ProjectNeeds {
|
|
323
361
|
const programPackages = new Set<string>();
|
|
324
362
|
for (const name of project.program.getSourceFileNames()) {
|
|
@@ -327,18 +365,19 @@ export function projectNeeds<Brand>(
|
|
|
327
365
|
programPackages.add(named);
|
|
328
366
|
}
|
|
329
367
|
}
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
project.program.getCompilerOptions() as Readonly<Record<string, unknown>>,
|
|
334
|
-
programPackages,
|
|
335
|
-
);
|
|
368
|
+
const options = project.program.getCompilerOptions() as Readonly<Record<string, unknown>>;
|
|
369
|
+
const packages = configurationPackages(host, project.configFile, options, programPackages, parse);
|
|
370
|
+
const automatic = automaticTypes(host, project.configFile, options, parse);
|
|
336
371
|
const uses = new Map<string, Set<string>>();
|
|
337
|
-
const at = new Map<string, Set<string
|
|
372
|
+
const at = new Map<string, Map<string, Set<string>>>();
|
|
338
373
|
const use = (owners: readonly string[], named: Iterable<string>, path: string): void => {
|
|
339
374
|
for (const one of named) {
|
|
340
|
-
|
|
375
|
+
const inFile = at.get(path) ?? new Map<string, Set<string>>();
|
|
376
|
+
at.set(path, inFile);
|
|
377
|
+
const holders = inFile.get(one) ?? new Set<string>();
|
|
378
|
+
inFile.set(one, holders);
|
|
341
379
|
for (const owner of owners) {
|
|
380
|
+
holders.add(owner);
|
|
342
381
|
let set = uses.get(owner);
|
|
343
382
|
if (set === undefined) {
|
|
344
383
|
set = new Set();
|
|
@@ -355,7 +394,7 @@ export function projectNeeds<Brand>(
|
|
|
355
394
|
const inFile = (owners: readonly string[], named: Iterable<string>): void => {
|
|
356
395
|
use(owners, named, file.fileName);
|
|
357
396
|
};
|
|
358
|
-
if (fileNeeds(project, held, file, programPackages, inFile)) {
|
|
397
|
+
if (fileNeeds(project, held, file, { programPackages, automatic }, inFile)) {
|
|
359
398
|
unanswered.push(file.fileName);
|
|
360
399
|
}
|
|
361
400
|
}
|
|
@@ -370,12 +409,8 @@ export function projectNeeds<Brand>(
|
|
|
370
409
|
}
|
|
371
410
|
return names;
|
|
372
411
|
};
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
targetRoot,
|
|
376
|
-
sourceFilesOf(project, targetRoot),
|
|
377
|
-
parse,
|
|
378
|
-
);
|
|
412
|
+
const files = sourceFilesOf(project, targetRoot);
|
|
413
|
+
const configuration = configurationFiles(host, targetRoot, files, parse);
|
|
379
414
|
for (const { file, manifestDir } of configuration.modules) {
|
|
380
415
|
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
381
416
|
for (const { node, text } of moduleStrings(file)) {
|
|
@@ -384,15 +419,22 @@ export function projectNeeds<Brand>(
|
|
|
384
419
|
}
|
|
385
420
|
for (const { strings, manifestDirs } of configuration.documents) {
|
|
386
421
|
for (const manifestDir of manifestDirs) {
|
|
387
|
-
const names = declaredAt(joinPath(manifestDir,
|
|
422
|
+
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
388
423
|
for (const text of strings) {
|
|
389
424
|
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
390
425
|
}
|
|
391
426
|
}
|
|
392
427
|
}
|
|
393
|
-
|
|
428
|
+
const ranFiles: readonly OutsideModule[] = outsideChain(
|
|
429
|
+
host,
|
|
430
|
+
targetRoot,
|
|
431
|
+
files,
|
|
432
|
+
parse,
|
|
433
|
+
scriptRunFiles(host, targetRoot, files),
|
|
434
|
+
);
|
|
435
|
+
for (const { file, manifestDirs } of [...configuration.outside, ...ranFiles]) {
|
|
394
436
|
for (const manifestDir of manifestDirs) {
|
|
395
|
-
const names = declaredAt(joinPath(manifestDir,
|
|
437
|
+
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
396
438
|
for (const { text } of moduleStrings(file)) {
|
|
397
439
|
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
398
440
|
}
|
|
@@ -401,6 +443,56 @@ export function projectNeeds<Brand>(
|
|
|
401
443
|
return { uses, at, unanswered };
|
|
402
444
|
}
|
|
403
445
|
|
|
446
|
+
/**
|
|
447
|
+
* The JavaScript files no program holds that a script of a manifest at or above one of the
|
|
448
|
+
* project's files runs, each with the directory of that manifest, and those a workflow
|
|
449
|
+
* step or git hook runs, with the target root's: a token naming such a file below the
|
|
450
|
+
* target root and in no `node_modules`, read against the manifest's directory.
|
|
451
|
+
*/
|
|
452
|
+
function scriptRunFiles(
|
|
453
|
+
host: Host,
|
|
454
|
+
targetRoot: string,
|
|
455
|
+
files: SourceFiles,
|
|
456
|
+
): readonly { readonly path: string; readonly manifestDir: string }[] {
|
|
457
|
+
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
458
|
+
const dirs = new Set<string>([root]);
|
|
459
|
+
for (const name of files.byName.keys()) {
|
|
460
|
+
for (const dir of ancestors(dirnamePath(name), root)) {
|
|
461
|
+
if (dirs.has(dir)) {
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
dirs.add(dir);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const tokens = new Map<string, ReadonlySet<string>>([
|
|
468
|
+
[root, new Set(workflowTokens(host, root))],
|
|
469
|
+
]);
|
|
470
|
+
for (const dir of dirs) {
|
|
471
|
+
const read = readDocument(host, joinPath(dir, MANIFEST));
|
|
472
|
+
if (read !== undefined && isRecord(read.value)) {
|
|
473
|
+
const own = scriptTokensOf(read.value);
|
|
474
|
+
tokens.set(dir, dir === root ? new Set([...own, ...(tokens.get(root) ?? [])]) : own);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const found: { path: string; manifestDir: string }[] = [];
|
|
478
|
+
for (const [manifestDir, held] of tokens) {
|
|
479
|
+
for (const token of held) {
|
|
480
|
+
const path = SCRIPT_FILE.test(token) ? resolvePath(manifestDir, token) : undefined;
|
|
481
|
+
const below = path === undefined ? undefined : relativePath(root, path);
|
|
482
|
+
if (
|
|
483
|
+
path !== undefined &&
|
|
484
|
+
below !== undefined &&
|
|
485
|
+
!inInstalledPackages(below) &&
|
|
486
|
+
!files.byName.has(path) &&
|
|
487
|
+
host.kindOf(path) === "file"
|
|
488
|
+
) {
|
|
489
|
+
found.push({ path, manifestDir });
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return found;
|
|
494
|
+
}
|
|
495
|
+
|
|
404
496
|
/** The declarations of the inventory a use written at one node belongs to. */
|
|
405
497
|
function ownersOf(file: SourceFile, held: Inventory, node: Node): readonly string[] {
|
|
406
498
|
const parent = node.parent as Node | undefined;
|
|
@@ -425,12 +517,47 @@ function ownersOf(file: SourceFile, held: Inventory, node: Node): readonly strin
|
|
|
425
517
|
return [PROJECT_OWNER];
|
|
426
518
|
}
|
|
427
519
|
|
|
428
|
-
/**
|
|
520
|
+
/**
|
|
521
|
+
* Whether one identifier stands for a value or type of the scope it is written in, rather
|
|
522
|
+
* than naming a member, a property or a label.
|
|
523
|
+
*/
|
|
524
|
+
function isScopeName(node: Identifier): boolean {
|
|
525
|
+
const parent = node.parent as Node | undefined;
|
|
526
|
+
if (parent === undefined || isShorthandPropertyAssignment(parent)) {
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
const named = parent as {
|
|
530
|
+
readonly name?: Node;
|
|
531
|
+
readonly right?: Node;
|
|
532
|
+
readonly propertyName?: Node;
|
|
533
|
+
readonly label?: Node;
|
|
534
|
+
};
|
|
535
|
+
return (
|
|
536
|
+
named.name !== node &&
|
|
537
|
+
(parent.kind !== SyntaxKind.QualifiedName || named.right !== node) &&
|
|
538
|
+
named.propertyName !== node &&
|
|
539
|
+
named.label !== node
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** What the packages a project's configuration brings in hold, which a file's needs are read against. */
|
|
544
|
+
interface ProgramTypes {
|
|
545
|
+
/** The packages the program holds a file of. */
|
|
546
|
+
readonly programPackages: ReadonlySet<string>;
|
|
547
|
+
/** What the type packages a compiler would include automatically declare. */
|
|
548
|
+
readonly automatic: AutomaticTypes;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Records the needs of one file, and answers whether a specifier of it went unanswered. A
|
|
553
|
+
* module specifier or a name of the file's scope that resolves to nothing, and that an
|
|
554
|
+
* automatically included type package declares, uses that package.
|
|
555
|
+
*/
|
|
429
556
|
function fileNeeds<Brand>(
|
|
430
557
|
project: ProjectView<Brand>,
|
|
431
558
|
held: Inventory,
|
|
432
559
|
file: SourceFile,
|
|
433
|
-
programPackages:
|
|
560
|
+
{ programPackages, automatic }: ProgramTypes,
|
|
434
561
|
use: (owners: readonly string[], named: Iterable<string>) => void,
|
|
435
562
|
): boolean {
|
|
436
563
|
const specifiers = [...file.imports, ...file.moduleAugmentations].filter(isLiteralText);
|
|
@@ -449,17 +576,22 @@ function fileNeeds<Brand>(
|
|
|
449
576
|
}
|
|
450
577
|
const names = new Set(bindings.map(({ node }) => node.text));
|
|
451
578
|
const candidates: Identifier[] = [];
|
|
579
|
+
const globals: Identifier[] = [];
|
|
452
580
|
const visit = (node: Node): void => {
|
|
453
|
-
if (isIdentifier(node) &&
|
|
454
|
-
|
|
581
|
+
if (isIdentifier(node) && !bound.has(node)) {
|
|
582
|
+
if (names.has(node.text)) {
|
|
583
|
+
candidates.push(node);
|
|
584
|
+
} else if (automatic.globals.has(node.text) && isScopeName(node)) {
|
|
585
|
+
globals.push(node);
|
|
586
|
+
}
|
|
455
587
|
}
|
|
456
588
|
node.forEachChild(visit);
|
|
457
589
|
};
|
|
458
|
-
if (names.size > 0) {
|
|
590
|
+
if (names.size > 0 || automatic.globals.size > 0) {
|
|
459
591
|
visit(file);
|
|
460
592
|
}
|
|
461
593
|
|
|
462
|
-
const asked = [...specifiers, ...bindings.map(({ node }) => node), ...candidates];
|
|
594
|
+
const asked = [...specifiers, ...bindings.map(({ node }) => node), ...candidates, ...globals];
|
|
463
595
|
const handles: Handle<Brand>[] = asked.map((node) => project.handle(node));
|
|
464
596
|
const resolved = project.symbolsAt(handles);
|
|
465
597
|
|
|
@@ -471,6 +603,9 @@ function fileNeeds<Brand>(
|
|
|
471
603
|
named.add(byName);
|
|
472
604
|
}
|
|
473
605
|
const symbol = resolved[index];
|
|
606
|
+
if (symbol === undefined) {
|
|
607
|
+
automatic.modules.get(specifier.text)?.forEach((one) => named.add(one));
|
|
608
|
+
}
|
|
474
609
|
for (const declaration of typeof symbol === "object" ? symbol.declarations : []) {
|
|
475
610
|
const byPath = packageOfPath(declaration.path);
|
|
476
611
|
if (byPath !== undefined) {
|
|
@@ -508,7 +643,7 @@ function fileNeeds<Brand>(
|
|
|
508
643
|
let symbol = resolved[offset + index];
|
|
509
644
|
const parent = node.parent as Node | undefined;
|
|
510
645
|
if (parent !== undefined && isShorthandPropertyAssignment(parent) && parent.name === node) {
|
|
511
|
-
symbol = project.shorthandValueAt(project.handle(
|
|
646
|
+
symbol = project.shorthandValueAt(project.handle(parent));
|
|
512
647
|
}
|
|
513
648
|
const named =
|
|
514
649
|
symbol === UNANSWERED || (unresolvedNames.has(node.text) && symbol === undefined)
|
|
@@ -521,6 +656,13 @@ function fileNeeds<Brand>(
|
|
|
521
656
|
use(ownersOf(file, held, node), named);
|
|
522
657
|
}
|
|
523
658
|
});
|
|
659
|
+
const unresolvedAt = offset + candidates.length;
|
|
660
|
+
globals.forEach((node, index) => {
|
|
661
|
+
const symbol = resolved[unresolvedAt + index];
|
|
662
|
+
if (symbol === undefined || symbol === UNANSWERED) {
|
|
663
|
+
use(ownersOf(file, held, node), automatic.globals.get(node.text) ?? []);
|
|
664
|
+
}
|
|
665
|
+
});
|
|
524
666
|
|
|
525
667
|
for (const reference of file.typeReferenceDirectives) {
|
|
526
668
|
use(ownersOf(file, held, file), typePackages(reference.fileName, programPackages));
|
|
@@ -646,8 +788,8 @@ function lineAndColumn(text: string, offset: number): { line: number; column: nu
|
|
|
646
788
|
|
|
647
789
|
/** The installed copy of one dependency, read where the package manager puts it for the target. */
|
|
648
790
|
function installedOf(host: Host, root: string, name: string): InstalledDependency | undefined {
|
|
649
|
-
for (
|
|
650
|
-
const read =
|
|
791
|
+
for (const dir of ancestors(root)) {
|
|
792
|
+
const read = readDocument(host, joinPath(dir, INSTALLED_PACKAGES, name, MANIFEST));
|
|
651
793
|
if (read !== undefined && isRecord(read.value)) {
|
|
652
794
|
const bin = read.value["bin"];
|
|
653
795
|
const peers = read.value["peerDependencies"];
|
|
@@ -664,10 +806,8 @@ function installedOf(host: Host, root: string, name: string): InstalledDependenc
|
|
|
664
806
|
: [],
|
|
665
807
|
};
|
|
666
808
|
}
|
|
667
|
-
if (dirnamePath(dir) === dir) {
|
|
668
|
-
return undefined;
|
|
669
|
-
}
|
|
670
809
|
}
|
|
810
|
+
return undefined;
|
|
671
811
|
}
|
|
672
812
|
|
|
673
813
|
/** Every dependency the manifest text at `path` declares, in the order the text writes them. */
|
|
@@ -720,6 +860,88 @@ function scriptTokensOf(manifest: Record<string, unknown>): ReadonlySet<string>
|
|
|
720
860
|
return found;
|
|
721
861
|
}
|
|
722
862
|
|
|
863
|
+
/**
|
|
864
|
+
* The runtime flags whose value is a module loaded before the entry, as `--flag value` or
|
|
865
|
+
* `--flag=value`: https://nodejs.org/api/cli.html#--importmodule,
|
|
866
|
+
* https://nodejs.org/api/cli.html#--experimental-loadermodule,
|
|
867
|
+
* https://nodejs.org/api/cli.html#-r---require-module
|
|
868
|
+
*/
|
|
869
|
+
const PRELOAD_FLAGS: ReadonlySet<string> = new Set([
|
|
870
|
+
"--experimental-loader",
|
|
871
|
+
"--import",
|
|
872
|
+
"--loader",
|
|
873
|
+
"--require",
|
|
874
|
+
"-r",
|
|
875
|
+
]);
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* The programs that pass a preload flag to the runtime they start: Node itself, and the
|
|
879
|
+
* runners that forward Node's flags or read the same `--require`:
|
|
880
|
+
* https://mochajs.org/#-require-module-r-module, https://tsx.is/node-enhancement,
|
|
881
|
+
* https://typestrong.org/ts-node/docs/options#require,
|
|
882
|
+
* https://github.com/remy/nodemon#running-non-node-scripts
|
|
883
|
+
*/
|
|
884
|
+
const PRELOADING_PROGRAMS: ReadonlySet<string> = new Set([
|
|
885
|
+
"mocha",
|
|
886
|
+
"node",
|
|
887
|
+
"nodemon",
|
|
888
|
+
"ts-node",
|
|
889
|
+
"tsx",
|
|
890
|
+
]);
|
|
891
|
+
|
|
892
|
+
/** The programs that run the command after them, and an environment assignment before it. */
|
|
893
|
+
const LAUNCHERS: ReadonlySet<string> = new Set(["cross-env", "env", "npx"]);
|
|
894
|
+
const ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/u;
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* The packages `scripts` preload by a runtime flag's bare module specifier, a flag
|
|
898
|
+
* counting only in a command whose program passes it to the runtime, and for Node only
|
|
899
|
+
* before the script it runs, whose own arguments come after it.
|
|
900
|
+
*/
|
|
901
|
+
function preloadsOf(scripts: readonly string[]): ReadonlySet<string> {
|
|
902
|
+
const found = new Set<string>();
|
|
903
|
+
for (const script of scripts) {
|
|
904
|
+
for (const command of commandsOf(script)) {
|
|
905
|
+
const tokens = scriptTokens(command);
|
|
906
|
+
let at = 0;
|
|
907
|
+
while (
|
|
908
|
+
at < tokens.length &&
|
|
909
|
+
(ASSIGNMENT.test(tokens[at] ?? "") || LAUNCHERS.has(tokens[at] ?? ""))
|
|
910
|
+
) {
|
|
911
|
+
at += 1;
|
|
912
|
+
}
|
|
913
|
+
const path = tokens[at] ?? "";
|
|
914
|
+
const program = path.slice(path.lastIndexOf("/") + 1);
|
|
915
|
+
if (!PRELOADING_PROGRAMS.has(program)) {
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
for (at += 1; at < tokens.length; at += 1) {
|
|
919
|
+
const token = tokens[at] ?? "";
|
|
920
|
+
if (!token.startsWith("-")) {
|
|
921
|
+
if (program === "node") {
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
const equals = token.indexOf("=");
|
|
927
|
+
const flag = equals < 0 ? token : token.slice(0, equals);
|
|
928
|
+
if (!PRELOAD_FLAGS.has(flag)) {
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
if (equals < 0) {
|
|
932
|
+
at += 1;
|
|
933
|
+
}
|
|
934
|
+
const value = equals < 0 ? tokens[at] : token.slice(equals + 1);
|
|
935
|
+
const name = value === undefined ? undefined : packageOfSpecifier(value);
|
|
936
|
+
if (name !== undefined) {
|
|
937
|
+
found.add(name);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return found;
|
|
943
|
+
}
|
|
944
|
+
|
|
723
945
|
/** Whether a path below the target root lies in the directory `dir` below it, `""` being the root. */
|
|
724
946
|
function isInside(path: string, dir: string): boolean {
|
|
725
947
|
return dir === "" || path.startsWith(`${dir}/`);
|
|
@@ -731,6 +953,22 @@ function manifestDir(dependency: DeclaredDependency): string {
|
|
|
731
953
|
return dir === "." ? "" : dir;
|
|
732
954
|
}
|
|
733
955
|
|
|
956
|
+
/**
|
|
957
|
+
* The declarations of one name a use written at `below` needs: those of the nearest
|
|
958
|
+
* manifest at or above it that declares the name, or every one for a use no file holds.
|
|
959
|
+
*/
|
|
960
|
+
function nearestDeclaring(
|
|
961
|
+
declarations: readonly DeclaredDependency[],
|
|
962
|
+
below: string | undefined,
|
|
963
|
+
): readonly DeclaredDependency[] {
|
|
964
|
+
if (below === undefined) {
|
|
965
|
+
return declarations;
|
|
966
|
+
}
|
|
967
|
+
const enclosing = declarations.filter((dependency) => isInside(below, manifestDir(dependency)));
|
|
968
|
+
const nearest = Math.max(-1, ...enclosing.map((dependency) => manifestDir(dependency).length));
|
|
969
|
+
return enclosing.filter((dependency) => manifestDir(dependency).length === nearest);
|
|
970
|
+
}
|
|
971
|
+
|
|
734
972
|
/**
|
|
735
973
|
* Adds to `needed` each declared dependency that the installed manifest of a needed or
|
|
736
974
|
* run declared dependency names as a peer, until no further one joins: the declaration of
|
|
@@ -799,22 +1037,29 @@ export function dependenciesOf(
|
|
|
799
1037
|
}
|
|
800
1038
|
}
|
|
801
1039
|
const workflow = new Set(workflowTokens(host, targetRoot));
|
|
1040
|
+
const workflowPreloads = preloadsOf(workflowCommands(host, targetRoot));
|
|
802
1041
|
const declared: DeclaredDependency[] = [];
|
|
803
1042
|
const ran = new Set<string>();
|
|
804
1043
|
const peers = new Map<string, readonly string[]>();
|
|
805
1044
|
const uninstalledHere = new Set(nested);
|
|
806
1045
|
for (const [dir, path] of manifests) {
|
|
807
|
-
const read =
|
|
1046
|
+
const read = readDocument(host, joinPath(dir, MANIFEST));
|
|
808
1047
|
if (read === undefined || !isRecord(read.value)) {
|
|
809
1048
|
continue;
|
|
810
1049
|
}
|
|
811
1050
|
const tokens = scriptTokensOf(read.value);
|
|
1051
|
+
const preloads = preloadsOf(
|
|
1052
|
+
manifestCommands(host, dir, read.value).map(({ command }) => command),
|
|
1053
|
+
);
|
|
812
1054
|
for (const dependency of declaredIn(read.text, read.value, path, scopeOf(path))) {
|
|
813
1055
|
const found = installedOf(host, dir, dependency.name);
|
|
814
1056
|
if (found === undefined && uninstalledHere.has(dir)) {
|
|
815
1057
|
continue;
|
|
816
1058
|
}
|
|
817
1059
|
declared.push(dependency);
|
|
1060
|
+
if (preloads.has(dependency.name) || workflowPreloads.has(dependency.name)) {
|
|
1061
|
+
ran.add(declarationKey(dependency));
|
|
1062
|
+
}
|
|
818
1063
|
if (found === undefined) {
|
|
819
1064
|
continue;
|
|
820
1065
|
}
|
|
@@ -829,6 +1074,8 @@ export function dependenciesOf(
|
|
|
829
1074
|
// A declaration several projects compile is one user, and a use any project makes is
|
|
830
1075
|
// a use, so the uses are merged by declaration before anything is counted.
|
|
831
1076
|
const needed = new Set<string>();
|
|
1077
|
+
const live = new Set<string>();
|
|
1078
|
+
const holders = new Map<string, Set<string>>();
|
|
832
1079
|
const byName = new Map<string, DeclaredDependency[]>();
|
|
833
1080
|
for (const dependency of declared) {
|
|
834
1081
|
byName.set(dependency.name, [...(byName.get(dependency.name) ?? []), dependency]);
|
|
@@ -841,7 +1088,10 @@ export function dependenciesOf(
|
|
|
841
1088
|
const unknownDeclared = declared.filter((dependency) =>
|
|
842
1089
|
unanswered.some((file) => isInside(file, manifestDir(dependency))),
|
|
843
1090
|
);
|
|
844
|
-
|
|
1091
|
+
for (const dependency of unknownDeclared) {
|
|
1092
|
+
needed.add(declarationKey(dependency));
|
|
1093
|
+
live.add(declarationKey(dependency));
|
|
1094
|
+
}
|
|
845
1095
|
const unknown = unknownDeclared.map((dependency) => dependency.name);
|
|
846
1096
|
const held = unknown.length === 0 ? [] : [[PROJECT_OWNER, new Set(unknown)] as const];
|
|
847
1097
|
for (const [id, used] of [...project.uses, ...held]) {
|
|
@@ -851,16 +1101,31 @@ export function dependenciesOf(
|
|
|
851
1101
|
}
|
|
852
1102
|
for (const [path, named] of project.at) {
|
|
853
1103
|
const below = path === ANYWHERE ? undefined : relativePath(root, path);
|
|
854
|
-
for (const one of named) {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1104
|
+
for (const [one, owners] of named) {
|
|
1105
|
+
// An import none of whose bindings is used is no evidence about the code using it.
|
|
1106
|
+
const unheld = owners.size === 0;
|
|
1107
|
+
for (const dependency of nearestDeclaring(byName.get(one) ?? [], below)) {
|
|
1108
|
+
const key = declarationKey(dependency);
|
|
1109
|
+
needed.add(key);
|
|
1110
|
+
if (unheld) {
|
|
1111
|
+
live.add(key);
|
|
1112
|
+
} else {
|
|
1113
|
+
const held = holders.get(key) ?? new Set<string>();
|
|
1114
|
+
owners.forEach((owner) => held.add(owner));
|
|
1115
|
+
holders.set(key, held);
|
|
858
1116
|
}
|
|
859
1117
|
}
|
|
860
1118
|
}
|
|
861
1119
|
}
|
|
862
1120
|
}
|
|
863
1121
|
addNeededPeers(declared, peers, needed, ran);
|
|
1122
|
+
addNeededPeers(declared, peers, live, ran);
|
|
1123
|
+
const heldBy = new Map<string, readonly string[]>();
|
|
1124
|
+
for (const [key, held] of holders) {
|
|
1125
|
+
if (!live.has(key) && !ran.has(key)) {
|
|
1126
|
+
heldBy.set(key, [...held].sort(bytewise));
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
864
1129
|
const users = new Map<string, number>();
|
|
865
1130
|
for (const used of uses.values()) {
|
|
866
1131
|
used.forEach((one) => users.set(one, (users.get(one) ?? 0) + 1));
|
|
@@ -870,11 +1135,24 @@ export function dependenciesOf(
|
|
|
870
1135
|
for (const id of candidates) {
|
|
871
1136
|
const sole = [...(uses.get(id) ?? [])].filter((one) => users.get(one) === 1 && names.has(one));
|
|
872
1137
|
if (sole.length > 0) {
|
|
873
|
-
lastUses.set(
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1138
|
+
lastUses.set(id, sole.sort(bytewise));
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
const owners = new Map<string, Set<string>>();
|
|
1142
|
+
for (const [owner, used] of uses) {
|
|
1143
|
+
for (const one of used) {
|
|
1144
|
+
if (names.has(one)) {
|
|
1145
|
+
owners.set(one, (owners.get(one) ?? new Set<string>()).add(owner));
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
const fileLastUses = new Map<string, string[]>();
|
|
1150
|
+
for (const [one, held] of [...owners].sort(([a], [b]) => bytewise(a, b))) {
|
|
1151
|
+
const paths = new Set([...held].map((owner) => owner.replace(/:\d+:\d+$/u, "")));
|
|
1152
|
+
const [path] = paths;
|
|
1153
|
+
if (paths.size === 1 && path !== undefined && path !== PROJECT_OWNER) {
|
|
1154
|
+
fileLastUses.set(path, [...(fileLastUses.get(path) ?? []), one]);
|
|
877
1155
|
}
|
|
878
1156
|
}
|
|
879
|
-
return { declared, needed, ran, unbuilt, lastUses };
|
|
1157
|
+
return { declared, needed, ran, unbuilt, lastUses, fileLastUses, heldBy };
|
|
880
1158
|
}
|