@cplieger/deadset-ts 8.0.0-dev.15 → 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 +94 -54
- 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/read.js +6 -7
- 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 +9 -10
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +5 -2
- package/dist/src/contract/contract.generated.js +119 -2
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +98 -71
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +22 -64
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
- package/dist/src/entries/conventions.js +739 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +9 -12
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +3 -3
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +670 -0
- package/dist/src/entries/test-runners.js +684 -0
- package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/findings/completion.js +40 -4
- package/dist/src/findings/emitters.js +5 -8
- 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/findings/self-check.js +36 -20
- package/dist/src/findings-pass.js +5 -6
- package/dist/src/framework-lifecycle.js +42 -15
- package/dist/src/fs/manifest.js +15 -20
- package/dist/src/generated-file.js +172 -29
- package/dist/src/graph.js +14 -8
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/inventory/commonjs.js +62 -17
- package/dist/src/inventory/enumerate.js +2 -1
- package/dist/src/javascript-file.js +26 -11
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/load/classify.js +28 -9
- package/dist/src/load/diagnostics.js +8 -1
- package/dist/src/load/discover.js +15 -7
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/plan.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +10 -5
- package/dist/src/load/workspace-manifests.js +1 -1
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/matrix.js +12 -17
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +56 -1
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +134 -0
- 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 +111 -1116
- package/dist/src/resolve.js +1 -1
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +7 -4
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
- package/dist/src/{manifest.js → roots/manifest.js} +4 -4
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +489 -0
- package/dist/src/roots/unseen-loads.js +236 -0
- package/dist/src/serialization-contract.js +9 -4
- package/dist/src/source-files.js +5 -9
- package/dist/src/stores.js +4 -2
- package/dist/src/suppress.js +30 -32
- 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} +5 -12
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +15 -3
- package/dist/src/verbs/describe.js +5 -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 +3 -3
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +156 -70
- package/src/augmentations.ts +41 -27
- package/src/automatic-types.ts +1 -1
- package/src/bindings.ts +460 -0
- package/src/calls.ts +25 -7
- package/src/commands/read.ts +6 -8
- 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 +12 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +5 -2
- package/src/contract/contract.generated.ts +138 -2
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +117 -71
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +33 -82
- package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
- package/src/{conventions.ts → entries/conventions.ts} +349 -523
- package/src/{html-entries.ts → entries/html-entries.ts} +19 -18
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +5 -5
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +858 -0
- package/src/entries/test-runners.ts +868 -0
- package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
- package/src/exempt.ts +3 -2
- package/src/extensions.ts +45 -0
- package/src/facts.ts +46 -0
- package/src/file-facts.ts +1 -1
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +43 -4
- package/src/findings/emitter.ts +1 -1
- package/src/findings/emitters.ts +14 -10
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/findings/self-check.ts +41 -22
- package/src/findings/visibility-narrowing.ts +1 -1
- package/src/findings-pass.ts +5 -12
- package/src/framework-lifecycle.ts +43 -15
- package/src/fs/manifest.ts +25 -21
- package/src/generated-file.ts +240 -36
- package/src/graph.ts +27 -11
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +570 -527
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +1 -1
- package/src/inventory/commonjs.ts +102 -17
- package/src/inventory/enumerate.ts +2 -1
- package/src/javascript-file.ts +29 -12
- package/src/keyof-comparisons.ts +131 -116
- package/src/load/classify.ts +49 -13
- package/src/load/diagnostics.ts +11 -1
- package/src/load/discover.ts +27 -11
- package/src/load/expected-module.ts +8 -2
- package/src/load/plan.ts +11 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +12 -6
- package/src/load/test-globals.ts +1 -1
- package/src/load/workspace-manifests.ts +1 -1
- package/src/load/workspace-resolution.ts +9 -2
- package/src/load/workspace-run.ts +1 -1
- package/src/matrix.ts +13 -17
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +65 -1
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +189 -0
- 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 +174 -1492
- package/src/report.ts +2 -2
- package/src/resolve.ts +1 -1
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +10 -7
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
- package/src/{manifest.ts → roots/manifest.ts} +5 -5
- package/src/roots/published-reach.ts +481 -0
- package/src/{roots.ts → roots/root-set.ts} +203 -630
- package/src/roots/unseen-loads.ts +345 -0
- package/src/serialization-contract.ts +9 -4
- package/src/source-files.ts +5 -10
- package/src/stores.ts +13 -2
- package/src/suppress.ts +48 -33
- 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} +15 -21
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +19 -5
- package/src/verbs/describe.ts +5 -2
- package/src/verbs/explain.ts +75 -32
- package/src/verbs/print-retained.ts +2 -2
- package/dist/src/build-scripts.js +0 -418
- package/dist/src/conventions.js +0 -907
- package/dist/src/entry-points.js +0 -444
- package/dist/src/own-body.js +0 -28
- package/dist/src/roots.js +0 -865
- package/dist/src/unseen-loads.js +0 -189
- package/src/build-scripts.ts +0 -536
- package/src/entry-points.ts +0 -584
- package/src/own-body.ts +0 -42
- package/src/unseen-loads.ts +0 -282
|
@@ -156,8 +156,6 @@ function sourceOf(target) {
|
|
|
156
156
|
export function destructuring(project, cap, properties = propertyTables(project.queries)) {
|
|
157
157
|
const queries = project.queries;
|
|
158
158
|
const tables = new Map();
|
|
159
|
-
let patternBatches = 0;
|
|
160
|
-
let patternLookups = 0;
|
|
161
159
|
const tableOf = (type) => {
|
|
162
160
|
const known = tables.get(type.id);
|
|
163
161
|
if (known !== undefined) {
|
|
@@ -166,7 +164,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
166
164
|
let table = new Map();
|
|
167
165
|
if ((type.flags & TypeFlags.AnyOrUnknown) === 0) {
|
|
168
166
|
const read = properties.of(type);
|
|
169
|
-
patternLookups += read.asked ? 1 : 0;
|
|
170
167
|
table = isAnswered(read.properties)
|
|
171
168
|
? new Map(read.properties.map((property) => [property.name, property]))
|
|
172
169
|
: UNANSWERED;
|
|
@@ -178,7 +175,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
178
175
|
if ((type.flags & TypeFlags.Union) === 0) {
|
|
179
176
|
return type;
|
|
180
177
|
}
|
|
181
|
-
patternLookups += 1;
|
|
182
178
|
return queries.nonNullable(type);
|
|
183
179
|
};
|
|
184
180
|
/**
|
|
@@ -193,11 +189,9 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
193
189
|
if (index === undefined) {
|
|
194
190
|
return undefined;
|
|
195
191
|
}
|
|
196
|
-
patternLookups += 1;
|
|
197
192
|
const elements = queries.typeArguments(type);
|
|
198
193
|
return isAnswered(elements) ? elements[index] : UNANSWERED;
|
|
199
194
|
}
|
|
200
|
-
patternLookups += 1;
|
|
201
195
|
const array = queries.isArray(type);
|
|
202
196
|
if (!isAnswered(array)) {
|
|
203
197
|
return UNANSWERED;
|
|
@@ -205,7 +199,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
205
199
|
if (!array) {
|
|
206
200
|
return undefined;
|
|
207
201
|
}
|
|
208
|
-
patternLookups += 1;
|
|
209
202
|
const elements = queries.typeArguments(type);
|
|
210
203
|
return isAnswered(elements) ? elements[0] : UNANSWERED;
|
|
211
204
|
};
|
|
@@ -222,14 +215,12 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
222
215
|
if (more.length > 0) {
|
|
223
216
|
return UNANSWERED;
|
|
224
217
|
}
|
|
225
|
-
patternLookups += 1;
|
|
226
218
|
return queries.typeOfSymbol(property);
|
|
227
219
|
};
|
|
228
220
|
const resultOf = (callee) => {
|
|
229
221
|
if (callee === undefined || callee === UNANSWERED) {
|
|
230
222
|
return callee;
|
|
231
223
|
}
|
|
232
|
-
patternLookups += 1;
|
|
233
224
|
const signatures = queries.signaturesOf(callee, SignatureKind.Call);
|
|
234
225
|
if (!isAnswered(signatures)) {
|
|
235
226
|
return UNANSWERED;
|
|
@@ -238,7 +229,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
238
229
|
if (signature === undefined) {
|
|
239
230
|
return undefined;
|
|
240
231
|
}
|
|
241
|
-
patternLookups += 1;
|
|
242
232
|
return queries.returnTypeOf(signature);
|
|
243
233
|
};
|
|
244
234
|
const iterations = new Map();
|
|
@@ -266,7 +256,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
266
256
|
if (result !== undefined && result !== UNANSWERED) {
|
|
267
257
|
let parts = [result];
|
|
268
258
|
if (result.isUnionType()) {
|
|
269
|
-
patternLookups += 1;
|
|
270
259
|
parts = queries.constituents(result);
|
|
271
260
|
}
|
|
272
261
|
const values = isAnswered(parts)
|
|
@@ -296,7 +285,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
296
285
|
if (property === undefined) {
|
|
297
286
|
return undefined;
|
|
298
287
|
}
|
|
299
|
-
patternLookups += 1;
|
|
300
288
|
return queries.typeOfSymbol(property);
|
|
301
289
|
}
|
|
302
290
|
return elementOf(defined, next.index);
|
|
@@ -317,7 +305,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
317
305
|
}
|
|
318
306
|
}
|
|
319
307
|
const asked = [...asking];
|
|
320
|
-
patternBatches += Math.ceil(asked.length / cap);
|
|
321
308
|
const answered = new Map();
|
|
322
309
|
queries.typesAt(asked, cap).forEach((type, index) => {
|
|
323
310
|
const node = asked[index];
|
|
@@ -347,10 +334,5 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
347
334
|
return isAnswered(table) ? table.get(read.name) : UNANSWERED;
|
|
348
335
|
});
|
|
349
336
|
};
|
|
350
|
-
return {
|
|
351
|
-
resolve,
|
|
352
|
-
get cost() {
|
|
353
|
-
return { patternBatches, patternLookups };
|
|
354
|
-
},
|
|
355
|
-
};
|
|
337
|
+
return { resolve };
|
|
356
338
|
}
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* target holds, the members are read at each call, by the type of the argument it passes.
|
|
6
6
|
*/
|
|
7
7
|
import { isArrayLiteralExpression, isArrowFunction, isBinaryExpression, isCallExpression, isElementAccessExpression, isForInStatement, isForOfStatement, isFunctionDeclaration, isFunctionExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
8
|
-
import { nodeKey } from "./inventory.js";
|
|
9
|
-
import { renderPosition } from "./position.js";
|
|
10
8
|
import { UNANSWERED } from "./query.js";
|
|
11
9
|
import { parenthesesRemoved } from "./unwrap.js";
|
|
12
10
|
/** The assignment operators that read their target before they write it. */
|
|
@@ -153,174 +151,191 @@ function assigns(body, name) {
|
|
|
153
151
|
return found;
|
|
154
152
|
}
|
|
155
153
|
/**
|
|
156
|
-
* The
|
|
157
|
-
*
|
|
154
|
+
* The reads the element accesses of a side's files make of the members their index's literal
|
|
155
|
+
* type names, each made by the declaration holding the access or the call. A type the checker
|
|
156
|
+
* leaves unanswered holds what its answer could have read.
|
|
158
157
|
*/
|
|
159
|
-
export
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (isCallExpression(node)) {
|
|
175
|
-
calls.set(node, { file, from: holder });
|
|
176
|
-
}
|
|
177
|
-
if (isIdentifier(node)) {
|
|
178
|
-
names.push(node);
|
|
179
|
-
}
|
|
180
|
-
node.forEachChild((child) => {
|
|
181
|
-
visit(child, holder);
|
|
182
|
-
});
|
|
183
|
-
};
|
|
184
|
-
file.forEachChild((child) => {
|
|
185
|
-
visit(child, fileId);
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
if (candidates.length === 0) {
|
|
189
|
-
return [];
|
|
190
|
-
}
|
|
191
|
-
// A parameter index is decided at the calls only where the function is a callable every
|
|
192
|
-
// use of which is a call, so the symbols of the parameters and of every name are read.
|
|
193
|
-
const parameterOf = new Map();
|
|
194
|
-
const byCallable = new Map();
|
|
195
|
-
for (const candidate of candidates) {
|
|
196
|
-
const index = parenthesesRemoved(candidate.access.argumentExpression);
|
|
197
|
-
const fn = isIdentifier(index) ? enclosingFunction(candidate.access) : undefined;
|
|
198
|
-
const callable = fn === undefined ? undefined : callableOf(fn);
|
|
199
|
-
if (callable === undefined || !isIdentifier(index)) {
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
const at = callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === index.text);
|
|
203
|
-
if (at < 0 || assigns(callable.body, index.text)) {
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
parameterOf.set(candidate, { callable, at });
|
|
207
|
-
byCallable.set(callable.name, [...(byCallable.get(callable.name) ?? []), candidate]);
|
|
208
|
-
}
|
|
209
|
-
const callableNames = [...byCallable.keys()];
|
|
210
|
-
const spelled = new Set(callableNames.map((name) => name.text));
|
|
211
|
-
const sameName = names.filter((name) => spelled.has(name.text));
|
|
212
|
-
const symbols = queries.symbolsAt([...callableNames, ...sameName]);
|
|
213
|
-
const symbolOf = new Map();
|
|
214
|
-
[...callableNames, ...sameName].forEach((node, at) => {
|
|
215
|
-
const symbol = symbols[at];
|
|
216
|
-
if (symbol !== UNANSWERED && symbol !== undefined) {
|
|
217
|
-
symbolOf.set(node, symbol);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
const callsOf = new Map();
|
|
221
|
-
for (const name of callableNames) {
|
|
222
|
-
const symbol = symbolOf.get(name);
|
|
223
|
-
let found = symbol === undefined ? undefined : [];
|
|
224
|
-
for (const use of sameName) {
|
|
225
|
-
if (found === undefined) {
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
if (use === name || symbolOf.get(use) !== symbol) {
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
const parent = use.parent;
|
|
232
|
-
found =
|
|
233
|
-
isCallExpression(parent) && parent.expression === use ? [...found, parent] : undefined;
|
|
234
|
-
}
|
|
235
|
-
callsOf.set(name, found);
|
|
236
|
-
}
|
|
237
|
-
const lookups = [];
|
|
238
|
-
for (const candidate of candidates) {
|
|
239
|
-
const deferred = parameterOf.get(candidate);
|
|
240
|
-
const sites = deferred === undefined ? undefined : callsOf.get(deferred.callable.name);
|
|
241
|
-
if (deferred === undefined || sites === undefined) {
|
|
242
|
-
lookups.push({
|
|
243
|
-
file: candidate.file,
|
|
244
|
-
from: candidate.from,
|
|
245
|
-
at: candidate.access.argumentExpression,
|
|
246
|
-
object: candidate.access.expression,
|
|
247
|
-
index: candidate.access.argumentExpression,
|
|
248
|
-
use: candidate.use,
|
|
249
|
-
});
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
// An object that is a parameter too is read as the argument each call passes there.
|
|
253
|
-
const object = parenthesesRemoved(candidate.access.expression);
|
|
254
|
-
const objectAt = isIdentifier(object)
|
|
255
|
-
? deferred.callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === object.text)
|
|
256
|
-
: -1;
|
|
257
|
-
for (const call of sites) {
|
|
258
|
-
const parameter = deferred.callable.parameters[deferred.at];
|
|
259
|
-
const index = call.arguments[deferred.at] ?? parameter?.initializer;
|
|
260
|
-
const site = calls.get(call);
|
|
261
|
-
if (index === undefined || site === undefined) {
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
lookups.push({
|
|
265
|
-
file: site.file,
|
|
266
|
-
from: site.from,
|
|
267
|
-
at: call,
|
|
268
|
-
object: (objectAt < 0 ? undefined : call.arguments[objectAt]) ?? candidate.access.expression,
|
|
269
|
-
index,
|
|
270
|
-
use: candidate.use,
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
const nodes = lookups.flatMap((one) => [one.index, one.object]);
|
|
275
|
-
const types = queries.typesAt(nodes);
|
|
276
|
-
const typeOf = (node, at) => {
|
|
277
|
-
const type = types[at];
|
|
278
|
-
return nodes[at] === node && type !== UNANSWERED ? type : undefined;
|
|
279
|
-
};
|
|
280
|
-
// A type parameter's body serves every instantiation, so it reads what its constraint admits.
|
|
281
|
-
const keysOf = (type) => {
|
|
282
|
-
if (!type.isTypeParameter()) {
|
|
283
|
-
return type;
|
|
284
|
-
}
|
|
285
|
-
const constraint = queries.constraintOf(type);
|
|
286
|
-
return constraint === UNANSWERED || constraint === undefined ? type : constraint;
|
|
287
|
-
};
|
|
288
|
-
const partsOf = (type) => {
|
|
289
|
-
const parts = queries.constituents(type);
|
|
290
|
-
return parts === UNANSWERED ? [type] : parts;
|
|
291
|
-
};
|
|
292
|
-
const found = [];
|
|
293
|
-
lookups.forEach((lookup, at) => {
|
|
294
|
-
const indexType = typeOf(lookup.index, 2 * at);
|
|
295
|
-
const objectType = typeOf(lookup.object, 2 * at + 1);
|
|
296
|
-
const literals = indexType === undefined ? undefined : literalNames(partsOf(keysOf(indexType)));
|
|
297
|
-
if (objectType === undefined || literals === undefined) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
const position = renderPosition(lookup.file, root, lookup.at.getStart());
|
|
301
|
-
for (const part of partsOf(objectType)) {
|
|
302
|
-
const properties = queries.propertiesOf(part);
|
|
303
|
-
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
304
|
-
if (!literals.has(property.escapedName)) {
|
|
305
|
-
continue;
|
|
158
|
+
export const elementAccess = {
|
|
159
|
+
start() {
|
|
160
|
+
const candidates = [];
|
|
161
|
+
const calls = new Map();
|
|
162
|
+
const names = [];
|
|
163
|
+
return {
|
|
164
|
+
visit(node, { file, from }) {
|
|
165
|
+
if (isElementAccessExpression(node) && !isLiteralIndex(node.argumentExpression)) {
|
|
166
|
+
candidates.push({ file, from, access: node, use: useOf(node) });
|
|
167
|
+
}
|
|
168
|
+
if (isCallExpression(node)) {
|
|
169
|
+
calls.set(node, { file, from });
|
|
170
|
+
}
|
|
171
|
+
if (isIdentifier(node)) {
|
|
172
|
+
names.push(node);
|
|
306
173
|
}
|
|
307
|
-
|
|
308
|
-
|
|
174
|
+
},
|
|
175
|
+
reads({ project, chains }) {
|
|
176
|
+
const found = [];
|
|
177
|
+
if (candidates.length === 0) {
|
|
178
|
+
return found;
|
|
179
|
+
}
|
|
180
|
+
const queries = project.queries;
|
|
181
|
+
// A parameter index is decided at the calls only where the function is a callable every
|
|
182
|
+
// use of which is a call, so the symbols of the parameters and of every name are read.
|
|
183
|
+
const parameterOf = new Map();
|
|
184
|
+
const byCallable = new Map();
|
|
185
|
+
for (const candidate of candidates) {
|
|
186
|
+
const index = parenthesesRemoved(candidate.access.argumentExpression);
|
|
187
|
+
const fn = isIdentifier(index) ? enclosingFunction(candidate.access) : undefined;
|
|
188
|
+
const callable = fn === undefined ? undefined : callableOf(fn);
|
|
189
|
+
if (callable === undefined || !isIdentifier(index)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const at = callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === index.text);
|
|
193
|
+
if (at < 0 || assigns(callable.body, index.text)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
parameterOf.set(candidate, { callable, at });
|
|
197
|
+
byCallable.set(callable.name, [...(byCallable.get(callable.name) ?? []), candidate]);
|
|
198
|
+
}
|
|
199
|
+
const callableNames = [...byCallable.keys()];
|
|
200
|
+
const spelled = new Set(callableNames.map((name) => name.text));
|
|
201
|
+
const sameName = names.filter((name) => spelled.has(name.text));
|
|
202
|
+
const asked = [...callableNames, ...sameName];
|
|
203
|
+
const symbols = queries.symbolsAt(asked);
|
|
204
|
+
const symbolOf = new Map();
|
|
205
|
+
asked.forEach((node, at) => {
|
|
206
|
+
const symbol = symbols[at];
|
|
207
|
+
if (symbol !== undefined) {
|
|
208
|
+
symbolOf.set(node, symbol);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
const callsOf = new Map();
|
|
212
|
+
for (const name of callableNames) {
|
|
213
|
+
const symbol = symbolOf.get(name);
|
|
214
|
+
let sites = symbol === undefined || symbol === UNANSWERED ? undefined : [];
|
|
215
|
+
for (const use of sameName) {
|
|
216
|
+
if (sites === undefined) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
const other = symbolOf.get(use);
|
|
220
|
+
if (use === name || (other !== symbol && other !== UNANSWERED)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// A use the checker could not resolve may be the function's, read where it is not called.
|
|
224
|
+
const parent = use.parent;
|
|
225
|
+
sites =
|
|
226
|
+
other !== UNANSWERED && isCallExpression(parent) && parent.expression === use
|
|
227
|
+
? [...sites, parent]
|
|
228
|
+
: undefined;
|
|
229
|
+
}
|
|
230
|
+
callsOf.set(name, sites);
|
|
231
|
+
}
|
|
232
|
+
const lookups = [];
|
|
233
|
+
for (const candidate of candidates) {
|
|
234
|
+
const deferred = parameterOf.get(candidate);
|
|
235
|
+
const sites = deferred === undefined ? undefined : callsOf.get(deferred.callable.name);
|
|
236
|
+
if (deferred === undefined || sites === undefined) {
|
|
237
|
+
lookups.push({
|
|
238
|
+
file: candidate.file,
|
|
239
|
+
from: candidate.from,
|
|
240
|
+
at: candidate.access.argumentExpression,
|
|
241
|
+
object: candidate.access.expression,
|
|
242
|
+
index: candidate.access.argumentExpression,
|
|
243
|
+
use: candidate.use,
|
|
244
|
+
});
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
// An object that is a parameter too is read as the argument each call passes there.
|
|
248
|
+
const object = parenthesesRemoved(candidate.access.expression);
|
|
249
|
+
const objectAt = isIdentifier(object)
|
|
250
|
+
? deferred.callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === object.text)
|
|
251
|
+
: -1;
|
|
252
|
+
for (const call of sites) {
|
|
253
|
+
const parameter = deferred.callable.parameters[deferred.at];
|
|
254
|
+
const index = call.arguments[deferred.at] ?? parameter?.initializer;
|
|
255
|
+
const site = calls.get(call);
|
|
256
|
+
if (index === undefined || site === undefined) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
lookups.push({
|
|
260
|
+
file: site.file,
|
|
261
|
+
from: site.from,
|
|
262
|
+
at: call,
|
|
263
|
+
object: (objectAt < 0 ? undefined : call.arguments[objectAt]) ??
|
|
264
|
+
candidate.access.expression,
|
|
265
|
+
index,
|
|
266
|
+
use: candidate.use,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const nodes = lookups.flatMap((one) => [one.index, one.object]);
|
|
271
|
+
const types = queries.typesAt(nodes);
|
|
272
|
+
// A type parameter's body serves every instantiation, so it reads what its constraint admits.
|
|
273
|
+
const keysOf = (type) => {
|
|
274
|
+
if (!type.isTypeParameter()) {
|
|
275
|
+
return type;
|
|
276
|
+
}
|
|
277
|
+
const constraint = queries.constraintOf(type);
|
|
278
|
+
return constraint === UNANSWERED || constraint === undefined ? type : constraint;
|
|
279
|
+
};
|
|
280
|
+
const partsOf = (type) => {
|
|
281
|
+
const parts = queries.constituents(type);
|
|
282
|
+
return parts === UNANSWERED ? [type] : parts;
|
|
283
|
+
};
|
|
284
|
+
lookups.forEach((lookup, at) => {
|
|
285
|
+
const indexType = types[2 * at];
|
|
286
|
+
const objectType = types[2 * at + 1];
|
|
287
|
+
const literals = indexType === undefined || indexType === UNANSWERED
|
|
288
|
+
? undefined
|
|
289
|
+
: literalNames(partsOf(keysOf(indexType)));
|
|
290
|
+
if (indexType === UNANSWERED || objectType === UNANSWERED) {
|
|
309
291
|
found.push({
|
|
292
|
+
kind: "held",
|
|
310
293
|
from: lookup.from,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
294
|
+
...(literals === undefined ? {} : { names: [...literals] }),
|
|
295
|
+
...(objectType === UNANSWERED || objectType === undefined
|
|
296
|
+
? literals === undefined
|
|
297
|
+
? { members: true }
|
|
298
|
+
: {}
|
|
299
|
+
: { types: partsOf(objectType) }),
|
|
316
300
|
});
|
|
301
|
+
return;
|
|
317
302
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
303
|
+
if (objectType === undefined || literals === undefined) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
for (const part of partsOf(objectType)) {
|
|
307
|
+
const properties = queries.propertiesOf(part);
|
|
308
|
+
if (properties === UNANSWERED) {
|
|
309
|
+
found.push({ kind: "held", from: lookup.from, names: [...literals] });
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
for (const property of properties) {
|
|
313
|
+
if (!literals.has(property.escapedName)) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
for (const to of chains.declarationsOf(property)) {
|
|
317
|
+
for (const use of lookup.use === "both"
|
|
318
|
+
? ["read", "write"]
|
|
319
|
+
: [lookup.use]) {
|
|
320
|
+
found.push({
|
|
321
|
+
kind: "named",
|
|
322
|
+
from: lookup.from,
|
|
323
|
+
to,
|
|
324
|
+
file: lookup.file,
|
|
325
|
+
at: lookup.at,
|
|
326
|
+
use,
|
|
327
|
+
via: "keyed",
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
return found;
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
},
|
|
338
|
+
};
|
|
324
339
|
/** The names a union of literal types spells, or none where a constituent is no string or number literal. */
|
|
325
340
|
function literalNames(parts) {
|
|
326
341
|
const names = new Set();
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files a bundler's JavaScript API builds from, read from the build scripts a
|
|
3
|
+
* manifest's scripts run: the strings an options object passes as the bundler's entries.
|
|
4
|
+
* A script is read without running it, so a value that is not written out, a computed
|
|
5
|
+
* path or an options object built at run time among them, names nothing.
|
|
6
|
+
*/
|
|
7
|
+
import { isArrowFunction, isCallExpression, isConstructorDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isNewExpression, isSetAccessorDeclaration, isVariableDeclaration, } from "@typescript/native/ast";
|
|
8
|
+
import { declarationOf, membersNamed, staticModule } from "./static-module.js";
|
|
9
|
+
import { filesNamedBy } from "./configuration-files.js";
|
|
10
|
+
import { ancestors, dirnamePath, isExplicitPath, relativePath, resolvePath } from "../paths.js";
|
|
11
|
+
import { bytewise } from "../text/compare.js";
|
|
12
|
+
import { unwrapped } from "../unwrap.js";
|
|
13
|
+
import { holdsManifest, inInstalledPackages, manifestAt } from "../fs/manifest.js";
|
|
14
|
+
import { globExpression } from "../text/glob.js";
|
|
15
|
+
import { pathsNamed, readCommands } from "../commands/read.js";
|
|
16
|
+
import { manifestCommands } from "../roots/manifest.js";
|
|
17
|
+
import { extensionOf, MODULE_EXTENSIONS } from "../extensions.js";
|
|
18
|
+
/**
|
|
19
|
+
* The bundlers whose build call names its entries: esbuild resolves `entryPoints` against
|
|
20
|
+
* `absWorkingDir` (https://esbuild.github.io/api/#working-directory), webpack `entry`
|
|
21
|
+
* against `context` (https://webpack.js.org/configuration/entry-context/), and Rollup
|
|
22
|
+
* `input` against the working directory, each the manifest's directory when its script runs.
|
|
23
|
+
*/
|
|
24
|
+
const BUNDLERS = [
|
|
25
|
+
{
|
|
26
|
+
module: "esbuild",
|
|
27
|
+
calls: ["build", "buildSync", "context"],
|
|
28
|
+
option: "entryPoints",
|
|
29
|
+
base: "absWorkingDir",
|
|
30
|
+
paths: true,
|
|
31
|
+
},
|
|
32
|
+
{ module: "webpack", calls: ["default"], option: "entry", base: "context", paths: false },
|
|
33
|
+
{ module: "rollup", calls: ["rollup", "watch"], option: "input", base: "", paths: true },
|
|
34
|
+
];
|
|
35
|
+
/** The one value an object writes last for a key, undefined where it writes none or may hide it. */
|
|
36
|
+
function lastMember(object, key) {
|
|
37
|
+
return membersNamed(object, key)?.at(-1);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The path one entry value writes, read against `dir`: a literal, or a join of the
|
|
41
|
+
* script's own directory and literals, read against that directory.
|
|
42
|
+
*/
|
|
43
|
+
function pathOf(value, dir, scriptDir) {
|
|
44
|
+
if (value.kind === "text") {
|
|
45
|
+
const { text } = value;
|
|
46
|
+
const request = !isExplicitPath(text);
|
|
47
|
+
return { path: resolvePath(dir, text), text, request };
|
|
48
|
+
}
|
|
49
|
+
if (value.kind === "directory") {
|
|
50
|
+
const joined = value.segments.join("/");
|
|
51
|
+
return { path: resolvePath(scriptDir, joined), text: joined, request: false };
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** The entry values one entries option writes: a value, an array of them, an object of them. */
|
|
56
|
+
function entryValues(value) {
|
|
57
|
+
if (value.kind === "array") {
|
|
58
|
+
return value.elements.flatMap(entryValues);
|
|
59
|
+
}
|
|
60
|
+
if (value.kind === "object") {
|
|
61
|
+
const imported = lastMember(value, "import") ?? lastMember(value, "in");
|
|
62
|
+
if (imported !== undefined) {
|
|
63
|
+
return entryValues(imported);
|
|
64
|
+
}
|
|
65
|
+
return value.members.flatMap((member) => entryValues(member.value));
|
|
66
|
+
}
|
|
67
|
+
return [value];
|
|
68
|
+
}
|
|
69
|
+
/** The own files one entry string names, read as `api` reads it. */
|
|
70
|
+
function entriesNamed(files, api, entry) {
|
|
71
|
+
if (!api.paths && entry.request) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
if (api.paths && /[*?[{]/u.test(entry.text)) {
|
|
75
|
+
const expression = globExpression(entry.path);
|
|
76
|
+
return [...files.byName.values()].filter((file) => expression.test(file.fileName));
|
|
77
|
+
}
|
|
78
|
+
const dir = dirnamePath(entry.path);
|
|
79
|
+
return filesNamedBy(files, dir, entry.path.slice(dir.length + 1));
|
|
80
|
+
}
|
|
81
|
+
/** Whether one node is a function, whose body runs only where something calls it. */
|
|
82
|
+
function isFunction(node) {
|
|
83
|
+
return (isFunctionDeclaration(node) ||
|
|
84
|
+
isFunctionExpression(node) ||
|
|
85
|
+
isArrowFunction(node) ||
|
|
86
|
+
isMethodDeclaration(node) ||
|
|
87
|
+
isConstructorDeclaration(node) ||
|
|
88
|
+
isGetAccessorDeclaration(node) ||
|
|
89
|
+
isSetAccessorDeclaration(node));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The calls one build script runs: those of its top level, and those of each function a
|
|
93
|
+
* call it runs calls or passes on, as a callee, an argument or a function called where it
|
|
94
|
+
* is written, a name read through to the function it declares.
|
|
95
|
+
*/
|
|
96
|
+
function runCalls(file, each) {
|
|
97
|
+
const ran = new Set();
|
|
98
|
+
const run = (fn) => {
|
|
99
|
+
if (!ran.has(fn)) {
|
|
100
|
+
ran.add(fn);
|
|
101
|
+
fn.forEachChild(visit);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const runs = (node) => {
|
|
105
|
+
const at = unwrapped(node);
|
|
106
|
+
const declared = isIdentifier(at) ? declarationOf(at) : undefined;
|
|
107
|
+
const fn = declared !== undefined && isVariableDeclaration(declared) && declared.initializer
|
|
108
|
+
? unwrapped(declared.initializer)
|
|
109
|
+
: (declared ?? at);
|
|
110
|
+
if (isFunction(fn)) {
|
|
111
|
+
run(fn);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const visit = (node) => {
|
|
115
|
+
if (isFunction(node)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (isCallExpression(node) || isNewExpression(node)) {
|
|
119
|
+
if (isCallExpression(node)) {
|
|
120
|
+
each(node);
|
|
121
|
+
}
|
|
122
|
+
runs(node.expression);
|
|
123
|
+
for (const argument of node.arguments ?? []) {
|
|
124
|
+
runs(argument);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
node.forEachChild(visit);
|
|
128
|
+
};
|
|
129
|
+
file.forEachChild(visit);
|
|
130
|
+
}
|
|
131
|
+
/** The entries every bundler call one build script runs names, read against `dir`. */
|
|
132
|
+
function scriptEntries(file, dir, files) {
|
|
133
|
+
const module = staticModule(file);
|
|
134
|
+
const building = new Map();
|
|
135
|
+
for (const api of BUNDLERS) {
|
|
136
|
+
for (const one of module.calls(api.module)) {
|
|
137
|
+
if (api.calls.includes(one.export)) {
|
|
138
|
+
building.set(one.call, api);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const scriptDir = dirnamePath(file.fileName);
|
|
143
|
+
const found = [];
|
|
144
|
+
runCalls(file, (call) => {
|
|
145
|
+
const api = building.get(call);
|
|
146
|
+
if (api === undefined) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
for (const options of module.argument(call)) {
|
|
150
|
+
const entries = lastMember(options, api.option);
|
|
151
|
+
const base = api.base === "" ? undefined : lastMember(options, api.base);
|
|
152
|
+
const against = base === undefined ? { path: dir } : pathOf(base, dir, scriptDir);
|
|
153
|
+
if (entries === undefined || against === undefined) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
for (const value of entryValues(entries)) {
|
|
157
|
+
const named = pathOf(value, against.path, scriptDir);
|
|
158
|
+
if (named === undefined) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
for (const entry of entriesNamed(files, api, named)) {
|
|
162
|
+
found.push({ file: entry, source: named.text });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
return found;
|
|
168
|
+
}
|
|
169
|
+
/** The modules one manifest's command strings run or name, by absolute path, each below the target root. */
|
|
170
|
+
function scriptModules(host, root, dir) {
|
|
171
|
+
const manifest = manifestAt(host, dir);
|
|
172
|
+
const found = new Set();
|
|
173
|
+
for (const { command } of manifest === undefined ? [] : manifestCommands(host, dir, manifest)) {
|
|
174
|
+
for (const { path } of pathsNamed(readCommands(command, dir))) {
|
|
175
|
+
if (extensionOf(path, MODULE_EXTENSIONS) !== undefined &&
|
|
176
|
+
relativePath(root, path) !== undefined &&
|
|
177
|
+
!inInstalledPackages(path) &&
|
|
178
|
+
host.kindOf(path) === "file") {
|
|
179
|
+
found.add(path);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return [...found].sort(bytewise);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Every own file of one project a build script names as a bundler's entry: a module a
|
|
187
|
+
* script of a manifest at or above an own file runs, below the target root, read from
|
|
188
|
+
* the program where it holds the module and parsed alone otherwise, where a parser is given.
|
|
189
|
+
*/
|
|
190
|
+
export function buildScriptEntries(host, targetRoot, files, parse) {
|
|
191
|
+
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
192
|
+
const dirs = new Set([root]);
|
|
193
|
+
for (const file of files.byName.values()) {
|
|
194
|
+
for (const dir of ancestors(dirnamePath(file.fileName), root)) {
|
|
195
|
+
if (dirs.has(dir)) {
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
dirs.add(dir);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const found = [];
|
|
202
|
+
for (const dir of [...dirs].sort(bytewise)) {
|
|
203
|
+
if (!holdsManifest(host, dir)) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
for (const path of scriptModules(host, root, dir)) {
|
|
207
|
+
let script = files.byName.get(path);
|
|
208
|
+
if (script === undefined && parse !== undefined) {
|
|
209
|
+
try {
|
|
210
|
+
script = parse(path, host.readFile(path));
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (script !== undefined) {
|
|
217
|
+
found.push(...scriptEntries(script, dir, files));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return found;
|
|
222
|
+
}
|