@cplieger/deadset-ts 8.0.0-dev.16 → 8.0.0-dev.17
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/src/alias-chain.js +0 -5
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +58 -22
- package/dist/src/augmentations.js +23 -24
- package/dist/src/bindings.js +382 -0
- package/dist/src/calls.js +25 -8
- package/dist/src/commands/tools.js +5 -9
- package/dist/src/component-files.js +3 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +6 -9
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +4 -1
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +25 -29
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +2 -2
- package/dist/src/entries/configuration-files.js +3 -7
- package/dist/src/entries/conventions.js +2 -2
- package/dist/src/entries/html-entries.js +7 -10
- package/dist/src/entries/mdx-entries.js +2 -2
- package/dist/src/entries/static-module.js +2 -1
- package/dist/src/facts.js +4 -4
- package/dist/src/findings/completion.js +40 -4
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
- package/dist/src/framework-lifecycle.js +41 -14
- package/dist/src/graph.js +12 -4
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +5 -0
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/matrix.js +6 -4
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +46 -7
- package/dist/src/platform-calls.js +2 -2
- package/dist/src/platform-symbols.js +13 -19
- package/dist/src/references/read-rule.js +1 -0
- package/dist/src/references/read-rules.js +48 -0
- package/dist/src/references/sink.js +151 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +110 -1285
- package/dist/src/roots/configured-declarations.js +3 -0
- package/dist/src/roots/root-set.js +1 -1
- package/dist/src/roots/unseen-loads.js +38 -10
- package/dist/src/serialization-contract.js +8 -3
- package/dist/src/stores.js +4 -2
- package/dist/src/sweep.js +25 -13
- package/dist/src/symbol-keys.js +89 -83
- package/dist/src/tests/file-class.js +76 -0
- package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
- package/dist/src/{test-support.js → tests/support.js} +2 -11
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +3 -3
- package/dist/src/verbs/explain.js +50 -28
- package/dist/src/verbs/print-retained.js +2 -2
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +86 -23
- package/src/augmentations.ts +41 -27
- package/src/bindings.ts +460 -0
- package/src/calls.ts +25 -7
- package/src/commands/tools.ts +5 -11
- package/src/component-files.ts +4 -2
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +8 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +4 -1
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +27 -28
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +2 -2
- package/src/entries/configuration-files.ts +11 -8
- package/src/entries/conventions.ts +2 -1
- package/src/entries/html-entries.ts +15 -14
- package/src/entries/mdx-entries.ts +2 -2
- package/src/entries/static-module.ts +2 -1
- package/src/facts.ts +5 -3
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +43 -4
- package/src/findings/emitter.ts +1 -1
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/framework-lifecycle.ts +42 -14
- package/src/graph.ts +24 -6
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/indexed-access.ts +570 -527
- package/src/interface-satisfaction.ts +1 -1
- package/src/keyof-comparisons.ts +131 -116
- package/src/load/expected-module.ts +8 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +6 -0
- package/src/load/workspace-resolution.ts +9 -2
- package/src/matrix.ts +6 -3
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +54 -8
- package/src/platform-calls.ts +3 -3
- package/src/platform-symbols.ts +11 -27
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +213 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +168 -1733
- package/src/report.ts +1 -1
- package/src/roots/configured-declarations.ts +3 -0
- package/src/roots/root-set.ts +1 -1
- package/src/roots/unseen-loads.ts +49 -11
- package/src/serialization-contract.ts +8 -3
- package/src/stores.ts +13 -2
- package/src/sweep.ts +37 -14
- package/src/symbol-keys.ts +102 -102
- package/src/tests/file-class.ts +118 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +9 -20
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +3 -2
- package/src/verbs/explain.ts +74 -31
- package/src/verbs/print-retained.ts +2 -2
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { isCallExpression, isClassDeclaration, isClassExpression, isClassStaticBlockDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isHeritageClause, isMethodDeclaration, isNewExpression, isObjectLiteralExpression, isSetAccessorDeclaration, isTypeLiteralNode, ModifierFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
11
11
|
import { aliasChains } from "./alias-chain.js";
|
|
12
|
-
import { emptyImplementation } from "./implementations.js";
|
|
12
|
+
import { emptyImplementation } from "./findings/implementations.js";
|
|
13
13
|
import { nodeKey } from "./inventory.js";
|
|
14
14
|
import { renderPosition } from "./position.js";
|
|
15
15
|
import { must } from "./query.js";
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
* that holds `keyof X`, the literal the other side has names the member of `X` under that
|
|
5
5
|
* key, because deleting the member makes the comparison a type error.
|
|
6
6
|
*/
|
|
7
|
-
import { isBinaryExpression, isCaseClause, isElementAccessExpression, isIdentifier, isPropertyAccessExpression, isSwitchStatement, isTypeOperatorNode, SyntaxKind, } from "@typescript/native/ast";
|
|
7
|
+
import { isBinaryExpression, isCaseClause, isElementAccessExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isPropertyAccessExpression, isStringLiteral, isSwitchStatement, isTypeOperatorNode, SyntaxKind, } from "@typescript/native/ast";
|
|
8
8
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
9
|
-
import { nodeKey } from "./inventory.js";
|
|
10
|
-
import { renderPosition } from "./position.js";
|
|
11
9
|
import { UNANSWERED } from "./query.js";
|
|
12
10
|
import { parenthesesRemoved } from "./unwrap.js";
|
|
13
11
|
/** The operators that compare their operands for equality. */
|
|
@@ -55,114 +53,137 @@ function literalNames(parts) {
|
|
|
55
53
|
return names;
|
|
56
54
|
}
|
|
57
55
|
/**
|
|
58
|
-
* The
|
|
59
|
-
*
|
|
56
|
+
* The reads the key comparisons of a side's files make of the members their literal side
|
|
57
|
+
* names, each made by the declaration holding the comparison. A question the checker leaves
|
|
58
|
+
* unanswered holds what its answer could have read.
|
|
60
59
|
*/
|
|
61
|
-
export
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
60
|
+
export const keyofComparisons = {
|
|
61
|
+
start() {
|
|
62
|
+
const comparisons = [];
|
|
63
|
+
return {
|
|
64
|
+
visit(node, { file, from }) {
|
|
65
|
+
if (isBinaryExpression(node) && EQUALITIES.has(node.operatorToken.kind)) {
|
|
66
|
+
comparisons.push({ file, from, keyed: node.left, other: node.right });
|
|
67
|
+
comparisons.push({ file, from, keyed: node.right, other: node.left });
|
|
68
|
+
}
|
|
69
|
+
if (isSwitchStatement(node)) {
|
|
70
|
+
for (const clause of node.caseBlock.clauses) {
|
|
71
|
+
if (isCaseClause(clause)) {
|
|
72
|
+
comparisons.push({ file, from, keyed: node.expression, other: clause.expression });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
reads({ project, chains }) {
|
|
78
|
+
const queries = project.queries;
|
|
79
|
+
const found = [];
|
|
80
|
+
const named = comparisons.flatMap((one) => {
|
|
81
|
+
const name = nameOf(one.keyed);
|
|
82
|
+
return name === undefined || isElementAccessExpression(parenthesesRemoved(one.other))
|
|
83
|
+
? []
|
|
84
|
+
: [{ ...one, name }];
|
|
85
|
+
});
|
|
86
|
+
if (named.length === 0) {
|
|
87
|
+
return found;
|
|
88
|
+
}
|
|
89
|
+
// The operand's own declaration states the key type, which the checker resolves to
|
|
90
|
+
// the literals it admits and so no longer names the type the key indexes.
|
|
91
|
+
const symbols = queries.symbolsAt(named.map((one) => one.name));
|
|
92
|
+
const keyed = named.flatMap((one, at) => {
|
|
93
|
+
const asked = symbols[at];
|
|
94
|
+
const symbol = asked === UNANSWERED || asked === undefined || (asked.flags & SymbolFlags.Alias) === 0
|
|
95
|
+
? asked
|
|
96
|
+
: queries.aliased(asked);
|
|
97
|
+
if (symbol === UNANSWERED) {
|
|
98
|
+
const text = literalText(one.other);
|
|
99
|
+
found.push({
|
|
100
|
+
kind: "held",
|
|
101
|
+
from: one.from,
|
|
102
|
+
...(text === undefined ? {} : { names: [text] }),
|
|
83
103
|
});
|
|
104
|
+
return [];
|
|
84
105
|
}
|
|
106
|
+
if (symbol === undefined) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const operands = symbol.declarations.flatMap((handle) => {
|
|
110
|
+
const declared = project.declarationAt(handle)?.node?.type;
|
|
111
|
+
return declared === undefined ? [] : keyofOperands(declared);
|
|
112
|
+
});
|
|
113
|
+
return operands.length === 0 ? [] : [{ ...one, operands }];
|
|
114
|
+
});
|
|
115
|
+
if (keyed.length === 0) {
|
|
116
|
+
return found;
|
|
85
117
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
const named = comparisons.flatMap((one) => {
|
|
96
|
-
const name = nameOf(one.keyed);
|
|
97
|
-
return name === undefined || isElementAccessExpression(parenthesesRemoved(one.other))
|
|
98
|
-
? []
|
|
99
|
-
: [{ ...one, name }];
|
|
100
|
-
});
|
|
101
|
-
if (named.length === 0) {
|
|
102
|
-
return [];
|
|
103
|
-
}
|
|
104
|
-
// The operand's own declaration states the key type, which the checker resolves to
|
|
105
|
-
// the literals it admits and so no longer names the type the key indexes.
|
|
106
|
-
const symbols = queries.symbolsAt(named.map((one) => one.name));
|
|
107
|
-
const keyed = named.flatMap((one, at) => {
|
|
108
|
-
const asked = symbols[at];
|
|
109
|
-
const symbol = asked === UNANSWERED || asked === undefined || (asked.flags & SymbolFlags.Alias) === 0
|
|
110
|
-
? asked
|
|
111
|
-
: queries.aliased(asked);
|
|
112
|
-
if (symbol === UNANSWERED || symbol === undefined) {
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
const operands = symbol.declarations.flatMap((handle) => {
|
|
116
|
-
const declared = project.declarationAt(handle)?.node?.type;
|
|
117
|
-
return declared === undefined ? [] : keyofOperands(declared);
|
|
118
|
-
});
|
|
119
|
-
return operands.length === 0 ? [] : [{ ...one, operands }];
|
|
120
|
-
});
|
|
121
|
-
if (keyed.length === 0) {
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
const nodes = keyed.flatMap((one) => [one.other, ...one.operands]);
|
|
125
|
-
const types = queries.typesAt(nodes);
|
|
126
|
-
const typeOf = new Map();
|
|
127
|
-
nodes.forEach((node, at) => {
|
|
128
|
-
const type = types[at];
|
|
129
|
-
if (type !== UNANSWERED && type !== undefined) {
|
|
130
|
-
typeOf.set(node, type);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
const partsOf = (type) => {
|
|
134
|
-
const parts = queries.constituents(type);
|
|
135
|
-
return parts === UNANSWERED ? [type] : parts;
|
|
136
|
-
};
|
|
137
|
-
const found = [];
|
|
138
|
-
for (const one of keyed) {
|
|
139
|
-
const literal = typeOf.get(one.other);
|
|
140
|
-
const names = literal === undefined ? [] : literalNames(partsOf(literal));
|
|
141
|
-
if (names.length === 0) {
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const position = renderPosition(one.file, root, one.other.getStart());
|
|
145
|
-
for (const operand of one.operands) {
|
|
146
|
-
const object = typeOf.get(operand);
|
|
147
|
-
for (const part of object === undefined ? [] : partsOf(object)) {
|
|
148
|
-
const properties = queries.propertiesOf(part);
|
|
149
|
-
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
150
|
-
if (!names.includes(property.name)) {
|
|
151
|
-
continue;
|
|
118
|
+
const nodes = keyed.flatMap((one) => [one.other, ...one.operands]);
|
|
119
|
+
const types = queries.typesAt(nodes);
|
|
120
|
+
const typeOf = new Map();
|
|
121
|
+
nodes.forEach((node, at) => {
|
|
122
|
+
const type = types[at];
|
|
123
|
+
if (type !== undefined) {
|
|
124
|
+
typeOf.set(node, type);
|
|
152
125
|
}
|
|
153
|
-
|
|
126
|
+
});
|
|
127
|
+
const partsOf = (type) => {
|
|
128
|
+
const parts = queries.constituents(type);
|
|
129
|
+
return parts === UNANSWERED ? [type] : parts;
|
|
130
|
+
};
|
|
131
|
+
for (const one of keyed) {
|
|
132
|
+
const literal = typeOf.get(one.other);
|
|
133
|
+
const objects = one.operands.map((operand) => typeOf.get(operand));
|
|
134
|
+
if (literal === UNANSWERED) {
|
|
135
|
+
const known = objects.filter((type) => type !== undefined && type !== UNANSWERED);
|
|
154
136
|
found.push({
|
|
137
|
+
kind: "held",
|
|
155
138
|
from: one.from,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
use: "read",
|
|
159
|
-
resolution: "keyed",
|
|
160
|
-
test: testFiles.has(position.path),
|
|
139
|
+
types: known.flatMap(partsOf),
|
|
140
|
+
...(known.length < objects.length ? { members: true } : {}),
|
|
161
141
|
});
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const names = literal === undefined ? [] : literalNames(partsOf(literal));
|
|
145
|
+
if (names.length === 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
for (const object of objects) {
|
|
149
|
+
if (object === UNANSWERED) {
|
|
150
|
+
found.push({ kind: "held", from: one.from, names });
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
for (const part of object === undefined ? [] : partsOf(object)) {
|
|
154
|
+
const properties = queries.propertiesOf(part);
|
|
155
|
+
if (properties === UNANSWERED) {
|
|
156
|
+
found.push({ kind: "held", from: one.from, names });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
for (const property of properties) {
|
|
160
|
+
if (!names.includes(property.name)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
for (const to of chains.declarationsOf(property)) {
|
|
164
|
+
found.push({
|
|
165
|
+
kind: "named",
|
|
166
|
+
from: one.from,
|
|
167
|
+
to,
|
|
168
|
+
file: one.file,
|
|
169
|
+
at: one.other,
|
|
170
|
+
use: "read",
|
|
171
|
+
via: "keyed",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
162
176
|
}
|
|
163
177
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
return found;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
/** The text a string or number literal operand spells, where it is one. */
|
|
184
|
+
function literalText(node) {
|
|
185
|
+
const at = parenthesesRemoved(node);
|
|
186
|
+
return isStringLiteral(at) || isNumericLiteral(at) || isNoSubstitutionTemplateLiteral(at)
|
|
187
|
+
? at.text
|
|
188
|
+
: undefined;
|
|
168
189
|
}
|
|
@@ -2,7 +2,7 @@ import { isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/n
|
|
|
2
2
|
import { packageOfSpecifier } from "../dependencies.js";
|
|
3
3
|
import { MANIFEST } from "../fs/manifest.js";
|
|
4
4
|
import { entriesOf } from "../fs/tree.js";
|
|
5
|
-
import {
|
|
5
|
+
import { cannotStartPackageName, dirnamePath, joinPath, relativePath, resolvePath, } from "../paths.js";
|
|
6
6
|
import { dependencySpecifiers, installedPackage, manifestsAbove } from "./module-lookup.js";
|
|
7
7
|
/** The string literal holding one offset, innermost first, a doc comment's included. */
|
|
8
8
|
function literalAt(file, offset) {
|
|
@@ -77,7 +77,7 @@ function declaringManifest(host, dir, targetRoot, name) {
|
|
|
77
77
|
function expectedModule(context, importer, specifier) {
|
|
78
78
|
const { host, targetRoot } = context;
|
|
79
79
|
const fix = "run the generator or the build that writes it";
|
|
80
|
-
if (
|
|
80
|
+
if (cannotStartPackageName(specifier)) {
|
|
81
81
|
return provided(host, resolvePath(dirnamePath(importer), specifier), false)
|
|
82
82
|
? undefined
|
|
83
83
|
: `which names no file of the target: ${fix}`;
|
|
@@ -9,12 +9,10 @@ import { isBinaryExpression, isCallExpression, isParenthesizedExpression, isStri
|
|
|
9
9
|
import { packageOfSpecifier } from "../dependencies.js";
|
|
10
10
|
import { readManifestAt } from "../fs/manifest.js";
|
|
11
11
|
import { JAVASCRIPT_FILE } from "../javascript-file.js";
|
|
12
|
-
import { ancestors, dirnamePath, relativePath, resolvePath } from "../paths.js";
|
|
12
|
+
import { ancestors, dirnamePath, isRelativeSpecifier, relativePath, resolvePath, } from "../paths.js";
|
|
13
13
|
import { renderPosition } from "../position.js";
|
|
14
14
|
import { batched } from "../session.js";
|
|
15
15
|
import { dependencySpecifiers } from "./module-lookup.js";
|
|
16
|
-
/** A relative specifier: `.`, `..`, or one starting `./` or `../`. */
|
|
17
|
-
const RELATIVE = /^\.\.?(?:\/|$)/u;
|
|
18
16
|
/**
|
|
19
17
|
* Whether one specifier is loaded optionally: written inside a `try` block, or as a call whose
|
|
20
18
|
* result an `||` or a `??` falls back from, so the program is written for its absence.
|
|
@@ -87,7 +85,7 @@ export function readSpecifiers(engine, host, project, { checked, root, outputs }
|
|
|
87
85
|
const resolved = (answers[at]?.resolvedFileName ?? "") !== "";
|
|
88
86
|
const specifier = usage.node.text;
|
|
89
87
|
const name = packageOfSpecifier(specifier);
|
|
90
|
-
if (
|
|
88
|
+
if (isRelativeSpecifier(specifier)) {
|
|
91
89
|
const named = resolvePath(dirnamePath(usage.file.fileName), specifier);
|
|
92
90
|
if (!resolved && !outputs.some((dir) => relativePath(dir, named) !== undefined)) {
|
|
93
91
|
unresolved.push({
|
|
@@ -89,6 +89,11 @@ const RUNTIME_OPTIONS = {
|
|
|
89
89
|
bundler: { module: "preserve", moduleResolution: "bundler" },
|
|
90
90
|
node: { module: "nodenext", moduleResolution: "nodenext" },
|
|
91
91
|
};
|
|
92
|
+
/** Whether one configuration is a program the run formed to resolve its modules as Node does. */
|
|
93
|
+
export function formedForNode(configuration) {
|
|
94
|
+
const options = configuration.written?.["compilerOptions"];
|
|
95
|
+
return isRecord(options) && options["moduleResolution"] === RUNTIME_OPTIONS.node.moduleResolution;
|
|
96
|
+
}
|
|
92
97
|
/** The manifest members that say a package is written for Node. */
|
|
93
98
|
const NODE_MEMBERS = ["main", "exports", "bin", "type"];
|
|
94
99
|
/**
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { emittedFrom } from "./emit-map.js";
|
|
9
9
|
import { packageOfSpecifier } from "../dependencies.js";
|
|
10
10
|
import { gitIgnored } from "../git-ignore.js";
|
|
11
|
-
import { ancestors, dirnamePath, joinPath, relativePath, resolvePath } from "../paths.js";
|
|
11
|
+
import { ancestors, cannotStartPackageName, dirnamePath, joinPath, relativePath, resolvePath, } from "../paths.js";
|
|
12
12
|
import { satisfies } from "../version-range.js";
|
|
13
13
|
import { isRecord } from "../json-document.js";
|
|
14
14
|
import { MANIFEST } from "../fs/manifest.js";
|
|
@@ -99,7 +99,7 @@ function dependsOn(specifier, member, fromDir) {
|
|
|
99
99
|
const inRange = (range) => member.version !== undefined && satisfies(member.version, range);
|
|
100
100
|
if (specifier.startsWith("workspace:")) {
|
|
101
101
|
const rest = specifier.slice("workspace:".length);
|
|
102
|
-
if (
|
|
102
|
+
if (cannotStartPackageName(rest)) {
|
|
103
103
|
return atPath(rest);
|
|
104
104
|
}
|
|
105
105
|
const alias = /^((?:@[^/@]+\/)?[^/@]+)@(.*)$/u.exec(rest);
|
package/dist/src/matrix.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import { componentsOf } from "./components.js";
|
|
7
7
|
import { graphOf, OUTSIDE } from "./graph.js";
|
|
8
8
|
import { byPosition } from "./position.js";
|
|
9
|
-
import {
|
|
9
|
+
import { edgeWithheldBy } from "./tests/file-class.js";
|
|
10
|
+
import { reachedWithTheTests, splitRefs, sweep, } from "./sweep.js";
|
|
10
11
|
/**
|
|
11
12
|
* What makes one reference one reference, whichever configuration made it. A consumer's
|
|
12
13
|
* position is rendered against its own root, so the consumer is part of what it names.
|
|
@@ -133,12 +134,13 @@ export function sweepMatrix(matrix, input) {
|
|
|
133
134
|
}
|
|
134
135
|
const held = matrix.heldIn[at] ?? [];
|
|
135
136
|
const admitted = admittedAt(at);
|
|
136
|
-
const
|
|
137
|
+
const split = splitRefs(union.made[at], input.mode);
|
|
138
|
+
const testRefs = split.test;
|
|
137
139
|
const heldByTests = heldBy[union.parent[at] ?? OUTSIDE] ??
|
|
138
140
|
(union.support[at] === true && testRefs > 0 && !admitted ? symbol.id : undefined);
|
|
139
141
|
const unreferenced = union.test[at] === true && !admitted && testReached[at] !== true;
|
|
140
142
|
heldBy[at] = heldByTests;
|
|
141
|
-
const productionRefs =
|
|
143
|
+
const productionRefs = split.production;
|
|
142
144
|
const counted = all.every((candidate) => candidate.relation === "reference-counting");
|
|
143
145
|
candidates.push({
|
|
144
146
|
id: symbol.id,
|
|
@@ -181,7 +183,7 @@ function compiledReferences(matrix, dead, mode) {
|
|
|
181
183
|
}
|
|
182
184
|
for (const edge of edges) {
|
|
183
185
|
const to = graph.symbols[edge.to]?.id ?? "";
|
|
184
|
-
if ((
|
|
186
|
+
if (!edgeWithheldBy(mode, edge) && dead.has(to) && !kept.has(to)) {
|
|
185
187
|
kept.add(to);
|
|
186
188
|
changed = true;
|
|
187
189
|
}
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* to the same module. One that names it by name or pattern also reads the members it
|
|
6
6
|
* imports, whatever file the specifier resolves to, as a compiler reading no such file does.
|
|
7
7
|
*/
|
|
8
|
-
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isImportTypeNode, isLiteralTypeNode, isModuleDeclaration,
|
|
8
|
+
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isImportTypeNode, isLiteralTypeNode, isModuleDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
9
|
+
import { namesImported } from "./import-bindings.js";
|
|
9
10
|
import { nodeKey } from "./inventory.js";
|
|
10
|
-
import { renderPosition } from "./position.js";
|
|
11
11
|
import { UNANSWERED } from "./query.js";
|
|
12
12
|
import { aliasFragment, nameComponent } from "./ref.js";
|
|
13
|
+
import { isPathSpecifier } from "./paths.js";
|
|
13
14
|
/** Whether a node is a specifier's text: a string literal or a template with no substitution. */
|
|
14
15
|
function literalText(node) {
|
|
15
16
|
return node !== undefined && (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node));
|
|
@@ -36,32 +37,12 @@ function specifierOf(node) {
|
|
|
36
37
|
}
|
|
37
38
|
return undefined;
|
|
38
39
|
}
|
|
39
|
-
/**
|
|
40
|
-
* The export names an import or a re-export reads from the module it names: none for a
|
|
41
|
-
* namespace, a star or a bare form.
|
|
42
|
-
*/
|
|
43
|
-
export function namesImported(node) {
|
|
44
|
-
if (isImportDeclaration(node)) {
|
|
45
|
-
const clause = node.importClause;
|
|
46
|
-
const bindings = clause?.namedBindings;
|
|
47
|
-
return [
|
|
48
|
-
...(clause?.name === undefined ? [] : ["default"]),
|
|
49
|
-
...(bindings === undefined || isNamespaceImport(bindings)
|
|
50
|
-
? []
|
|
51
|
-
: bindings.elements.map((one) => (one.propertyName ?? one.name).text)),
|
|
52
|
-
];
|
|
53
|
-
}
|
|
54
|
-
const clause = isExportDeclaration(node) ? node.exportClause : undefined;
|
|
55
|
-
return clause === undefined || isNamespaceExport(clause)
|
|
56
|
-
? []
|
|
57
|
-
: clause.elements.map((one) => (one.propertyName ?? one.name).text);
|
|
58
|
-
}
|
|
59
40
|
/**
|
|
60
41
|
* Whether a specifier matches a declaration's name, the name's one `*` standing for any run.
|
|
61
42
|
* A relative name is resolved against its own file, so no specifier names it by its text.
|
|
62
43
|
*/
|
|
63
44
|
function namesModule(specifier, name) {
|
|
64
|
-
if (
|
|
45
|
+
if (isPathSpecifier(name)) {
|
|
65
46
|
return false;
|
|
66
47
|
}
|
|
67
48
|
const star = name.indexOf("*");
|
|
@@ -107,67 +88,71 @@ function namedBy(held) {
|
|
|
107
88
|
});
|
|
108
89
|
}
|
|
109
90
|
/**
|
|
110
|
-
* The
|
|
111
|
-
*
|
|
91
|
+
* The reads the imports of a side's files make of the module declarations of the target they
|
|
92
|
+
* name, each at the import's specifier. A specifier the checker leaves unanswered still names
|
|
93
|
+
* a declaration by its name or pattern.
|
|
112
94
|
*/
|
|
113
|
-
export
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const visit = (node, from) => {
|
|
122
|
-
const own = held.declarations.get(nodeKey(file, node));
|
|
123
|
-
if (isModuleDeclaration(node) && literalText(node.name) && own !== undefined) {
|
|
124
|
-
declared.push({ id: own, name: node.name });
|
|
125
|
-
}
|
|
126
|
-
const specifier = specifierOf(node);
|
|
127
|
-
if (specifier !== undefined) {
|
|
128
|
-
imports.push({ file, from, specifier, names: namesImported(node) });
|
|
129
|
-
}
|
|
130
|
-
node.forEachChild((child) => {
|
|
131
|
-
visit(child, own ?? from);
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
file.forEachChild((child) => {
|
|
135
|
-
visit(child, fileId);
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
if (declared.length === 0 || imports.length === 0) {
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
const asked = [...declared.map((one) => one.name), ...imports.map((one) => one.specifier)];
|
|
142
|
-
const answers = project.symbolsAt(asked.map((node) => project.handle(node)));
|
|
143
|
-
const moduleOf = (at) => {
|
|
144
|
-
const symbol = answers[at];
|
|
145
|
-
return symbol === undefined || symbol === UNANSWERED ? undefined : symbol.id;
|
|
146
|
-
};
|
|
147
|
-
const named = namedBy(held);
|
|
148
|
-
const found = [];
|
|
149
|
-
imports.forEach((one, index) => {
|
|
150
|
-
const resolved = moduleOf(declared.length + index);
|
|
151
|
-
const position = renderPosition(one.file, root, one.specifier.getStart());
|
|
152
|
-
const read = (to, resolution, test) => {
|
|
153
|
-
found.push({ from: one.from, to, position, use: "read", resolution, test });
|
|
154
|
-
};
|
|
155
|
-
const test = testFiles.has(position.path);
|
|
156
|
-
const same = new Set(declared
|
|
157
|
-
.filter((_, at) => resolved !== undefined && resolved === moduleOf(at))
|
|
158
|
-
.map((declaration) => declaration.id));
|
|
159
|
-
for (const id of same) {
|
|
160
|
-
read(id, "batch", test);
|
|
161
|
-
}
|
|
162
|
-
for (const declaration of named(one.specifier.text, one.names)) {
|
|
163
|
-
// A wildcard declaration is how the compiler types the file a matching specifier
|
|
164
|
-
// names, so the import uses it whoever writes it, a test included.
|
|
165
|
-
if (declaration.wildcard || !same.has(declaration.id)) {
|
|
166
|
-
for (const to of [declaration.id, ...declaration.members]) {
|
|
167
|
-
read(to, "syntax", test && !declaration.wildcard);
|
|
95
|
+
export const moduleDeclarations = {
|
|
96
|
+
start() {
|
|
97
|
+
const imports = [];
|
|
98
|
+
return {
|
|
99
|
+
visit(node, { file, from }) {
|
|
100
|
+
const specifier = specifierOf(node);
|
|
101
|
+
if (specifier !== undefined) {
|
|
102
|
+
imports.push({ file, from, specifier, names: namesImported(node) });
|
|
168
103
|
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
104
|
+
},
|
|
105
|
+
reads({ project, held }) {
|
|
106
|
+
const found = [];
|
|
107
|
+
// A module declaration whose name is a string is written at a file's top level.
|
|
108
|
+
const declared = project.ownSourceFiles().flatMap((file) => file.statements.flatMap((statement) => {
|
|
109
|
+
const id = held.declarations.get(nodeKey(file, statement));
|
|
110
|
+
return isModuleDeclaration(statement) && literalText(statement.name) && id !== undefined
|
|
111
|
+
? [{ id, name: statement.name }]
|
|
112
|
+
: [];
|
|
113
|
+
}));
|
|
114
|
+
if (declared.length === 0 || imports.length === 0) {
|
|
115
|
+
return found;
|
|
116
|
+
}
|
|
117
|
+
const asked = [...declared.map((one) => one.name), ...imports.map((one) => one.specifier)];
|
|
118
|
+
const answers = project.symbolsAt(asked.map((node) => project.handle(node)));
|
|
119
|
+
const moduleOf = (at) => {
|
|
120
|
+
const symbol = answers[at];
|
|
121
|
+
return symbol === undefined || symbol === UNANSWERED ? undefined : symbol.id;
|
|
122
|
+
};
|
|
123
|
+
const named = namedBy(held);
|
|
124
|
+
imports.forEach((one, index) => {
|
|
125
|
+
const resolved = moduleOf(declared.length + index);
|
|
126
|
+
const read = (to, via, production) => {
|
|
127
|
+
found.push({
|
|
128
|
+
kind: "named",
|
|
129
|
+
from: one.from,
|
|
130
|
+
to,
|
|
131
|
+
file: one.file,
|
|
132
|
+
at: one.specifier,
|
|
133
|
+
use: "read",
|
|
134
|
+
via,
|
|
135
|
+
...(production ? { production: true } : {}),
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
const same = new Set(declared
|
|
139
|
+
.filter((_, at) => resolved !== undefined && resolved === moduleOf(at))
|
|
140
|
+
.map((declaration) => declaration.id));
|
|
141
|
+
for (const id of same) {
|
|
142
|
+
read(id, "batch", false);
|
|
143
|
+
}
|
|
144
|
+
for (const declaration of named(one.specifier.text, one.names)) {
|
|
145
|
+
// A wildcard declaration is how the compiler types the file a matching specifier
|
|
146
|
+
// names, so the import uses it whoever writes it, a test included.
|
|
147
|
+
if (declaration.wildcard || !same.has(declaration.id)) {
|
|
148
|
+
for (const to of [declaration.id, ...declaration.members]) {
|
|
149
|
+
read(to, "syntax", declaration.wildcard);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
return found;
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
};
|