@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/dist/src/dependencies.js
CHANGED
|
@@ -6,18 +6,23 @@
|
|
|
6
6
|
* file a tool loads needs each dependency its strings spell. A package one dependency pulls in for
|
|
7
7
|
* itself is that dependency's need, not the target's.
|
|
8
8
|
*/
|
|
9
|
-
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isMetaProperty, isNamespaceExport, isPropertyAccessExpression, isShorthandPropertyAssignment, isStringLiteral, isNoSubstitutionTemplateLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
10
|
-
import {
|
|
9
|
+
import { isArrayLiteralExpression, isCallExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isMetaProperty, isNamespaceExport, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isStringLiteral, isNoSubstitutionTemplateLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
10
|
+
import { automaticTypes } from "./automatic-types.js";
|
|
11
|
+
import { configurationFiles, declaredIn as declaredBeside, dependenciesNamed, moduleStrings, outsideChain, } from "./configuration-files.js";
|
|
11
12
|
import { nodeKey, packageScope } from "./inventory.js";
|
|
12
|
-
import {
|
|
13
|
-
import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
13
|
+
import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, } from "./paths.js";
|
|
14
14
|
import { UNANSWERED } from "./query.js";
|
|
15
15
|
import { sourceFilesOf } from "./source-files.js";
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
import { manifestCommands } from "./manifest.js";
|
|
17
|
+
import { workflowCommands, workflowTokens } from "./workflow-steps.js";
|
|
18
|
+
import { bytewise } from "./text/compare.js";
|
|
19
|
+
import { isRecord } from "./json-document.js";
|
|
20
|
+
import { inInstalledPackages, INSTALLED_PACKAGES, MANIFEST, readDocument } from "./fs/manifest.js";
|
|
21
|
+
import { commandsOf, scriptTokens } from "./text/shell.js";
|
|
22
|
+
const MODULES_SEGMENT = `/${INSTALLED_PACKAGES}/`;
|
|
20
23
|
const TYPES_SCOPE = "@types/";
|
|
24
|
+
/** A script token naming a JavaScript file by a path no expansion changes. */
|
|
25
|
+
const SCRIPT_FILE = /^[^$`{*?[~]+\.(?:js|mjs|cjs)$/u;
|
|
21
26
|
/** The manifest sections a dependency is declared in, each by the selector its reference spells. */
|
|
22
27
|
const SECTIONS = [
|
|
23
28
|
["dependencies", "dependency"],
|
|
@@ -30,31 +35,12 @@ const PROJECT_OWNER = "";
|
|
|
30
35
|
export function configurationNeeds(packages) {
|
|
31
36
|
return {
|
|
32
37
|
uses: new Map([[PROJECT_OWNER, new Set(packages)]]),
|
|
33
|
-
at: new Map([[ANYWHERE, new
|
|
38
|
+
at: new Map([[ANYWHERE, new Map(packages.map((one) => [one, new Set([PROJECT_OWNER])]))]]),
|
|
34
39
|
unanswered: [],
|
|
35
40
|
};
|
|
36
41
|
}
|
|
37
42
|
/** The key of {@link ProjectNeeds.at} for a use no file of the target governs. */
|
|
38
43
|
const ANYWHERE = "";
|
|
39
|
-
function isRecord(value) {
|
|
40
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
|
-
}
|
|
42
|
-
/** One JSON document's decoded value, or undefined where it cannot be read or parsed. */
|
|
43
|
-
function readJSON(host, path) {
|
|
44
|
-
let text;
|
|
45
|
-
try {
|
|
46
|
-
text = host.readFile(path);
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
try {
|
|
52
|
-
return { text, value: JSON.parse(text) };
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
44
|
/**
|
|
59
45
|
* The package a bare module specifier names: its first segment, or its first two
|
|
60
46
|
* where it starts with a scope. A relative, absolute or scheme-prefixed specifier and
|
|
@@ -99,12 +85,35 @@ function typePackages(name, programPackages) {
|
|
|
99
85
|
}
|
|
100
86
|
return [typed, named];
|
|
101
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The strings the `extends` member of one compiler configuration file holds, read as the
|
|
90
|
+
* compiler reads the file, comments and trailing commas allowed; none where it cannot be read.
|
|
91
|
+
*/
|
|
92
|
+
function extendsOf(host, path, parse) {
|
|
93
|
+
let text;
|
|
94
|
+
try {
|
|
95
|
+
text = host.readFile(path);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const [statement] = parse(path, text).statements;
|
|
101
|
+
const document = statement !== undefined && isExpressionStatement(statement) ? statement.expression : undefined;
|
|
102
|
+
if (document === undefined || !isObjectLiteralExpression(document)) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
const value = document.properties
|
|
106
|
+
.filter(isPropertyAssignment)
|
|
107
|
+
.findLast((property) => isLiteralText(property.name) && property.name.text === "extends")?.initializer;
|
|
108
|
+
const entries = value !== undefined && isArrayLiteralExpression(value) ? value.elements : [value];
|
|
109
|
+
return entries.flatMap((entry) => entry !== undefined && isLiteralText(entry) ? [entry.text] : []);
|
|
110
|
+
}
|
|
102
111
|
/**
|
|
103
112
|
* The packages the compiler configuration of one project names: its type packages,
|
|
104
113
|
* its JSX import source, the helper library it imports, its plugins, and every
|
|
105
114
|
* package a configuration file it extends through is read from.
|
|
106
115
|
*/
|
|
107
|
-
function configurationPackages(host, configFile, options, programPackages) {
|
|
116
|
+
function configurationPackages(host, configFile, options, programPackages, parse) {
|
|
108
117
|
const found = new Set();
|
|
109
118
|
const add = (name) => {
|
|
110
119
|
const named = typeof name === "string" ? packageOfSpecifier(name) : undefined;
|
|
@@ -139,12 +148,7 @@ function configurationPackages(host, configFile, options, programPackages) {
|
|
|
139
148
|
continue;
|
|
140
149
|
}
|
|
141
150
|
seen.add(file);
|
|
142
|
-
const
|
|
143
|
-
const extended = isRecord(read?.value) ? read.value["extends"] : undefined;
|
|
144
|
-
for (const entry of Array.isArray(extended) ? extended : [extended]) {
|
|
145
|
-
if (typeof entry !== "string") {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
151
|
+
for (const entry of extendsOf(host, file, parse)) {
|
|
148
152
|
if (entry.startsWith(".") || isAbsolutePath(entry)) {
|
|
149
153
|
const local = resolvePath(dirnamePath(file), entry);
|
|
150
154
|
pending.push(local.endsWith(".json") ? local : `${local}.json`);
|
|
@@ -207,13 +211,19 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
207
211
|
programPackages.add(named);
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
|
-
const
|
|
214
|
+
const options = project.program.getCompilerOptions();
|
|
215
|
+
const packages = configurationPackages(host, project.configFile, options, programPackages, parse);
|
|
216
|
+
const automatic = automaticTypes(host, project.configFile, options, parse);
|
|
211
217
|
const uses = new Map();
|
|
212
218
|
const at = new Map();
|
|
213
219
|
const use = (owners, named, path) => {
|
|
214
220
|
for (const one of named) {
|
|
215
|
-
|
|
221
|
+
const inFile = at.get(path) ?? new Map();
|
|
222
|
+
at.set(path, inFile);
|
|
223
|
+
const holders = inFile.get(one) ?? new Set();
|
|
224
|
+
inFile.set(one, holders);
|
|
216
225
|
for (const owner of owners) {
|
|
226
|
+
holders.add(owner);
|
|
217
227
|
let set = uses.get(owner);
|
|
218
228
|
if (set === undefined) {
|
|
219
229
|
set = new Set();
|
|
@@ -229,7 +239,7 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
229
239
|
const inFile = (owners, named) => {
|
|
230
240
|
use(owners, named, file.fileName);
|
|
231
241
|
};
|
|
232
|
-
if (fileNeeds(project, held, file, programPackages, inFile)) {
|
|
242
|
+
if (fileNeeds(project, held, file, { programPackages, automatic }, inFile)) {
|
|
233
243
|
unanswered.push(file.fileName);
|
|
234
244
|
}
|
|
235
245
|
}
|
|
@@ -243,7 +253,8 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
243
253
|
}
|
|
244
254
|
return names;
|
|
245
255
|
};
|
|
246
|
-
const
|
|
256
|
+
const files = sourceFilesOf(project, targetRoot);
|
|
257
|
+
const configuration = configurationFiles(host, targetRoot, files, parse);
|
|
247
258
|
for (const { file, manifestDir } of configuration.modules) {
|
|
248
259
|
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
249
260
|
for (const { node, text } of moduleStrings(file)) {
|
|
@@ -252,15 +263,16 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
252
263
|
}
|
|
253
264
|
for (const { strings, manifestDirs } of configuration.documents) {
|
|
254
265
|
for (const manifestDir of manifestDirs) {
|
|
255
|
-
const names = declaredAt(joinPath(manifestDir,
|
|
266
|
+
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
256
267
|
for (const text of strings) {
|
|
257
268
|
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
271
|
}
|
|
261
|
-
|
|
272
|
+
const ranFiles = outsideChain(host, targetRoot, files, parse, scriptRunFiles(host, targetRoot, files));
|
|
273
|
+
for (const { file, manifestDirs } of [...configuration.outside, ...ranFiles]) {
|
|
262
274
|
for (const manifestDir of manifestDirs) {
|
|
263
|
-
const names = declaredAt(joinPath(manifestDir,
|
|
275
|
+
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
264
276
|
for (const { text } of moduleStrings(file)) {
|
|
265
277
|
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
266
278
|
}
|
|
@@ -268,6 +280,49 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
268
280
|
}
|
|
269
281
|
return { uses, at, unanswered };
|
|
270
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* The JavaScript files no program holds that a script of a manifest at or above one of the
|
|
285
|
+
* project's files runs, each with the directory of that manifest, and those a workflow
|
|
286
|
+
* step or git hook runs, with the target root's: a token naming such a file below the
|
|
287
|
+
* target root and in no `node_modules`, read against the manifest's directory.
|
|
288
|
+
*/
|
|
289
|
+
function scriptRunFiles(host, targetRoot, files) {
|
|
290
|
+
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
291
|
+
const dirs = new Set([root]);
|
|
292
|
+
for (const name of files.byName.keys()) {
|
|
293
|
+
for (const dir of ancestors(dirnamePath(name), root)) {
|
|
294
|
+
if (dirs.has(dir)) {
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
dirs.add(dir);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const tokens = new Map([
|
|
301
|
+
[root, new Set(workflowTokens(host, root))],
|
|
302
|
+
]);
|
|
303
|
+
for (const dir of dirs) {
|
|
304
|
+
const read = readDocument(host, joinPath(dir, MANIFEST));
|
|
305
|
+
if (read !== undefined && isRecord(read.value)) {
|
|
306
|
+
const own = scriptTokensOf(read.value);
|
|
307
|
+
tokens.set(dir, dir === root ? new Set([...own, ...(tokens.get(root) ?? [])]) : own);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const found = [];
|
|
311
|
+
for (const [manifestDir, held] of tokens) {
|
|
312
|
+
for (const token of held) {
|
|
313
|
+
const path = SCRIPT_FILE.test(token) ? resolvePath(manifestDir, token) : undefined;
|
|
314
|
+
const below = path === undefined ? undefined : relativePath(root, path);
|
|
315
|
+
if (path !== undefined &&
|
|
316
|
+
below !== undefined &&
|
|
317
|
+
!inInstalledPackages(below) &&
|
|
318
|
+
!files.byName.has(path) &&
|
|
319
|
+
host.kindOf(path) === "file") {
|
|
320
|
+
found.push({ path, manifestDir });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return found;
|
|
325
|
+
}
|
|
271
326
|
/** The declarations of the inventory a use written at one node belongs to. */
|
|
272
327
|
function ownersOf(file, held, node) {
|
|
273
328
|
const parent = node.parent;
|
|
@@ -291,8 +346,27 @@ function ownersOf(file, held, node) {
|
|
|
291
346
|
}
|
|
292
347
|
return [PROJECT_OWNER];
|
|
293
348
|
}
|
|
294
|
-
/**
|
|
295
|
-
|
|
349
|
+
/**
|
|
350
|
+
* Whether one identifier stands for a value or type of the scope it is written in, rather
|
|
351
|
+
* than naming a member, a property or a label.
|
|
352
|
+
*/
|
|
353
|
+
function isScopeName(node) {
|
|
354
|
+
const parent = node.parent;
|
|
355
|
+
if (parent === undefined || isShorthandPropertyAssignment(parent)) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
const named = parent;
|
|
359
|
+
return (named.name !== node &&
|
|
360
|
+
(parent.kind !== SyntaxKind.QualifiedName || named.right !== node) &&
|
|
361
|
+
named.propertyName !== node &&
|
|
362
|
+
named.label !== node);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Records the needs of one file, and answers whether a specifier of it went unanswered. A
|
|
366
|
+
* module specifier or a name of the file's scope that resolves to nothing, and that an
|
|
367
|
+
* automatically included type package declares, uses that package.
|
|
368
|
+
*/
|
|
369
|
+
function fileNeeds(project, held, file, { programPackages, automatic }, use) {
|
|
296
370
|
const specifiers = [...file.imports, ...file.moduleAugmentations].filter(isLiteralText);
|
|
297
371
|
const bindings = [];
|
|
298
372
|
const bound = new Set();
|
|
@@ -309,16 +383,22 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
309
383
|
}
|
|
310
384
|
const names = new Set(bindings.map(({ node }) => node.text));
|
|
311
385
|
const candidates = [];
|
|
386
|
+
const globals = [];
|
|
312
387
|
const visit = (node) => {
|
|
313
|
-
if (isIdentifier(node) &&
|
|
314
|
-
|
|
388
|
+
if (isIdentifier(node) && !bound.has(node)) {
|
|
389
|
+
if (names.has(node.text)) {
|
|
390
|
+
candidates.push(node);
|
|
391
|
+
}
|
|
392
|
+
else if (automatic.globals.has(node.text) && isScopeName(node)) {
|
|
393
|
+
globals.push(node);
|
|
394
|
+
}
|
|
315
395
|
}
|
|
316
396
|
node.forEachChild(visit);
|
|
317
397
|
};
|
|
318
|
-
if (names.size > 0) {
|
|
398
|
+
if (names.size > 0 || automatic.globals.size > 0) {
|
|
319
399
|
visit(file);
|
|
320
400
|
}
|
|
321
|
-
const asked = [...specifiers, ...bindings.map(({ node }) => node), ...candidates];
|
|
401
|
+
const asked = [...specifiers, ...bindings.map(({ node }) => node), ...candidates, ...globals];
|
|
322
402
|
const handles = asked.map((node) => project.handle(node));
|
|
323
403
|
const resolved = project.symbolsAt(handles);
|
|
324
404
|
const packagesOf = new Map();
|
|
@@ -329,6 +409,9 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
329
409
|
named.add(byName);
|
|
330
410
|
}
|
|
331
411
|
const symbol = resolved[index];
|
|
412
|
+
if (symbol === undefined) {
|
|
413
|
+
automatic.modules.get(specifier.text)?.forEach((one) => named.add(one));
|
|
414
|
+
}
|
|
332
415
|
for (const declaration of typeof symbol === "object" ? symbol.declarations : []) {
|
|
333
416
|
const byPath = packageOfPath(declaration.path);
|
|
334
417
|
if (byPath !== undefined) {
|
|
@@ -364,7 +447,7 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
364
447
|
let symbol = resolved[offset + index];
|
|
365
448
|
const parent = node.parent;
|
|
366
449
|
if (parent !== undefined && isShorthandPropertyAssignment(parent) && parent.name === node) {
|
|
367
|
-
symbol = project.shorthandValueAt(project.handle(
|
|
450
|
+
symbol = project.shorthandValueAt(project.handle(parent));
|
|
368
451
|
}
|
|
369
452
|
const named = symbol === UNANSWERED || (unresolvedNames.has(node.text) && symbol === undefined)
|
|
370
453
|
? byName.get(node.text)
|
|
@@ -376,6 +459,13 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
376
459
|
use(ownersOf(file, held, node), named);
|
|
377
460
|
}
|
|
378
461
|
});
|
|
462
|
+
const unresolvedAt = offset + candidates.length;
|
|
463
|
+
globals.forEach((node, index) => {
|
|
464
|
+
const symbol = resolved[unresolvedAt + index];
|
|
465
|
+
if (symbol === undefined || symbol === UNANSWERED) {
|
|
466
|
+
use(ownersOf(file, held, node), automatic.globals.get(node.text) ?? []);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
379
469
|
for (const reference of file.typeReferenceDirectives) {
|
|
380
470
|
use(ownersOf(file, held, file), typePackages(reference.fileName, programPackages));
|
|
381
471
|
}
|
|
@@ -494,8 +584,8 @@ function lineAndColumn(text, offset) {
|
|
|
494
584
|
}
|
|
495
585
|
/** The installed copy of one dependency, read where the package manager puts it for the target. */
|
|
496
586
|
function installedOf(host, root, name) {
|
|
497
|
-
for (
|
|
498
|
-
const read =
|
|
587
|
+
for (const dir of ancestors(root)) {
|
|
588
|
+
const read = readDocument(host, joinPath(dir, INSTALLED_PACKAGES, name, MANIFEST));
|
|
499
589
|
if (read !== undefined && isRecord(read.value)) {
|
|
500
590
|
const bin = read.value["bin"];
|
|
501
591
|
const peers = read.value["peerDependencies"];
|
|
@@ -511,10 +601,8 @@ function installedOf(host, root, name) {
|
|
|
511
601
|
: [],
|
|
512
602
|
};
|
|
513
603
|
}
|
|
514
|
-
if (dirnamePath(dir) === dir) {
|
|
515
|
-
return undefined;
|
|
516
|
-
}
|
|
517
604
|
}
|
|
605
|
+
return undefined;
|
|
518
606
|
}
|
|
519
607
|
/** Every dependency the manifest text at `path` declares, in the order the text writes them. */
|
|
520
608
|
function declaredIn(text, manifest, path, scope) {
|
|
@@ -558,6 +646,82 @@ function scriptTokensOf(manifest) {
|
|
|
558
646
|
}
|
|
559
647
|
return found;
|
|
560
648
|
}
|
|
649
|
+
/**
|
|
650
|
+
* The runtime flags whose value is a module loaded before the entry, as `--flag value` or
|
|
651
|
+
* `--flag=value`: https://nodejs.org/api/cli.html#--importmodule,
|
|
652
|
+
* https://nodejs.org/api/cli.html#--experimental-loadermodule,
|
|
653
|
+
* https://nodejs.org/api/cli.html#-r---require-module
|
|
654
|
+
*/
|
|
655
|
+
const PRELOAD_FLAGS = new Set([
|
|
656
|
+
"--experimental-loader",
|
|
657
|
+
"--import",
|
|
658
|
+
"--loader",
|
|
659
|
+
"--require",
|
|
660
|
+
"-r",
|
|
661
|
+
]);
|
|
662
|
+
/**
|
|
663
|
+
* The programs that pass a preload flag to the runtime they start: Node itself, and the
|
|
664
|
+
* runners that forward Node's flags or read the same `--require`:
|
|
665
|
+
* https://mochajs.org/#-require-module-r-module, https://tsx.is/node-enhancement,
|
|
666
|
+
* https://typestrong.org/ts-node/docs/options#require,
|
|
667
|
+
* https://github.com/remy/nodemon#running-non-node-scripts
|
|
668
|
+
*/
|
|
669
|
+
const PRELOADING_PROGRAMS = new Set([
|
|
670
|
+
"mocha",
|
|
671
|
+
"node",
|
|
672
|
+
"nodemon",
|
|
673
|
+
"ts-node",
|
|
674
|
+
"tsx",
|
|
675
|
+
]);
|
|
676
|
+
/** The programs that run the command after them, and an environment assignment before it. */
|
|
677
|
+
const LAUNCHERS = new Set(["cross-env", "env", "npx"]);
|
|
678
|
+
const ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/u;
|
|
679
|
+
/**
|
|
680
|
+
* The packages `scripts` preload by a runtime flag's bare module specifier, a flag
|
|
681
|
+
* counting only in a command whose program passes it to the runtime, and for Node only
|
|
682
|
+
* before the script it runs, whose own arguments come after it.
|
|
683
|
+
*/
|
|
684
|
+
function preloadsOf(scripts) {
|
|
685
|
+
const found = new Set();
|
|
686
|
+
for (const script of scripts) {
|
|
687
|
+
for (const command of commandsOf(script)) {
|
|
688
|
+
const tokens = scriptTokens(command);
|
|
689
|
+
let at = 0;
|
|
690
|
+
while (at < tokens.length &&
|
|
691
|
+
(ASSIGNMENT.test(tokens[at] ?? "") || LAUNCHERS.has(tokens[at] ?? ""))) {
|
|
692
|
+
at += 1;
|
|
693
|
+
}
|
|
694
|
+
const path = tokens[at] ?? "";
|
|
695
|
+
const program = path.slice(path.lastIndexOf("/") + 1);
|
|
696
|
+
if (!PRELOADING_PROGRAMS.has(program)) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
for (at += 1; at < tokens.length; at += 1) {
|
|
700
|
+
const token = tokens[at] ?? "";
|
|
701
|
+
if (!token.startsWith("-")) {
|
|
702
|
+
if (program === "node") {
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
const equals = token.indexOf("=");
|
|
708
|
+
const flag = equals < 0 ? token : token.slice(0, equals);
|
|
709
|
+
if (!PRELOAD_FLAGS.has(flag)) {
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (equals < 0) {
|
|
713
|
+
at += 1;
|
|
714
|
+
}
|
|
715
|
+
const value = equals < 0 ? tokens[at] : token.slice(equals + 1);
|
|
716
|
+
const name = value === undefined ? undefined : packageOfSpecifier(value);
|
|
717
|
+
if (name !== undefined) {
|
|
718
|
+
found.add(name);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return found;
|
|
724
|
+
}
|
|
561
725
|
/** Whether a path below the target root lies in the directory `dir` below it, `""` being the root. */
|
|
562
726
|
function isInside(path, dir) {
|
|
563
727
|
return dir === "" || path.startsWith(`${dir}/`);
|
|
@@ -567,6 +731,18 @@ function manifestDir(dependency) {
|
|
|
567
731
|
const dir = dirnamePath(dependency.position.path);
|
|
568
732
|
return dir === "." ? "" : dir;
|
|
569
733
|
}
|
|
734
|
+
/**
|
|
735
|
+
* The declarations of one name a use written at `below` needs: those of the nearest
|
|
736
|
+
* manifest at or above it that declares the name, or every one for a use no file holds.
|
|
737
|
+
*/
|
|
738
|
+
function nearestDeclaring(declarations, below) {
|
|
739
|
+
if (below === undefined) {
|
|
740
|
+
return declarations;
|
|
741
|
+
}
|
|
742
|
+
const enclosing = declarations.filter((dependency) => isInside(below, manifestDir(dependency)));
|
|
743
|
+
const nearest = Math.max(-1, ...enclosing.map((dependency) => manifestDir(dependency).length));
|
|
744
|
+
return enclosing.filter((dependency) => manifestDir(dependency).length === nearest);
|
|
745
|
+
}
|
|
570
746
|
/**
|
|
571
747
|
* Adds to `needed` each declared dependency that the installed manifest of a needed or
|
|
572
748
|
* run declared dependency names as a peer, until no further one joins: the declaration of
|
|
@@ -617,22 +793,27 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
617
793
|
}
|
|
618
794
|
}
|
|
619
795
|
const workflow = new Set(workflowTokens(host, targetRoot));
|
|
796
|
+
const workflowPreloads = preloadsOf(workflowCommands(host, targetRoot));
|
|
620
797
|
const declared = [];
|
|
621
798
|
const ran = new Set();
|
|
622
799
|
const peers = new Map();
|
|
623
800
|
const uninstalledHere = new Set(nested);
|
|
624
801
|
for (const [dir, path] of manifests) {
|
|
625
|
-
const read =
|
|
802
|
+
const read = readDocument(host, joinPath(dir, MANIFEST));
|
|
626
803
|
if (read === undefined || !isRecord(read.value)) {
|
|
627
804
|
continue;
|
|
628
805
|
}
|
|
629
806
|
const tokens = scriptTokensOf(read.value);
|
|
807
|
+
const preloads = preloadsOf(manifestCommands(host, dir, read.value).map(({ command }) => command));
|
|
630
808
|
for (const dependency of declaredIn(read.text, read.value, path, scopeOf(path))) {
|
|
631
809
|
const found = installedOf(host, dir, dependency.name);
|
|
632
810
|
if (found === undefined && uninstalledHere.has(dir)) {
|
|
633
811
|
continue;
|
|
634
812
|
}
|
|
635
813
|
declared.push(dependency);
|
|
814
|
+
if (preloads.has(dependency.name) || workflowPreloads.has(dependency.name)) {
|
|
815
|
+
ran.add(declarationKey(dependency));
|
|
816
|
+
}
|
|
636
817
|
if (found === undefined) {
|
|
637
818
|
continue;
|
|
638
819
|
}
|
|
@@ -646,6 +827,8 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
646
827
|
// A declaration several projects compile is one user, and a use any project makes is
|
|
647
828
|
// a use, so the uses are merged by declaration before anything is counted.
|
|
648
829
|
const needed = new Set();
|
|
830
|
+
const live = new Set();
|
|
831
|
+
const holders = new Map();
|
|
649
832
|
const byName = new Map();
|
|
650
833
|
for (const dependency of declared) {
|
|
651
834
|
byName.set(dependency.name, [...(byName.get(dependency.name) ?? []), dependency]);
|
|
@@ -656,7 +839,10 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
656
839
|
// or above its file declares, and what the configuration needs is never deleted.
|
|
657
840
|
const unanswered = project.unanswered.flatMap((file) => relativePath(root, file) ?? []);
|
|
658
841
|
const unknownDeclared = declared.filter((dependency) => unanswered.some((file) => isInside(file, manifestDir(dependency))));
|
|
659
|
-
|
|
842
|
+
for (const dependency of unknownDeclared) {
|
|
843
|
+
needed.add(declarationKey(dependency));
|
|
844
|
+
live.add(declarationKey(dependency));
|
|
845
|
+
}
|
|
660
846
|
const unknown = unknownDeclared.map((dependency) => dependency.name);
|
|
661
847
|
const held = unknown.length === 0 ? [] : [[PROJECT_OWNER, new Set(unknown)]];
|
|
662
848
|
for (const [id, used] of [...project.uses, ...held]) {
|
|
@@ -666,16 +852,32 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
666
852
|
}
|
|
667
853
|
for (const [path, named] of project.at) {
|
|
668
854
|
const below = path === ANYWHERE ? undefined : relativePath(root, path);
|
|
669
|
-
for (const one of named) {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
855
|
+
for (const [one, owners] of named) {
|
|
856
|
+
// An import none of whose bindings is used is no evidence about the code using it.
|
|
857
|
+
const unheld = owners.size === 0;
|
|
858
|
+
for (const dependency of nearestDeclaring(byName.get(one) ?? [], below)) {
|
|
859
|
+
const key = declarationKey(dependency);
|
|
860
|
+
needed.add(key);
|
|
861
|
+
if (unheld) {
|
|
862
|
+
live.add(key);
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
const held = holders.get(key) ?? new Set();
|
|
866
|
+
owners.forEach((owner) => held.add(owner));
|
|
867
|
+
holders.set(key, held);
|
|
673
868
|
}
|
|
674
869
|
}
|
|
675
870
|
}
|
|
676
871
|
}
|
|
677
872
|
}
|
|
678
873
|
addNeededPeers(declared, peers, needed, ran);
|
|
874
|
+
addNeededPeers(declared, peers, live, ran);
|
|
875
|
+
const heldBy = new Map();
|
|
876
|
+
for (const [key, held] of holders) {
|
|
877
|
+
if (!live.has(key) && !ran.has(key)) {
|
|
878
|
+
heldBy.set(key, [...held].sort(bytewise));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
679
881
|
const users = new Map();
|
|
680
882
|
for (const used of uses.values()) {
|
|
681
883
|
used.forEach((one) => users.set(one, (users.get(one) ?? 0) + 1));
|
|
@@ -685,8 +887,24 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
685
887
|
for (const id of candidates) {
|
|
686
888
|
const sole = [...(uses.get(id) ?? [])].filter((one) => users.get(one) === 1 && names.has(one));
|
|
687
889
|
if (sole.length > 0) {
|
|
688
|
-
lastUses.set(id, sole.sort(
|
|
890
|
+
lastUses.set(id, sole.sort(bytewise));
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
const owners = new Map();
|
|
894
|
+
for (const [owner, used] of uses) {
|
|
895
|
+
for (const one of used) {
|
|
896
|
+
if (names.has(one)) {
|
|
897
|
+
owners.set(one, (owners.get(one) ?? new Set()).add(owner));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
const fileLastUses = new Map();
|
|
902
|
+
for (const [one, held] of [...owners].sort(([a], [b]) => bytewise(a, b))) {
|
|
903
|
+
const paths = new Set([...held].map((owner) => owner.replace(/:\d+:\d+$/u, "")));
|
|
904
|
+
const [path] = paths;
|
|
905
|
+
if (paths.size === 1 && path !== undefined && path !== PROJECT_OWNER) {
|
|
906
|
+
fileLastUses.set(path, [...(fileLastUses.get(path) ?? []), one]);
|
|
689
907
|
}
|
|
690
908
|
}
|
|
691
|
-
return { declared, needed, ran, unbuilt, lastUses };
|
|
909
|
+
return { declared, needed, ran, unbuilt, lastUses, fileLastUses, heldBy };
|
|
692
910
|
}
|