@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/roots.js
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { isExpressionWithTypeArguments, isFunctionLikeDeclaration, isPropertyAccessExpression, isQualifiedName, isTypeQueryNode, isTypeReferenceNode, isPropertyDeclaration, isVariableDeclaration, isVariableStatement, } from "@typescript/native/ast";
|
|
11
11
|
import { aliasChains } from "./alias-chain.js";
|
|
12
|
+
import { filesNamedBy, outsideChain, relativeSpecifiers, } from "./configuration-files.js";
|
|
12
13
|
import { conventionEntries } from "./conventions.js";
|
|
13
14
|
import { entryPoints } from "./entry-points.js";
|
|
14
15
|
import { globExpression } from "./glob.js";
|
|
15
16
|
import { nodeKey } from "./inventory.js";
|
|
16
|
-
import { relativePath, resolvePath } from "./paths.js";
|
|
17
|
+
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
17
18
|
import { byPosition } from "./position.js";
|
|
18
19
|
import { UNANSWERED } from "./query.js";
|
|
19
20
|
import { sourceFilesOf } from "./source-files.js";
|
|
@@ -43,11 +44,22 @@ const READ_BACK_RULES = new Set([
|
|
|
43
44
|
const OUTPUT_EXTENSION = /\.(?:d\.ts|d\.mts|d\.cts|js|mjs|cjs|jsx)$/u;
|
|
44
45
|
/** The extension a source file's name drops. */
|
|
45
46
|
const SOURCE_EXTENSION = /\.(?:d\.)?[cm]?[jt]sx?$/u;
|
|
47
|
+
/** Whether no `package.json` sits between one file and the manifest's directory, which holds it. */
|
|
48
|
+
function ofPackage(host, dir, file) {
|
|
49
|
+
for (let at = dirnamePath(file); at !== dir; at = dirnamePath(at)) {
|
|
50
|
+
if (relativePath(dir, at) === undefined ||
|
|
51
|
+
host.kindOf(joinPath(at, "package.json")) === "file") {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
46
57
|
/**
|
|
47
58
|
* The rooted files one manifest target reads back to by name: its path below the
|
|
48
59
|
* manifest's directory, past its first directory where it names one, without its output
|
|
49
|
-
* extension, is the name, and a rooted file
|
|
50
|
-
* without its source extension, ends with that name at a
|
|
60
|
+
* extension, is the name, and a rooted file of the manifest's own package is one whose
|
|
61
|
+
* path below the same directory, without its source extension, ends with that name at a
|
|
62
|
+
* directory boundary.
|
|
51
63
|
*/
|
|
52
64
|
function readBackByName(host, entry, rooted) {
|
|
53
65
|
const dir = resolvePath(host.workingDirectory(), entry.dir);
|
|
@@ -63,12 +75,41 @@ function readBackByName(host, entry, rooted) {
|
|
|
63
75
|
const found = new Map();
|
|
64
76
|
for (const file of rooted) {
|
|
65
77
|
const path = relativePath(dir, file.fileName)?.replace(SOURCE_EXTENSION, "");
|
|
66
|
-
if (path !== undefined &&
|
|
78
|
+
if (path !== undefined &&
|
|
79
|
+
(path === name || path.endsWith(`/${name}`)) &&
|
|
80
|
+
ofPackage(host, dir, file.fileName)) {
|
|
67
81
|
found.set(file.fileName, file);
|
|
68
82
|
}
|
|
69
83
|
}
|
|
70
84
|
return [...found.values()];
|
|
71
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The program files a manifest target no program holds reaches: the target is a file
|
|
88
|
+
* read as a configuration file outside the program is, and each file of the chain it
|
|
89
|
+
* starts loads the program files its relative specifiers name, read through the emit
|
|
90
|
+
* mapping, as a command's shim loads the compiled entry it runs.
|
|
91
|
+
*/
|
|
92
|
+
function throughOutside(options, targetRoot, files, entry) {
|
|
93
|
+
const cwd = options.host.workingDirectory();
|
|
94
|
+
const path = resolvePath(cwd, entry.path);
|
|
95
|
+
if (options.parse === undefined ||
|
|
96
|
+
options.runFiles?.has(path) === true ||
|
|
97
|
+
options.host.kindOf(path) !== "file") {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const chain = outsideChain(options.host, targetRoot, files, options.parse, [
|
|
101
|
+
{ path, manifestDir: resolvePath(cwd, entry.dir) },
|
|
102
|
+
]);
|
|
103
|
+
const found = new Map();
|
|
104
|
+
for (const { file } of chain) {
|
|
105
|
+
for (const specifier of relativeSpecifiers(file)) {
|
|
106
|
+
for (const named of filesNamedBy(files, dirnamePath(file.fileName), specifier)) {
|
|
107
|
+
found.set(named.fileName, named);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...found.values()];
|
|
112
|
+
}
|
|
72
113
|
/** Whether a configured string carries either special character. */
|
|
73
114
|
function isPattern(text) {
|
|
74
115
|
return text.includes("*") || text.includes("?");
|
|
@@ -168,8 +209,8 @@ export function roots(project, held, targetRoot, options) {
|
|
|
168
209
|
found.set([id, kind, source].join("\u0000"), { id, kind, source });
|
|
169
210
|
};
|
|
170
211
|
const entered = [];
|
|
171
|
-
const enter = (file, kind, source, exports) => {
|
|
172
|
-
entered.push({ file, kind, source, exports });
|
|
212
|
+
const enter = (file, kind, source, exports, names) => {
|
|
213
|
+
entered.push({ file, kind, source, exports, ...(names === undefined ? {} : { names }) });
|
|
173
214
|
};
|
|
174
215
|
const points = entryPoints(project, files, options.testFiles, options.host, targetRoot, options.parse, options.conventions?.selected);
|
|
175
216
|
const published = [];
|
|
@@ -191,11 +232,28 @@ export function roots(project, held, targetRoot, options) {
|
|
|
191
232
|
...scripted,
|
|
192
233
|
...points.filter((point) => READ_BACK_RULES.has(point.rule)).map((point) => point.file),
|
|
193
234
|
];
|
|
235
|
+
const entryFilesOf = new Map();
|
|
194
236
|
const entryFiles = (entry) => {
|
|
237
|
+
const known = entryFilesOf.get(entry);
|
|
238
|
+
if (known !== undefined) {
|
|
239
|
+
return known;
|
|
240
|
+
}
|
|
241
|
+
const found = readEntryFiles(entry);
|
|
242
|
+
entryFilesOf.set(entry, found);
|
|
243
|
+
return found;
|
|
244
|
+
};
|
|
245
|
+
const readEntryFiles = (entry) => {
|
|
195
246
|
const named = files.named(entry.path);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
247
|
+
if (named.length > 0 || entry.role === "script") {
|
|
248
|
+
return named;
|
|
249
|
+
}
|
|
250
|
+
const cwd = options.host.workingDirectory();
|
|
251
|
+
const shipped = files.namedAsOutput(resolvePath(cwd, entry.dir), resolvePath(cwd, entry.path));
|
|
252
|
+
if (shipped.length > 0) {
|
|
253
|
+
return shipped;
|
|
254
|
+
}
|
|
255
|
+
const byName = readBackByName(options.host, entry, readBackTo);
|
|
256
|
+
return byName.length > 0 ? byName : throughOutside(options, targetRoot, files, entry);
|
|
199
257
|
};
|
|
200
258
|
for (const { entry, publishes } of entries) {
|
|
201
259
|
if (publishes) {
|
|
@@ -203,27 +261,39 @@ export function roots(project, held, targetRoot, options) {
|
|
|
203
261
|
}
|
|
204
262
|
}
|
|
205
263
|
const publishedNames = new Set(published.map((file) => file.fileName));
|
|
206
|
-
|
|
207
|
-
for (const file of entryFiles(entry)) {
|
|
208
|
-
const kind = ENTRY_KINDS[entry.role];
|
|
209
|
-
enter(file, kind, entry.member, !publishedNames.has(file.fileName));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
264
|
+
const patterned = [];
|
|
212
265
|
for (const pattern of options.entryFiles) {
|
|
213
266
|
const expression = globExpression(pattern);
|
|
214
267
|
for (const [path, file] of files.byPath) {
|
|
215
268
|
if (expression.test(path)) {
|
|
216
|
-
|
|
269
|
+
patterned.push({ file, pattern });
|
|
217
270
|
}
|
|
218
271
|
}
|
|
219
272
|
}
|
|
273
|
+
const unmapped = [];
|
|
274
|
+
const mapped = patterned.map(({ file }) => file);
|
|
275
|
+
for (const { entry } of entries) {
|
|
276
|
+
const named = entryFiles(entry);
|
|
277
|
+
if (named.length === 0 && entry.role !== "script") {
|
|
278
|
+
unmapped.push(entry);
|
|
279
|
+
}
|
|
280
|
+
mapped.push(...named);
|
|
281
|
+
for (const file of named) {
|
|
282
|
+
const kind = ENTRY_KINDS[entry.role];
|
|
283
|
+
enter(file, kind, entry.member, !publishedNames.has(file.fileName));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const unmappedEntries = unmapped.filter((entry) => readBackByName(options.host, entry, mapped).length === 0);
|
|
287
|
+
for (const { file, pattern } of patterned) {
|
|
288
|
+
enter(file, "entry-file", pattern, true);
|
|
289
|
+
}
|
|
220
290
|
for (const { file, row } of options.conventions === undefined
|
|
221
291
|
? []
|
|
222
292
|
: conventionEntries(options.conventions, files)) {
|
|
223
293
|
enter(file, "convention", row, true);
|
|
224
294
|
}
|
|
225
295
|
for (const point of points) {
|
|
226
|
-
enter(point.file, point.rule, point.source, point.exports);
|
|
296
|
+
enter(point.file, point.rule, point.source, point.exports, point.names);
|
|
227
297
|
}
|
|
228
298
|
for (const file of files.byPath.values()) {
|
|
229
299
|
for (const alias of typeQueryAliases(file)) {
|
|
@@ -236,7 +306,7 @@ export function roots(project, held, targetRoot, options) {
|
|
|
236
306
|
}
|
|
237
307
|
}
|
|
238
308
|
}
|
|
239
|
-
const { tables: exportsOf, answered } = exportTables(project, held, [
|
|
309
|
+
const { tables: exportsOf, named, answered, } = exportTables(project, held, [
|
|
240
310
|
...entered.filter((entry) => entry.exports).map((entry) => entry.file),
|
|
241
311
|
...published,
|
|
242
312
|
]);
|
|
@@ -247,8 +317,12 @@ export function roots(project, held, targetRoot, options) {
|
|
|
247
317
|
}
|
|
248
318
|
add(id, entry.kind, entry.source);
|
|
249
319
|
if (entry.exports) {
|
|
250
|
-
|
|
251
|
-
|
|
320
|
+
const byName = named.get(entry.file.fileName);
|
|
321
|
+
const exported = entry.names === undefined || byName === undefined
|
|
322
|
+
? (exportsOf.get(entry.file.fileName) ?? [])
|
|
323
|
+
: entry.names.flatMap((name) => byName.get(name) ?? []);
|
|
324
|
+
for (const one of exported) {
|
|
325
|
+
add(one, entry.kind, entry.source);
|
|
252
326
|
}
|
|
253
327
|
}
|
|
254
328
|
}
|
|
@@ -271,6 +345,7 @@ export function roots(project, held, targetRoot, options) {
|
|
|
271
345
|
liveUnderReachability: ordered,
|
|
272
346
|
unmatched,
|
|
273
347
|
unanswered: answered ? api.guessed : held.symbols.map((symbol) => symbol.id),
|
|
348
|
+
unmappedEntries,
|
|
274
349
|
};
|
|
275
350
|
}
|
|
276
351
|
/**
|
|
@@ -284,9 +359,10 @@ export function roots(project, held, targetRoot, options) {
|
|
|
284
359
|
function exportTables(project, held, wanted) {
|
|
285
360
|
const unique = [...new Map(wanted.map((file) => [file.fileName, file])).values()];
|
|
286
361
|
const tables = new Map();
|
|
362
|
+
const named = new Map();
|
|
287
363
|
let answered = true;
|
|
288
364
|
if (unique.length === 0) {
|
|
289
|
-
return { tables, answered };
|
|
365
|
+
return { tables, named, answered };
|
|
290
366
|
}
|
|
291
367
|
const modules = project.symbolsAt(unique.map((file) => project.handle(file)));
|
|
292
368
|
const chains = aliasChains(project, held);
|
|
@@ -307,15 +383,18 @@ function exportTables(project, held, wanted) {
|
|
|
307
383
|
return;
|
|
308
384
|
}
|
|
309
385
|
const ids = new Set();
|
|
386
|
+
const byName = new Map();
|
|
310
387
|
for (const exported of table) {
|
|
311
388
|
for (const target of chains.chainOf(exported)) {
|
|
312
389
|
answered &&= !target.guessed;
|
|
313
390
|
ids.add(target.id);
|
|
391
|
+
byName.set(exported.name, [...(byName.get(exported.name) ?? []), target.id]);
|
|
314
392
|
}
|
|
315
393
|
}
|
|
316
394
|
tables.set(file.fileName, [...ids]);
|
|
395
|
+
named.set(file.fileName, byName);
|
|
317
396
|
});
|
|
318
|
-
return { tables, answered };
|
|
397
|
+
return { tables, named, answered };
|
|
319
398
|
}
|
|
320
399
|
/** A release tag of a documentation comment: `@` and its name, between white space or the ends. */
|
|
321
400
|
const RELEASE_TAG = /(?:^|\s)@(?:public|beta|alpha)(?=\s|$)/u;
|
|
@@ -370,11 +449,13 @@ function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
|
|
|
370
449
|
const nodes = declarationNodes(held, byPath);
|
|
371
450
|
const chains = aliasChains(project, held);
|
|
372
451
|
const reached = new Set();
|
|
452
|
+
const entered = new Set();
|
|
373
453
|
const guessed = new Set();
|
|
374
454
|
const pending = [];
|
|
375
455
|
const reach = (id) => {
|
|
376
|
-
|
|
377
|
-
|
|
456
|
+
const file = byId.get(id)?.kind === "file";
|
|
457
|
+
if (!(file ? entered : reached).has(id)) {
|
|
458
|
+
(file ? entered : reached).add(id);
|
|
378
459
|
pending.push(id);
|
|
379
460
|
}
|
|
380
461
|
};
|
|
@@ -383,12 +464,7 @@ function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
|
|
|
383
464
|
}
|
|
384
465
|
for (const file of published) {
|
|
385
466
|
for (const id of exportsOf.get(file.fileName) ?? []) {
|
|
386
|
-
|
|
387
|
-
pending.push(id);
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
reach(id);
|
|
391
|
-
}
|
|
467
|
+
reach(id);
|
|
392
468
|
}
|
|
393
469
|
}
|
|
394
470
|
while (pending.length > 0) {
|
|
@@ -411,8 +487,7 @@ function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
|
|
|
411
487
|
});
|
|
412
488
|
for (const symbol of inferredTypeSymbols(project, round.flatMap((id) => {
|
|
413
489
|
const symbol = byId.get(id);
|
|
414
|
-
|
|
415
|
-
return node === undefined ? [] : [node];
|
|
490
|
+
return symbol === undefined ? [] : nodes.nodesOf(symbol);
|
|
416
491
|
}))) {
|
|
417
492
|
for (const target of chains.chainOf(symbol)) {
|
|
418
493
|
reach(target.id);
|
|
@@ -449,9 +524,7 @@ function declarationNodes(held, byPath) {
|
|
|
449
524
|
const id = held.declarations.get(nodeKey(file, node));
|
|
450
525
|
if (id !== undefined) {
|
|
451
526
|
found.nodes.add(node);
|
|
452
|
-
|
|
453
|
-
found.byId.set(id, node);
|
|
454
|
-
}
|
|
527
|
+
found.byId.set(id, [...(found.byId.get(id) ?? []), node]);
|
|
455
528
|
}
|
|
456
529
|
node.forEachChild(visit);
|
|
457
530
|
};
|
|
@@ -463,10 +536,9 @@ function declarationNodes(held, byPath) {
|
|
|
463
536
|
return {
|
|
464
537
|
typeNamesOf: (symbol) => {
|
|
465
538
|
const { byId, nodes } = nodesIn(symbol.position.path);
|
|
466
|
-
|
|
467
|
-
return declaration === undefined ? [] : typeNamesOf(declaration, nodes);
|
|
539
|
+
return (byId.get(symbol.id) ?? []).flatMap((declaration) => typeNamesOf(declaration, nodes));
|
|
468
540
|
},
|
|
469
|
-
|
|
541
|
+
nodesOf: (symbol) => nodesIn(symbol.position.path).byId.get(symbol.id) ?? [],
|
|
470
542
|
};
|
|
471
543
|
}
|
|
472
544
|
/** How deep the type arguments and constituents of an inferred type are followed. */
|
package/dist/src/schema.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The serialization-contract class: the data members and `toJSON` of every type a value's
|
|
3
3
|
* static type reaches, where the value reaches `JSON.stringify` or a call `ts.serializers`
|
|
4
|
-
* names, and `toString` too where it reaches an
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* names, and `toString` too where it reaches an erased parameter of a function outside the
|
|
5
|
+
* analysis, or an erased property of a type the program does not declare. A function of
|
|
6
|
+
* the project handing such a parameter to a destination is one, to a fixpoint, and a
|
|
7
|
+
* function invoked through `call`, `apply` or `bind` is the callee.
|
|
8
8
|
*/
|
|
9
9
|
import { isArrayLiteralExpression, isArrowFunction, isAsExpression, isCallExpression, isClassDeclaration, isClassExpression, isClassStaticBlockDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isInterfaceDeclaration, isMethodDeclaration, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyDeclaration, isSatisfiesExpression, isSetAccessorDeclaration, isSpreadElement, isStringLiteral, isTypeLiteralNode, isVariableDeclaration, ModifierFlags, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { SignatureKind, SymbolFlags, TypeFlags, } from "@typescript/native/sync";
|
|
@@ -16,7 +16,7 @@ import { nodeKey } from "./inventory.js";
|
|
|
16
16
|
import { renderPosition } from "./position.js";
|
|
17
17
|
import { must } from "./query.js";
|
|
18
18
|
import { erasedStores } from "./erased-stores.js";
|
|
19
|
-
import { checkedTypes, valueReach } from "./value-reach.js";
|
|
19
|
+
import { checkedTypes, CONVERSION_MEMBERS, valueReach } from "./value-reach.js";
|
|
20
20
|
/** The serializer every run reads, whatever the configuration names. */
|
|
21
21
|
const JSON_STRINGIFY = { shape: "global", global: "JSON.stringify" };
|
|
22
22
|
/** The methods a serializer and a formatter resolve by name on a value that left the analysis. */
|
|
@@ -25,6 +25,8 @@ const CONVERSIONS = new Set(["toJSON", "toString"]);
|
|
|
25
25
|
const SERIALIZED_CONVERSION = "toJSON";
|
|
26
26
|
/** The parameter types that keep nothing of the value they are given. */
|
|
27
27
|
const ERASED = TypeFlags.Any | TypeFlags.Unknown;
|
|
28
|
+
/** How deep a callee's result is searched for a type parameter: `Promise<Box<T>>` is two levels. */
|
|
29
|
+
const MENTION_DEPTH = 3;
|
|
28
30
|
/** The wider of two reaches, where either may be absent. */
|
|
29
31
|
function wider(a, b) {
|
|
30
32
|
return a === "conversions" || b === "conversions" ? "conversions" : (a ?? b);
|
|
@@ -313,13 +315,14 @@ function destinations(input, chains, walked) {
|
|
|
313
315
|
const inAnalysis = (path) => own.has(path) || input.consumers.some((root) => path === root || path.startsWith(`${root}/`));
|
|
314
316
|
const implemented = implementedOwners(project, inAnalysis);
|
|
315
317
|
/**
|
|
316
|
-
* Whether every
|
|
317
|
-
* declaration has no body, so the code that runs is
|
|
318
|
-
* member of an interface or an object type a class
|
|
319
|
-
* analysis implements; `settled` false skips that
|
|
318
|
+
* Whether every one of `declarations`, all of the callee's by default, is outside the
|
|
319
|
+
* analysis or ambient. An ambient declaration has no body, so the code that runs is
|
|
320
|
+
* outside the analysis, unless it is a member of an interface or an object type a class
|
|
321
|
+
* or a typed object literal of the analysis implements; `settled` false skips that
|
|
322
|
+
* question, which asks the checker.
|
|
320
323
|
*/
|
|
321
|
-
const outside = (target, settled
|
|
322
|
-
|
|
324
|
+
const outside = (target, settled, declarations = target.declarations) => declarations.length > 0 &&
|
|
325
|
+
declarations.every((handle) => {
|
|
323
326
|
if (!inAnalysis(handle.path)) {
|
|
324
327
|
return true;
|
|
325
328
|
}
|
|
@@ -333,6 +336,12 @@ function destinations(input, chains, walked) {
|
|
|
333
336
|
const owner = must(queries.parentOf(target));
|
|
334
337
|
return owner === undefined || !implemented().has(owner.id);
|
|
335
338
|
});
|
|
339
|
+
/**
|
|
340
|
+
* Whether some one declaration of a callee is outside the analysis or ambient, without
|
|
341
|
+
* asking the checker. A project's declaration merged into a library's symbol leaves the
|
|
342
|
+
* library's beside it, so a call can still resolve to the library's.
|
|
343
|
+
*/
|
|
344
|
+
const mayBeOutside = (target) => target.declarations.some((handle) => outside(target, false, [handle]));
|
|
336
345
|
const invocation = (call) => {
|
|
337
346
|
const form = forms.get(call);
|
|
338
347
|
const method = form === undefined ? undefined : callees.get(form.method);
|
|
@@ -340,7 +349,7 @@ function destinations(input, chains, walked) {
|
|
|
340
349
|
// program's own `call` and `Reflect.apply` invoke nothing here.
|
|
341
350
|
if (form !== undefined &&
|
|
342
351
|
method !== undefined &&
|
|
343
|
-
|
|
352
|
+
mayBeOutside(method) &&
|
|
344
353
|
FUNCTION_INTERFACES.has(must(project.queries.parentOf(method))?.name ?? "")) {
|
|
345
354
|
return form.invocation;
|
|
346
355
|
}
|
|
@@ -350,6 +359,52 @@ function destinations(input, chains, walked) {
|
|
|
350
359
|
const name = calleeName(invocation(call).callee);
|
|
351
360
|
return name === undefined ? undefined : callees.get(name);
|
|
352
361
|
};
|
|
362
|
+
const signatures = new Map();
|
|
363
|
+
const resolvedOf = (call) => {
|
|
364
|
+
if (!signatures.has(call)) {
|
|
365
|
+
signatures.set(call, must(queries.resolvedSignature(call)));
|
|
366
|
+
}
|
|
367
|
+
return signatures.get(call);
|
|
368
|
+
};
|
|
369
|
+
/**
|
|
370
|
+
* The declarations of a callee that the declaration of a direct call's resolved
|
|
371
|
+
* signature lies in, or all of them where it lies in none: a declaration merged into
|
|
372
|
+
* the callee's symbol that the call does not resolve to does not decide where it runs.
|
|
373
|
+
*/
|
|
374
|
+
const calledDeclarations = (call, target) => {
|
|
375
|
+
const declaration = invocation(call).indirect ? undefined : resolvedOf(call)?.declaration;
|
|
376
|
+
const signature = declaration === undefined ? undefined : project.declarationAt(declaration)?.node;
|
|
377
|
+
if (declaration === undefined || signature === undefined) {
|
|
378
|
+
return target.declarations;
|
|
379
|
+
}
|
|
380
|
+
const containing = target.declarations.filter((handle) => {
|
|
381
|
+
const node = handle.path === declaration.path ? project.declarationAt(handle)?.node : undefined;
|
|
382
|
+
return node !== undefined && node.pos <= signature.pos && signature.end <= node.end;
|
|
383
|
+
});
|
|
384
|
+
return containing.length === 0 ? target.declarations : containing;
|
|
385
|
+
};
|
|
386
|
+
/** Whether a type names a type parameter, through constituents, type arguments and call signatures. */
|
|
387
|
+
const mentions = (type, parameter, depth) => {
|
|
388
|
+
// A conditional type an alias names, `Awaited<T>` among them, carries its arguments
|
|
389
|
+
// only on the alias the client's type object reports.
|
|
390
|
+
const aliased = type
|
|
391
|
+
.aliasTypeArguments;
|
|
392
|
+
if (type.id === parameter.id || aliased?.includes(parameter.id) === true) {
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
if (depth >= MENTION_DEPTH) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
const inner = [
|
|
399
|
+
...(type.isUnionType() || type.isIntersectionType() ? must(queries.constituents(type)) : []),
|
|
400
|
+
...must(queries.typeArguments(type)),
|
|
401
|
+
...must(queries.signaturesOf(type, SignatureKind.Call)).flatMap((signature) => [
|
|
402
|
+
...signature.parameters.map((_parameter, at) => must(queries.typeAtPosition(signature, at))),
|
|
403
|
+
...[must(queries.returnTypeOf(signature))].filter((one) => one !== undefined),
|
|
404
|
+
]),
|
|
405
|
+
];
|
|
406
|
+
return inner.some((one) => mentions(one, parameter, depth + 1));
|
|
407
|
+
};
|
|
353
408
|
const erasedArguments = new Map();
|
|
354
409
|
const erasedAt = (call, argument) => {
|
|
355
410
|
let indexes = erasedArguments.get(call);
|
|
@@ -362,11 +417,33 @@ function destinations(input, chains, walked) {
|
|
|
362
417
|
? type === undefined
|
|
363
418
|
? []
|
|
364
419
|
: must(queries.signaturesOf(type, SignatureKind.Call))
|
|
365
|
-
: [
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
420
|
+
: [resolvedOf(call)].filter((one) => one !== undefined);
|
|
421
|
+
// A type parameter of the callee that nothing constrains and its result does not name
|
|
422
|
+
// keeps nothing of the value either: the instantiated signature names the value's own
|
|
423
|
+
// type there instead, and a result that names it hands the value back.
|
|
424
|
+
const generic = invoked.indirect ? undefined : resolvedOf(call);
|
|
425
|
+
const declared = generic === undefined ? undefined : must(queries.genericOf(generic));
|
|
426
|
+
const unconstrained = (index) => {
|
|
427
|
+
const parameter = declared === undefined ? undefined : must(queries.parameterType(declared, index));
|
|
428
|
+
const parts = parameter === undefined
|
|
429
|
+
? []
|
|
430
|
+
: parameter.isUnionType()
|
|
431
|
+
? must(queries.constituents(parameter))
|
|
432
|
+
: [parameter];
|
|
433
|
+
const result = declared === undefined ? undefined : must(queries.returnTypeOf(declared));
|
|
434
|
+
return parts.some((part) => {
|
|
435
|
+
if (!part.isTypeParameter() || (result !== undefined && mentions(result, part, 0))) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
const constraint = must(queries.constraintOf(part));
|
|
439
|
+
return constraint === undefined || (constraint.flags & ERASED) !== 0;
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
indexes = new Set(invoked.arguments.flatMap((_unused, index) => unconstrained(index) ||
|
|
443
|
+
signatures.some((signature) => {
|
|
444
|
+
const parameter = must(queries.parameterType(signature, index));
|
|
445
|
+
return parameter !== undefined && (parameter.flags & ERASED) !== 0;
|
|
446
|
+
})
|
|
370
447
|
? [index]
|
|
371
448
|
: []));
|
|
372
449
|
erasedArguments.set(call, indexes);
|
|
@@ -384,7 +461,7 @@ function destinations(input, chains, walked) {
|
|
|
384
461
|
if (entry !== undefined) {
|
|
385
462
|
return { reach: "properties", callee: spelledEntry(entry.entry, held) };
|
|
386
463
|
}
|
|
387
|
-
if (outside(target, true)) {
|
|
464
|
+
if (outside(target, true, calledDeclarations(call, target))) {
|
|
388
465
|
return erasedAt(call, argument)
|
|
389
466
|
? {
|
|
390
467
|
reach: "conversions",
|
|
@@ -419,7 +496,7 @@ function destinations(input, chains, walked) {
|
|
|
419
496
|
if (target === undefined) {
|
|
420
497
|
return false;
|
|
421
498
|
}
|
|
422
|
-
if (named.some((one) => names(one, target, chains)) ||
|
|
499
|
+
if (named.some((one) => names(one, target, chains)) || mayBeOutside(target)) {
|
|
423
500
|
return true;
|
|
424
501
|
}
|
|
425
502
|
return chains
|
|
@@ -509,7 +586,7 @@ export function serializationContract(input) {
|
|
|
509
586
|
// A destination holds back a data member and `toJSON`, and `toString` as well where
|
|
510
587
|
// the value left the analysis.
|
|
511
588
|
const holdsBack = (flags, name, to) => (flags & SymbolFlags.Property) !== 0 ||
|
|
512
|
-
((flags &
|
|
589
|
+
((flags & CONVERSION_MEMBERS) !== 0 &&
|
|
513
590
|
(name === SERIALIZED_CONVERSION || (to.reach === "conversions" && CONVERSIONS.has(name))));
|
|
514
591
|
const found = [];
|
|
515
592
|
for (const flow of flows) {
|
package/dist/src/session.js
CHANGED
|
@@ -31,6 +31,7 @@ export function openEngine(options) {
|
|
|
31
31
|
...(mapper === undefined ? {} : { runExternalCode: true }),
|
|
32
32
|
});
|
|
33
33
|
let closed = false;
|
|
34
|
+
const parsed = new Map();
|
|
34
35
|
return {
|
|
35
36
|
parseConfigFile: (file) => api.parseConfigFile(file),
|
|
36
37
|
parseConfigJson: (json, configFile) => api.parseJsonConfigFileContent(json, { configFileName: configFile }),
|
|
@@ -54,8 +55,14 @@ export function openEngine(options) {
|
|
|
54
55
|
moduleResolutions: { fallback: "resolve", entries: [...entries] },
|
|
55
56
|
}),
|
|
56
57
|
parseSourceFile: (fileName, text) => {
|
|
58
|
+
// Every project of a run reads the same files outside its program, so one parse serves all.
|
|
59
|
+
const known = parsed.get(fileName);
|
|
60
|
+
if (known?.text === text) {
|
|
61
|
+
return known.file;
|
|
62
|
+
}
|
|
57
63
|
const retained = api.createSourceFile(fileName, text);
|
|
58
64
|
try {
|
|
65
|
+
parsed.set(fileName, { text, file: retained.sourceFile });
|
|
59
66
|
return retained.sourceFile;
|
|
60
67
|
}
|
|
61
68
|
finally {
|
|
@@ -189,12 +196,30 @@ export function ownedAs(project, own) {
|
|
|
189
196
|
},
|
|
190
197
|
};
|
|
191
198
|
}
|
|
192
|
-
/**
|
|
199
|
+
/**
|
|
200
|
+
* The program's diagnostics placed in no file. The per-file sets never carry one; the
|
|
201
|
+
* client answers its `fileName` as `null`, and the global set repeats the program set:
|
|
202
|
+
* https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/api/session.go#L3726-L3752
|
|
203
|
+
*/
|
|
204
|
+
function unplacedOf(program) {
|
|
205
|
+
const seen = new Set();
|
|
206
|
+
return [...program.getGlobalDiagnostics(), ...program.getProgramDiagnostics()]
|
|
207
|
+
.filter((diagnostic) => diagnostic.fileName == null)
|
|
208
|
+
.filter((diagnostic) => {
|
|
209
|
+
const key = `${String(diagnostic.code)}\u0000${diagnostic.text}`;
|
|
210
|
+
const fresh = !seen.has(key);
|
|
211
|
+
seen.add(key);
|
|
212
|
+
return fresh;
|
|
213
|
+
})
|
|
214
|
+
.map((diagnostic) => ({ ...diagnostic, fileName: undefined }));
|
|
215
|
+
}
|
|
216
|
+
/** The diagnostic sets one project carries, read before any analysis. */
|
|
193
217
|
export function diagnosticsOf(project) {
|
|
194
218
|
return {
|
|
195
219
|
syntactic: project.program.getSyntacticDiagnostics(),
|
|
196
220
|
semantic: project.program.getSemanticDiagnostics(),
|
|
197
221
|
configParsing: project.program.getConfigFileParsingDiagnostics(),
|
|
222
|
+
unplaced: unplacedOf(project.program),
|
|
198
223
|
locate: (fileName, offset) => {
|
|
199
224
|
const file = project.program.getSourceFile(fileName);
|
|
200
225
|
if (file === undefined || offset < 0 || offset > file.text.length) {
|
package/dist/src/source-files.js
CHANGED
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
* compiler emits from it.
|
|
5
5
|
*/
|
|
6
6
|
import { commonDirectory, emittedFrom } from "./emit-map.js";
|
|
7
|
-
import { dirnamePath, relativePath, resolvePath } from "./paths.js";
|
|
7
|
+
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
8
8
|
/** The one character a subpath pattern of a manifest's `exports` holds. */
|
|
9
9
|
const SUBPATH_WILDCARD = "*";
|
|
10
|
+
/** The extension a source file's name drops when the project writes it out. */
|
|
11
|
+
const SOURCE_EXTENSION = /\.[cm]?[jt]sx?$/u;
|
|
12
|
+
/** The extensions the project may write a source file's output under. */
|
|
13
|
+
const OUTPUT_EXTENSIONS = [".js", ".mjs", ".cjs", ".jsx", ".d.ts", ".d.mts", ".d.cts"];
|
|
10
14
|
/**
|
|
11
15
|
* Whether one path is what a subpath pattern stands for: the text before the
|
|
12
16
|
* wildcard, any run of characters including the solidus, then the text after it.
|
|
@@ -17,6 +21,27 @@ function matchesSubpath(pattern, path) {
|
|
|
17
21
|
const after = pattern.slice(at + SUBPATH_WILDCARD.length);
|
|
18
22
|
return (path.length >= before.length + after.length && path.startsWith(before) && path.endsWith(after));
|
|
19
23
|
}
|
|
24
|
+
/** The first own file of the paths given that the project holds, or none. */
|
|
25
|
+
function heldAt(byName, paths) {
|
|
26
|
+
for (const candidate of paths) {
|
|
27
|
+
const file = byName.get(candidate);
|
|
28
|
+
if (file !== undefined) {
|
|
29
|
+
return [file];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
/** Where one project writes its output, and the directory it writes it from, absolute. */
|
|
35
|
+
function layoutOf(project, fileNames) {
|
|
36
|
+
const options = project.program.getCompilerOptions();
|
|
37
|
+
const configDir = dirnamePath(project.configFile);
|
|
38
|
+
const at = (option) => option === undefined ? undefined : resolvePath(configDir, option);
|
|
39
|
+
return {
|
|
40
|
+
outDir: at(options.outDir),
|
|
41
|
+
declarationDir: at(options.declarationDir),
|
|
42
|
+
sourceDir: at(options.rootDir) ?? commonDirectory(fileNames),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
20
45
|
/**
|
|
21
46
|
* The lookups over one project's own files.
|
|
22
47
|
*
|
|
@@ -36,12 +61,7 @@ export function sourceFilesOf(project, targetRoot) {
|
|
|
36
61
|
byPath.set(path, file);
|
|
37
62
|
}
|
|
38
63
|
}
|
|
39
|
-
const
|
|
40
|
-
const configDir = dirnamePath(project.configFile);
|
|
41
|
-
const at = (option) => option === undefined ? undefined : resolvePath(configDir, option);
|
|
42
|
-
const outDir = at(options.outDir);
|
|
43
|
-
const declarationDir = at(options.declarationDir);
|
|
44
|
-
const sourceDir = at(options.rootDir) ?? commonDirectory([...byName.keys()]);
|
|
64
|
+
const { outDir, declarationDir, sourceDir } = layoutOf(project, [...byName.keys()]);
|
|
45
65
|
const layout = { outDir, declarationDir, sourceDirs: [sourceDir] };
|
|
46
66
|
/** The source paths one written path may stand for, the path itself first. */
|
|
47
67
|
const candidates = (path) => [path, ...emittedFrom(path, layout)];
|
|
@@ -49,15 +69,20 @@ export function sourceFilesOf(project, targetRoot) {
|
|
|
49
69
|
byPath,
|
|
50
70
|
byName,
|
|
51
71
|
outputDirs: [...new Set([outDir, declarationDir])].filter((dir) => dir !== undefined),
|
|
72
|
+
writtenAs: (file) => {
|
|
73
|
+
const below = relativePath(sourceDir, file.fileName);
|
|
74
|
+
if (below === undefined) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const written = [outDir, declarationDir].flatMap((dir) => dir === undefined
|
|
78
|
+
? []
|
|
79
|
+
: OUTPUT_EXTENSIONS.map((extension) => joinPath(dir, below.replace(SOURCE_EXTENSION, extension))));
|
|
80
|
+
return [...new Set(written)].filter((path) => heldAt(byName, candidates(path))[0] === file);
|
|
81
|
+
},
|
|
82
|
+
namedAsOutput: (outputDir, path) => heldAt(byName, emittedFrom(path, { outDir: outputDir, sourceDirs: [sourceDir] })),
|
|
52
83
|
named: (path) => {
|
|
53
84
|
if (!path.includes(SUBPATH_WILDCARD)) {
|
|
54
|
-
|
|
55
|
-
const held = byName.get(candidate);
|
|
56
|
-
if (held !== undefined) {
|
|
57
|
-
return [held];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return [];
|
|
85
|
+
return heldAt(byName, candidates(path));
|
|
61
86
|
}
|
|
62
87
|
const patterns = candidates(path).filter((candidate) => candidate.includes(SUBPATH_WILDCARD));
|
|
63
88
|
return [...byName.values()].filter((file) => patterns.some((pattern) => matchesSubpath(pattern, file.fileName)));
|