@cplieger/deadset-ts 7.2.0-dev.3 → 8.0.0-dev.1
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/dist/bin/mapper-connection.js +7 -4
- package/dist/src/analysis.js +116 -16
- package/dist/src/augmentations.js +21 -8
- package/dist/src/automatic-types.js +189 -0
- package/dist/src/base-url.js +18 -0
- package/dist/src/component-files.js +96 -31
- package/dist/src/components.js +7 -6
- package/dist/src/config.js +1 -0
- package/dist/src/configuration-files.js +54 -15
- package/dist/src/configured-declarations.js +24 -2
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/contextual-uses.js +26 -26
- package/dist/src/convention-rows.js +75 -0
- package/dist/src/conventions.js +101 -5
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +185 -30
- package/dist/src/destructuring.js +77 -2
- package/dist/src/discover.js +188 -85
- package/dist/src/edges.js +1 -1
- package/dist/src/entry-point-gaps.js +7 -10
- package/dist/src/entry-points.js +112 -11
- package/dist/src/findings/completion.js +116 -21
- package/dist/src/findings/dependencies-and-module-machinery.js +71 -25
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +44 -13
- package/dist/src/findings/non-code-artifacts.js +66 -33
- package/dist/src/findings/unused-declarations.js +21 -1
- package/dist/src/findings/visibility-narrowing.js +22 -3
- package/dist/src/generated-file.js +11 -0
- package/dist/src/git-ignore.js +86 -0
- package/dist/src/graph.js +4 -1
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +313 -50
- package/dist/src/inventory.js +63 -8
- package/dist/src/json-document.js +5 -5
- package/dist/src/kinds.js +13 -13
- package/dist/src/matrix.js +18 -9
- package/dist/src/mdx-entries.js +197 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/namespace-objects.js +28 -18
- package/dist/src/overrides.js +45 -9
- package/dist/src/paths.js +9 -0
- package/dist/src/print.js +1 -0
- package/dist/src/ref.js +10 -0
- package/dist/src/references.js +29 -13
- package/dist/src/reflective-lookup.js +8 -6
- package/dist/src/report.js +4 -2
- package/dist/src/reporters/template.js +22 -13
- package/dist/src/resolve.js +2 -0
- package/dist/src/roots.js +110 -38
- package/dist/src/schema.js +1 -0
- package/dist/src/serialization-contract.js +97 -20
- package/dist/src/session.js +26 -1
- package/dist/src/source-files.js +39 -14
- package/dist/src/spelled-names.js +58 -0
- package/dist/src/suppress-file.js +12 -4
- package/dist/src/suppress-inline.js +21 -3
- package/dist/src/suppress.js +7 -5
- package/dist/src/sweep.js +27 -10
- package/dist/src/symbol-keys.js +103 -2
- package/dist/src/template-field.js +9 -6
- package/dist/src/test-support.js +121 -17
- package/dist/src/type-errors.js +116 -35
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/value-reach.js +17 -5
- package/dist/src/verbs/analyze.js +19 -1
- package/dist/src/verbs/explain.js +4 -2
- package/dist/src/version-range.js +10 -5
- package/dist/src/version.js +1 -1
- package/dist/src/workflow-steps.js +119 -22
- package/dist/src/workspace-programs.js +7 -3
- package/dist/src/workspace-resolution.js +72 -13
- package/dist/src/workspace.js +1 -1
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/analysis.ts +175 -16
- package/src/augmentations.ts +21 -10
- package/src/automatic-types.ts +246 -0
- package/src/base-url.ts +24 -0
- package/src/component-files.ts +116 -31
- package/src/components.ts +7 -6
- package/src/config.ts +3 -0
- package/src/configuration-files.ts +80 -18
- package/src/configured-declarations.ts +33 -4
- package/src/conformance-record.ts +2 -2
- package/src/contextual-uses.ts +27 -27
- package/src/convention-rows.ts +103 -0
- package/src/conventions.ts +121 -7
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +249 -43
- package/src/destructuring.ts +93 -2
- package/src/discover.ts +234 -86
- package/src/edges.ts +1 -1
- package/src/entry-point-gaps.ts +8 -11
- package/src/entry-points.ts +133 -11
- package/src/finding.ts +10 -0
- package/src/findings/completion.ts +147 -24
- package/src/findings/dependencies-and-module-machinery.ts +90 -28
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +56 -14
- package/src/findings/non-code-artifacts.ts +81 -41
- package/src/findings/unused-declarations.ts +25 -1
- package/src/findings/visibility-narrowing.ts +32 -3
- package/src/generated-file.ts +16 -0
- package/src/git-ignore.ts +98 -0
- package/src/graph.ts +6 -0
- package/src/host.ts +5 -9
- package/src/import-bindings.ts +128 -0
- package/src/indexed-access.ts +152 -30
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +415 -64
- package/src/inventory.ts +83 -7
- package/src/json-document.ts +7 -7
- package/src/kinds.ts +13 -13
- package/src/matrix.ts +32 -14
- package/src/mdx-entries.ts +241 -0
- package/src/module-declarations.ts +11 -13
- package/src/namespace-objects.ts +34 -21
- package/src/overrides.ts +56 -9
- package/src/paths.ts +10 -0
- package/src/print.ts +1 -0
- package/src/ref.ts +12 -0
- package/src/references.ts +38 -17
- package/src/reflective-lookup.ts +9 -7
- package/src/report.ts +16 -3
- package/src/reporters/template.ts +21 -13
- package/src/resolve.ts +3 -0
- package/src/roots.ts +163 -45
- package/src/schema.ts +1 -0
- package/src/serialization-contract.ts +108 -17
- package/src/session.ts +29 -1
- package/src/source-files.ts +66 -16
- package/src/spelled-names.ts +102 -0
- package/src/suppress-file.ts +14 -3
- package/src/suppress-inline.ts +21 -2
- package/src/suppress.ts +9 -5
- package/src/sweep.ts +32 -11
- package/src/symbol-keys.ts +129 -2
- package/src/template-field.ts +9 -6
- package/src/test-support.ts +149 -16
- package/src/type-errors.ts +132 -41
- package/src/type-parameter-reads.ts +227 -0
- package/src/value-reach.ts +19 -6
- package/src/verbs/analyze.ts +28 -1
- package/src/verbs/explain.ts +5 -2
- package/src/version-range.ts +10 -5
- package/src/version.ts +1 -1
- package/src/workflow-steps.ts +127 -22
- package/src/workspace-programs.ts +7 -3
- package/src/workspace-resolution.ts +86 -14
- package/src/workspace.ts +1 -1
package/dist/src/conventions.js
CHANGED
|
@@ -471,6 +471,40 @@ function manifestKeyUses(manifest, keys) {
|
|
|
471
471
|
const strings = keys.flatMap((key) => documentStrings(manifest[key]));
|
|
472
472
|
return declared.filter((name) => strings.some((text) => text === name || text.startsWith(`${name}/`)));
|
|
473
473
|
}
|
|
474
|
+
/** The extensions an entry a manifest entry key names without one may carry. */
|
|
475
|
+
const ENTRY_EXTENSIONS = "{js,jsx,mjs,cjs,ts,tsx,mts,cts}";
|
|
476
|
+
/** The characters that make a manifest string a pattern rather than a directory's name. */
|
|
477
|
+
const PATTERN = /[*?{}[\]]/u;
|
|
478
|
+
/**
|
|
479
|
+
* The entry globs one applied row's manifest entry keys write, below the manifest's
|
|
480
|
+
* directory: a directory's every module, or a file with or without an extension. A string
|
|
481
|
+
* that leaves the directory names nothing.
|
|
482
|
+
*/
|
|
483
|
+
function manifestEntryGlobs(manifest, keys) {
|
|
484
|
+
const at = (value, path) => {
|
|
485
|
+
const [key, ...rest] = path;
|
|
486
|
+
if (key === undefined) {
|
|
487
|
+
return [value];
|
|
488
|
+
}
|
|
489
|
+
if (!isRecord(value)) {
|
|
490
|
+
return [];
|
|
491
|
+
}
|
|
492
|
+
const next = key === "*" ? Object.values(value) : Object.hasOwn(value, key) ? [value[key]] : [];
|
|
493
|
+
return next.flatMap((one) => at(one, rest));
|
|
494
|
+
};
|
|
495
|
+
return keys.flatMap(({ key, directory }) => at(manifest, key.split(".")).flatMap((value) => documentStrings(value).flatMap((text) => {
|
|
496
|
+
const path = normalizePath(text)
|
|
497
|
+
.replace(/^(?:\.\/)+/u, "")
|
|
498
|
+
.replace(/\/+$/u, "");
|
|
499
|
+
if (path === "" || path === ".." || path.startsWith("../") || path.startsWith("/")) {
|
|
500
|
+
return [];
|
|
501
|
+
}
|
|
502
|
+
if (!directory) {
|
|
503
|
+
return [path, `${path}.${ENTRY_EXTENSIONS}`];
|
|
504
|
+
}
|
|
505
|
+
return PATTERN.test(path) ? [] : [`${path}/**/*.${ENTRY_EXTENSIONS}`];
|
|
506
|
+
})));
|
|
507
|
+
}
|
|
474
508
|
/**
|
|
475
509
|
* Every value one file writes at one property's path inside the object it is read from:
|
|
476
510
|
* the options argument of every call `call` names, or the file's default export. A string
|
|
@@ -620,6 +654,57 @@ function shortNameUses(host, parse, dir, manifest, readings) {
|
|
|
620
654
|
}
|
|
621
655
|
return [...used].filter((name) => declared.has(name)).sort(compare);
|
|
622
656
|
}
|
|
657
|
+
/**
|
|
658
|
+
* The package one value of a prefixed-name reading names, or none: ESLint's own naming,
|
|
659
|
+
* https://github.com/eslint/eslintrc/blob/main/lib/shared/naming.js
|
|
660
|
+
*/
|
|
661
|
+
function prefixedPackage(value, reading) {
|
|
662
|
+
const { prefix, marker } = reading;
|
|
663
|
+
let name;
|
|
664
|
+
if (marker === undefined) {
|
|
665
|
+
if (value.includes(":") || value.startsWith(".") || value.startsWith("/")) {
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
668
|
+
const parts = value.split("/");
|
|
669
|
+
name = (value.startsWith("@") ? parts.slice(0, 2) : parts.slice(0, 1)).join("/");
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
if (!value.startsWith(marker) || !value.includes("/", marker.length)) {
|
|
673
|
+
return undefined;
|
|
674
|
+
}
|
|
675
|
+
name = value.slice(marker.length, value.lastIndexOf("/"));
|
|
676
|
+
}
|
|
677
|
+
if (name === "") {
|
|
678
|
+
return undefined;
|
|
679
|
+
}
|
|
680
|
+
if (!name.startsWith("@")) {
|
|
681
|
+
return name.startsWith(`${prefix}-`) ? name : `${prefix}-${name}`;
|
|
682
|
+
}
|
|
683
|
+
const [scope, rest] = name.split("/");
|
|
684
|
+
if (rest === undefined || rest === "" || rest === prefix) {
|
|
685
|
+
return `${scope ?? name}/${prefix}`;
|
|
686
|
+
}
|
|
687
|
+
return rest.startsWith(`${prefix}-`) ? name : `${scope ?? name}/${prefix}-${rest}`;
|
|
688
|
+
}
|
|
689
|
+
/** The declared dependencies an applied row's prefixed names use. */
|
|
690
|
+
function prefixedNameUses(host, parse, dir, manifest, readings) {
|
|
691
|
+
const declared = new Set(DEPENDENCY_SECTIONS.flatMap((section) => {
|
|
692
|
+
const held = isRecord(manifest) ? manifest[section] : undefined;
|
|
693
|
+
return isRecord(held) ? Object.keys(held) : [];
|
|
694
|
+
}));
|
|
695
|
+
const used = new Set();
|
|
696
|
+
for (const reading of readings) {
|
|
697
|
+
for (const { file } of readingFiles(host, parse, dir, reading.files)) {
|
|
698
|
+
for (const value of readProperty(file, reading.property, reading.call, true).values) {
|
|
699
|
+
const named = prefixedPackage(value, reading);
|
|
700
|
+
if (named !== undefined && declared.has(named)) {
|
|
701
|
+
used.add(named);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return [...used].sort(compare);
|
|
707
|
+
}
|
|
623
708
|
/** The declared dependencies an applied row's helper settings use. */
|
|
624
709
|
function helperUses(host, parse, dir, manifest, readings) {
|
|
625
710
|
const declared = new Set(DEPENDENCY_SECTIONS.flatMap((section) => {
|
|
@@ -756,6 +841,10 @@ export function readConventions(host, parse, manifests, disabled, rows = CONVENT
|
|
|
756
841
|
for (const name of shortNameUses(host, parse, manifest.dir, declared, row.shortNames ?? [])) {
|
|
757
842
|
uses.add(name);
|
|
758
843
|
}
|
|
844
|
+
const prefixed = row.prefixedNames ?? [];
|
|
845
|
+
for (const name of prefixedNameUses(host, parse, manifest.dir, declared, prefixed)) {
|
|
846
|
+
uses.add(name);
|
|
847
|
+
}
|
|
759
848
|
const keys = row.packageKeys ?? [];
|
|
760
849
|
for (const name of packageKeyUses(host, parse, manifest.dir, declared, keys)) {
|
|
761
850
|
uses.add(name);
|
|
@@ -774,7 +863,11 @@ export function readConventions(host, parse, manifests, disabled, rows = CONVENT
|
|
|
774
863
|
for (const below of new Set((row.generated ?? []).flatMap((one) => expand(one, resolved)))) {
|
|
775
864
|
generated.push({ dir: manifest.dir, row: row.name, below });
|
|
776
865
|
}
|
|
777
|
-
|
|
866
|
+
const written = [
|
|
867
|
+
...row.entries.flatMap((entry) => expand(entry, resolved)),
|
|
868
|
+
...manifestEntryGlobs(declared, row.manifestEntries ?? []),
|
|
869
|
+
];
|
|
870
|
+
for (const glob of new Set(written)) {
|
|
778
871
|
globs.push({
|
|
779
872
|
dir: manifest.dir,
|
|
780
873
|
row: row.name,
|
|
@@ -819,10 +912,13 @@ export function conventionEntries(conventions, files) {
|
|
|
819
912
|
const found = new Map();
|
|
820
913
|
for (const glob of conventions.globs) {
|
|
821
914
|
for (const file of files.byName.values()) {
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
915
|
+
const matched = [file.fileName, ...files.writtenAs(file)].some((name) => {
|
|
916
|
+
const path = relativePath(glob.dir, name);
|
|
917
|
+
return (path !== undefined &&
|
|
918
|
+
glob.expression.test(path) &&
|
|
919
|
+
!glob.excludes.some((one) => one.test(path)));
|
|
920
|
+
});
|
|
921
|
+
if (matched) {
|
|
826
922
|
found.set(`${file.fileName}\u0000${glob.row}`, { file, row: glob.row });
|
|
827
923
|
}
|
|
828
924
|
}
|
package/dist/src/decorator.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The decorator class: a member a decorator expression is attached to, and every
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* the
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* The use the decorator itself makes of what it decorates is the reference pass's,
|
|
9
|
-
* which records it as a `decorator` reference; this class records only that the
|
|
10
|
-
* declaration is held back, and the member set of a decorated class is every member
|
|
11
|
-
* the inventory holds for the class, static members included.
|
|
2
|
+
* The decorator class: a member a decorator expression is attached to, and every member
|
|
3
|
+
* of a class one is attached to but the member a parameter property declares, is held
|
|
4
|
+
* back, because the decorator may reach the member by its name. The rule is read from
|
|
5
|
+
* the tree alone. The decorator's own use of what it decorates is the reference pass's
|
|
6
|
+
* `decorator` reference; this class records only that the declaration is held back,
|
|
7
|
+
* static members of a decorated class included.
|
|
12
8
|
*/
|
|
13
9
|
import { decoratorsOf, spelledDecorator } from "./calls.js";
|
|
14
10
|
import { isParameterProperty, nodeKey, } from "./inventory.js";
|
|
@@ -34,25 +30,32 @@ export function decorator(input) {
|
|
|
34
30
|
}
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
|
-
const
|
|
33
|
+
const decorated = [];
|
|
34
|
+
// A decorated class's container passes each constructor argument, which keeps the
|
|
35
|
+
// parameter, not the member a parameter property stores it in.
|
|
36
|
+
const parameterMembers = new Set();
|
|
38
37
|
for (const file of project.ownSourceFiles()) {
|
|
39
38
|
const visit = (node) => {
|
|
40
39
|
const id = held.declarations.get(nodeKey(file, node));
|
|
41
40
|
const symbol = id === undefined ? undefined : byId.get(id);
|
|
41
|
+
if (id !== undefined && isParameterProperty(node)) {
|
|
42
|
+
parameterMembers.add(id);
|
|
43
|
+
}
|
|
42
44
|
// A parameter property's decorator is attached to its parameter, and to no member.
|
|
43
45
|
if (symbol !== undefined && DECORATED.has(symbol.kind) && !isParameterProperty(node)) {
|
|
44
46
|
for (const attached of decoratorsOf(node)) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
47
|
+
decorated.push({
|
|
48
|
+
symbol,
|
|
49
|
+
detail: `decorated by ${spelledDecorator(attached)}`,
|
|
50
|
+
site: renderPosition(file, targetRoot, attached.getStart()),
|
|
51
|
+
});
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
node.forEachChild(visit);
|
|
54
55
|
};
|
|
55
56
|
file.forEachChild(visit);
|
|
56
57
|
}
|
|
57
|
-
return
|
|
58
|
+
return decorated.flatMap(({ symbol, detail, site }) => (symbol.kind === "class"
|
|
59
|
+
? (membersOf.get(symbol.id) ?? []).filter((member) => !parameterMembers.has(member.id))
|
|
60
|
+
: [symbol]).map((member) => ({ id: member.id, detail, site })));
|
|
58
61
|
}
|
package/dist/src/dependencies.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
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
13
|
import { scriptTokens } from "./manifest.js";
|
|
13
14
|
import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
@@ -18,6 +19,8 @@ const MANIFEST = "package.json";
|
|
|
18
19
|
const MODULES_DIR = "node_modules";
|
|
19
20
|
const MODULES_SEGMENT = `/${MODULES_DIR}/`;
|
|
20
21
|
const TYPES_SCOPE = "@types/";
|
|
22
|
+
/** A script token naming a JavaScript file by a path no expansion changes. */
|
|
23
|
+
const SCRIPT_FILE = /^[^$`{*?[~]+\.(?:js|mjs|cjs)$/u;
|
|
21
24
|
/** The manifest sections a dependency is declared in, each by the selector its reference spells. */
|
|
22
25
|
const SECTIONS = [
|
|
23
26
|
["dependencies", "dependency"],
|
|
@@ -30,7 +33,7 @@ const PROJECT_OWNER = "";
|
|
|
30
33
|
export function configurationNeeds(packages) {
|
|
31
34
|
return {
|
|
32
35
|
uses: new Map([[PROJECT_OWNER, new Set(packages)]]),
|
|
33
|
-
at: new Map([[ANYWHERE, new
|
|
36
|
+
at: new Map([[ANYWHERE, new Map(packages.map((one) => [one, new Set([PROJECT_OWNER])]))]]),
|
|
34
37
|
unanswered: [],
|
|
35
38
|
};
|
|
36
39
|
}
|
|
@@ -99,12 +102,35 @@ function typePackages(name, programPackages) {
|
|
|
99
102
|
}
|
|
100
103
|
return [typed, named];
|
|
101
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* The strings the `extends` member of one compiler configuration file holds, read as the
|
|
107
|
+
* compiler reads the file, comments and trailing commas allowed; none where it cannot be read.
|
|
108
|
+
*/
|
|
109
|
+
function extendsOf(host, path, parse) {
|
|
110
|
+
let text;
|
|
111
|
+
try {
|
|
112
|
+
text = host.readFile(path);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const [statement] = parse(path, text).statements;
|
|
118
|
+
const document = statement !== undefined && isExpressionStatement(statement) ? statement.expression : undefined;
|
|
119
|
+
if (document === undefined || !isObjectLiteralExpression(document)) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const value = document.properties
|
|
123
|
+
.filter(isPropertyAssignment)
|
|
124
|
+
.findLast((property) => isLiteralText(property.name) && property.name.text === "extends")?.initializer;
|
|
125
|
+
const entries = value !== undefined && isArrayLiteralExpression(value) ? value.elements : [value];
|
|
126
|
+
return entries.flatMap((entry) => entry !== undefined && isLiteralText(entry) ? [entry.text] : []);
|
|
127
|
+
}
|
|
102
128
|
/**
|
|
103
129
|
* The packages the compiler configuration of one project names: its type packages,
|
|
104
130
|
* its JSX import source, the helper library it imports, its plugins, and every
|
|
105
131
|
* package a configuration file it extends through is read from.
|
|
106
132
|
*/
|
|
107
|
-
function configurationPackages(host, configFile, options, programPackages) {
|
|
133
|
+
function configurationPackages(host, configFile, options, programPackages, parse) {
|
|
108
134
|
const found = new Set();
|
|
109
135
|
const add = (name) => {
|
|
110
136
|
const named = typeof name === "string" ? packageOfSpecifier(name) : undefined;
|
|
@@ -139,12 +165,7 @@ function configurationPackages(host, configFile, options, programPackages) {
|
|
|
139
165
|
continue;
|
|
140
166
|
}
|
|
141
167
|
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
|
-
}
|
|
168
|
+
for (const entry of extendsOf(host, file, parse)) {
|
|
148
169
|
if (entry.startsWith(".") || isAbsolutePath(entry)) {
|
|
149
170
|
const local = resolvePath(dirnamePath(file), entry);
|
|
150
171
|
pending.push(local.endsWith(".json") ? local : `${local}.json`);
|
|
@@ -207,13 +228,19 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
207
228
|
programPackages.add(named);
|
|
208
229
|
}
|
|
209
230
|
}
|
|
210
|
-
const
|
|
231
|
+
const options = project.program.getCompilerOptions();
|
|
232
|
+
const packages = configurationPackages(host, project.configFile, options, programPackages, parse);
|
|
233
|
+
const automatic = automaticTypes(host, project.configFile, options, parse);
|
|
211
234
|
const uses = new Map();
|
|
212
235
|
const at = new Map();
|
|
213
236
|
const use = (owners, named, path) => {
|
|
214
237
|
for (const one of named) {
|
|
215
|
-
|
|
238
|
+
const inFile = at.get(path) ?? new Map();
|
|
239
|
+
at.set(path, inFile);
|
|
240
|
+
const holders = inFile.get(one) ?? new Set();
|
|
241
|
+
inFile.set(one, holders);
|
|
216
242
|
for (const owner of owners) {
|
|
243
|
+
holders.add(owner);
|
|
217
244
|
let set = uses.get(owner);
|
|
218
245
|
if (set === undefined) {
|
|
219
246
|
set = new Set();
|
|
@@ -229,7 +256,7 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
229
256
|
const inFile = (owners, named) => {
|
|
230
257
|
use(owners, named, file.fileName);
|
|
231
258
|
};
|
|
232
|
-
if (fileNeeds(project, held, file, programPackages, inFile)) {
|
|
259
|
+
if (fileNeeds(project, held, file, { programPackages, automatic }, inFile)) {
|
|
233
260
|
unanswered.push(file.fileName);
|
|
234
261
|
}
|
|
235
262
|
}
|
|
@@ -243,9 +270,10 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
243
270
|
}
|
|
244
271
|
return names;
|
|
245
272
|
};
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
273
|
+
const files = sourceFilesOf(project, targetRoot);
|
|
274
|
+
const configuration = configurationFiles(host, targetRoot, files, parse);
|
|
275
|
+
for (const { file, manifestDir } of configuration.modules) {
|
|
276
|
+
const names = declaredAt(joinPath(manifestDir, MANIFEST));
|
|
249
277
|
for (const { node, text } of moduleStrings(file)) {
|
|
250
278
|
use(ownersOf(file, held, node), dependenciesNamed(text, names), file.fileName);
|
|
251
279
|
}
|
|
@@ -258,7 +286,8 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
258
286
|
}
|
|
259
287
|
}
|
|
260
288
|
}
|
|
261
|
-
|
|
289
|
+
const ranFiles = outsideChain(host, targetRoot, files, parse, scriptRunFiles(host, targetRoot, files));
|
|
290
|
+
for (const { file, manifestDirs } of [...configuration.outside, ...ranFiles]) {
|
|
262
291
|
for (const manifestDir of manifestDirs) {
|
|
263
292
|
const names = declaredAt(joinPath(manifestDir, "package.json"));
|
|
264
293
|
for (const { text } of moduleStrings(file)) {
|
|
@@ -268,6 +297,48 @@ export function projectNeeds(project, held, host, targetRoot, parse) {
|
|
|
268
297
|
}
|
|
269
298
|
return { uses, at, unanswered };
|
|
270
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* The JavaScript files no program holds that a script of a manifest at or above one of the
|
|
302
|
+
* project's files runs, each with the directory of that manifest, and those a workflow
|
|
303
|
+
* step or git hook runs, with the target root's: a token naming such a file below the
|
|
304
|
+
* target root and in no `node_modules`, read against the manifest's directory.
|
|
305
|
+
*/
|
|
306
|
+
function scriptRunFiles(host, targetRoot, files) {
|
|
307
|
+
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
308
|
+
const dirs = new Set([root]);
|
|
309
|
+
for (const name of files.byName.keys()) {
|
|
310
|
+
for (let dir = dirnamePath(name); !dirs.has(dir); dir = dirnamePath(dir)) {
|
|
311
|
+
if (relativePath(root, dir) === undefined) {
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
dirs.add(dir);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const tokens = new Map([
|
|
318
|
+
[root, new Set(workflowTokens(host, root))],
|
|
319
|
+
]);
|
|
320
|
+
for (const dir of dirs) {
|
|
321
|
+
const read = readJSON(host, joinPath(dir, MANIFEST));
|
|
322
|
+
if (read !== undefined && isRecord(read.value)) {
|
|
323
|
+
const own = scriptTokensOf(read.value);
|
|
324
|
+
tokens.set(dir, dir === root ? new Set([...own, ...(tokens.get(root) ?? [])]) : own);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const found = [];
|
|
328
|
+
for (const [manifestDir, held] of tokens) {
|
|
329
|
+
for (const token of held) {
|
|
330
|
+
const path = SCRIPT_FILE.test(token) ? resolvePath(manifestDir, token) : undefined;
|
|
331
|
+
const below = path === undefined ? undefined : relativePath(root, path);
|
|
332
|
+
if (path !== undefined &&
|
|
333
|
+
below?.split("/").includes(MODULES_DIR) === false &&
|
|
334
|
+
!files.byName.has(path) &&
|
|
335
|
+
host.kindOf(path) === "file") {
|
|
336
|
+
found.push({ path, manifestDir });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return found;
|
|
341
|
+
}
|
|
271
342
|
/** The declarations of the inventory a use written at one node belongs to. */
|
|
272
343
|
function ownersOf(file, held, node) {
|
|
273
344
|
const parent = node.parent;
|
|
@@ -291,8 +362,27 @@ function ownersOf(file, held, node) {
|
|
|
291
362
|
}
|
|
292
363
|
return [PROJECT_OWNER];
|
|
293
364
|
}
|
|
294
|
-
/**
|
|
295
|
-
|
|
365
|
+
/**
|
|
366
|
+
* Whether one identifier stands for a value or type of the scope it is written in, rather
|
|
367
|
+
* than naming a member, a property or a label.
|
|
368
|
+
*/
|
|
369
|
+
function isScopeName(node) {
|
|
370
|
+
const parent = node.parent;
|
|
371
|
+
if (parent === undefined || isShorthandPropertyAssignment(parent)) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
const named = parent;
|
|
375
|
+
return (named.name !== node &&
|
|
376
|
+
(parent.kind !== SyntaxKind.QualifiedName || named.right !== node) &&
|
|
377
|
+
named.propertyName !== node &&
|
|
378
|
+
named.label !== node);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Records the needs of one file, and answers whether a specifier of it went unanswered. A
|
|
382
|
+
* module specifier or a name of the file's scope that resolves to nothing, and that an
|
|
383
|
+
* automatically included type package declares, uses that package.
|
|
384
|
+
*/
|
|
385
|
+
function fileNeeds(project, held, file, { programPackages, automatic }, use) {
|
|
296
386
|
const specifiers = [...file.imports, ...file.moduleAugmentations].filter(isLiteralText);
|
|
297
387
|
const bindings = [];
|
|
298
388
|
const bound = new Set();
|
|
@@ -309,16 +399,22 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
309
399
|
}
|
|
310
400
|
const names = new Set(bindings.map(({ node }) => node.text));
|
|
311
401
|
const candidates = [];
|
|
402
|
+
const globals = [];
|
|
312
403
|
const visit = (node) => {
|
|
313
|
-
if (isIdentifier(node) &&
|
|
314
|
-
|
|
404
|
+
if (isIdentifier(node) && !bound.has(node)) {
|
|
405
|
+
if (names.has(node.text)) {
|
|
406
|
+
candidates.push(node);
|
|
407
|
+
}
|
|
408
|
+
else if (automatic.globals.has(node.text) && isScopeName(node)) {
|
|
409
|
+
globals.push(node);
|
|
410
|
+
}
|
|
315
411
|
}
|
|
316
412
|
node.forEachChild(visit);
|
|
317
413
|
};
|
|
318
|
-
if (names.size > 0) {
|
|
414
|
+
if (names.size > 0 || automatic.globals.size > 0) {
|
|
319
415
|
visit(file);
|
|
320
416
|
}
|
|
321
|
-
const asked = [...specifiers, ...bindings.map(({ node }) => node), ...candidates];
|
|
417
|
+
const asked = [...specifiers, ...bindings.map(({ node }) => node), ...candidates, ...globals];
|
|
322
418
|
const handles = asked.map((node) => project.handle(node));
|
|
323
419
|
const resolved = project.symbolsAt(handles);
|
|
324
420
|
const packagesOf = new Map();
|
|
@@ -329,6 +425,9 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
329
425
|
named.add(byName);
|
|
330
426
|
}
|
|
331
427
|
const symbol = resolved[index];
|
|
428
|
+
if (symbol === undefined) {
|
|
429
|
+
automatic.modules.get(specifier.text)?.forEach((one) => named.add(one));
|
|
430
|
+
}
|
|
332
431
|
for (const declaration of typeof symbol === "object" ? symbol.declarations : []) {
|
|
333
432
|
const byPath = packageOfPath(declaration.path);
|
|
334
433
|
if (byPath !== undefined) {
|
|
@@ -364,7 +463,7 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
364
463
|
let symbol = resolved[offset + index];
|
|
365
464
|
const parent = node.parent;
|
|
366
465
|
if (parent !== undefined && isShorthandPropertyAssignment(parent) && parent.name === node) {
|
|
367
|
-
symbol = project.shorthandValueAt(project.handle(
|
|
466
|
+
symbol = project.shorthandValueAt(project.handle(parent));
|
|
368
467
|
}
|
|
369
468
|
const named = symbol === UNANSWERED || (unresolvedNames.has(node.text) && symbol === undefined)
|
|
370
469
|
? byName.get(node.text)
|
|
@@ -376,6 +475,13 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
376
475
|
use(ownersOf(file, held, node), named);
|
|
377
476
|
}
|
|
378
477
|
});
|
|
478
|
+
const unresolvedAt = offset + candidates.length;
|
|
479
|
+
globals.forEach((node, index) => {
|
|
480
|
+
const symbol = resolved[unresolvedAt + index];
|
|
481
|
+
if (symbol === undefined || symbol === UNANSWERED) {
|
|
482
|
+
use(ownersOf(file, held, node), automatic.globals.get(node.text) ?? []);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
379
485
|
for (const reference of file.typeReferenceDirectives) {
|
|
380
486
|
use(ownersOf(file, held, file), typePackages(reference.fileName, programPackages));
|
|
381
487
|
}
|
|
@@ -567,6 +673,18 @@ function manifestDir(dependency) {
|
|
|
567
673
|
const dir = dirnamePath(dependency.position.path);
|
|
568
674
|
return dir === "." ? "" : dir;
|
|
569
675
|
}
|
|
676
|
+
/**
|
|
677
|
+
* The declarations of one name a use written at `below` needs: those of the nearest
|
|
678
|
+
* manifest at or above it that declares the name, or every one for a use no file holds.
|
|
679
|
+
*/
|
|
680
|
+
function nearestDeclaring(declarations, below) {
|
|
681
|
+
if (below === undefined) {
|
|
682
|
+
return declarations;
|
|
683
|
+
}
|
|
684
|
+
const enclosing = declarations.filter((dependency) => isInside(below, manifestDir(dependency)));
|
|
685
|
+
const nearest = Math.max(-1, ...enclosing.map((dependency) => manifestDir(dependency).length));
|
|
686
|
+
return enclosing.filter((dependency) => manifestDir(dependency).length === nearest);
|
|
687
|
+
}
|
|
570
688
|
/**
|
|
571
689
|
* Adds to `needed` each declared dependency that the installed manifest of a needed or
|
|
572
690
|
* run declared dependency names as a peer, until no further one joins: the declaration of
|
|
@@ -646,6 +764,8 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
646
764
|
// A declaration several projects compile is one user, and a use any project makes is
|
|
647
765
|
// a use, so the uses are merged by declaration before anything is counted.
|
|
648
766
|
const needed = new Set();
|
|
767
|
+
const live = new Set();
|
|
768
|
+
const holders = new Map();
|
|
649
769
|
const byName = new Map();
|
|
650
770
|
for (const dependency of declared) {
|
|
651
771
|
byName.set(dependency.name, [...(byName.get(dependency.name) ?? []), dependency]);
|
|
@@ -656,7 +776,10 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
656
776
|
// or above its file declares, and what the configuration needs is never deleted.
|
|
657
777
|
const unanswered = project.unanswered.flatMap((file) => relativePath(root, file) ?? []);
|
|
658
778
|
const unknownDeclared = declared.filter((dependency) => unanswered.some((file) => isInside(file, manifestDir(dependency))));
|
|
659
|
-
|
|
779
|
+
for (const dependency of unknownDeclared) {
|
|
780
|
+
needed.add(declarationKey(dependency));
|
|
781
|
+
live.add(declarationKey(dependency));
|
|
782
|
+
}
|
|
660
783
|
const unknown = unknownDeclared.map((dependency) => dependency.name);
|
|
661
784
|
const held = unknown.length === 0 ? [] : [[PROJECT_OWNER, new Set(unknown)]];
|
|
662
785
|
for (const [id, used] of [...project.uses, ...held]) {
|
|
@@ -666,16 +789,32 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
666
789
|
}
|
|
667
790
|
for (const [path, named] of project.at) {
|
|
668
791
|
const below = path === ANYWHERE ? undefined : relativePath(root, path);
|
|
669
|
-
for (const one of named) {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
792
|
+
for (const [one, owners] of named) {
|
|
793
|
+
// An import none of whose bindings is used is no evidence about the code using it.
|
|
794
|
+
const unheld = owners.size === 0;
|
|
795
|
+
for (const dependency of nearestDeclaring(byName.get(one) ?? [], below)) {
|
|
796
|
+
const key = declarationKey(dependency);
|
|
797
|
+
needed.add(key);
|
|
798
|
+
if (unheld) {
|
|
799
|
+
live.add(key);
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
const held = holders.get(key) ?? new Set();
|
|
803
|
+
owners.forEach((owner) => held.add(owner));
|
|
804
|
+
holders.set(key, held);
|
|
673
805
|
}
|
|
674
806
|
}
|
|
675
807
|
}
|
|
676
808
|
}
|
|
677
809
|
}
|
|
678
810
|
addNeededPeers(declared, peers, needed, ran);
|
|
811
|
+
addNeededPeers(declared, peers, live, ran);
|
|
812
|
+
const heldBy = new Map();
|
|
813
|
+
for (const [key, held] of holders) {
|
|
814
|
+
if (!live.has(key) && !ran.has(key)) {
|
|
815
|
+
heldBy.set(key, [...held].sort((a, b) => (a < b ? -1 : 1)));
|
|
816
|
+
}
|
|
817
|
+
}
|
|
679
818
|
const users = new Map();
|
|
680
819
|
for (const used of uses.values()) {
|
|
681
820
|
used.forEach((one) => users.set(one, (users.get(one) ?? 0) + 1));
|
|
@@ -688,5 +827,21 @@ export function dependenciesOf(host, targetRoot, perProject, candidates, members
|
|
|
688
827
|
lastUses.set(id, sole.sort((a, b) => (a < b ? -1 : 1)));
|
|
689
828
|
}
|
|
690
829
|
}
|
|
691
|
-
|
|
830
|
+
const owners = new Map();
|
|
831
|
+
for (const [owner, used] of uses) {
|
|
832
|
+
for (const one of used) {
|
|
833
|
+
if (names.has(one)) {
|
|
834
|
+
owners.set(one, (owners.get(one) ?? new Set()).add(owner));
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
const fileLastUses = new Map();
|
|
839
|
+
for (const [one, held] of [...owners].sort(([a], [b]) => (a < b ? -1 : 1))) {
|
|
840
|
+
const paths = new Set([...held].map((owner) => owner.replace(/:\d+:\d+$/u, "")));
|
|
841
|
+
const [path] = paths;
|
|
842
|
+
if (paths.size === 1 && path !== undefined && path !== PROJECT_OWNER) {
|
|
843
|
+
fileLastUses.set(path, [...(fileLastUses.get(path) ?? []), one]);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return { declared, needed, ran, unbuilt, lastUses, fileLastUses, heldBy };
|
|
692
847
|
}
|