@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.
Files changed (207) hide show
  1. package/dist/src/alias-chain.js +0 -5
  2. package/dist/src/amd.js +81 -0
  3. package/dist/src/analysis.js +94 -54
  4. package/dist/src/augmentations.js +23 -24
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +25 -8
  7. package/dist/src/commands/read.js +6 -7
  8. package/dist/src/commands/tools.js +5 -9
  9. package/dist/src/component-files.js +3 -2
  10. package/dist/src/conformance-record.js +1 -1
  11. package/dist/src/consumers.js +9 -10
  12. package/dist/src/contextual-uses.js +1 -17
  13. package/dist/src/contract/config.generated.js +5 -2
  14. package/dist/src/contract/contract.generated.js +119 -2
  15. package/dist/src/contract/report.generated.js +6 -2
  16. package/dist/src/dependencies.js +98 -71
  17. package/dist/src/destructuring.js +1 -19
  18. package/dist/src/element-access.js +179 -164
  19. package/dist/src/entries/build-scripts.js +222 -0
  20. package/dist/src/{configuration-files.js → entries/configuration-files.js} +22 -64
  21. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  22. package/dist/src/entries/conventions.js +739 -0
  23. package/dist/src/{html-entries.js → entries/html-entries.js} +9 -12
  24. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +3 -3
  25. package/dist/src/entries/run-directories.js +77 -0
  26. package/dist/src/entries/sources.js +144 -0
  27. package/dist/src/entries/static-module.js +670 -0
  28. package/dist/src/entries/test-runners.js +684 -0
  29. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  30. package/dist/src/extensions.js +37 -0
  31. package/dist/src/facts.js +38 -0
  32. package/dist/src/findings/completion.js +40 -4
  33. package/dist/src/findings/emitters.js +5 -8
  34. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  35. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  36. package/dist/src/findings/self-check.js +36 -20
  37. package/dist/src/findings-pass.js +5 -6
  38. package/dist/src/framework-lifecycle.js +42 -15
  39. package/dist/src/fs/manifest.js +15 -20
  40. package/dist/src/generated-file.js +172 -29
  41. package/dist/src/graph.js +14 -8
  42. package/dist/src/import-bindings.js +73 -28
  43. package/dist/src/index/sites.js +557 -0
  44. package/dist/src/index.js +1 -1
  45. package/dist/src/indexed-access.js +523 -478
  46. package/dist/src/injection-container.js +1 -1
  47. package/dist/src/interface-satisfaction.js +1 -1
  48. package/dist/src/inventory/commonjs.js +62 -17
  49. package/dist/src/inventory/enumerate.js +2 -1
  50. package/dist/src/javascript-file.js +26 -11
  51. package/dist/src/keyof-comparisons.js +124 -103
  52. package/dist/src/load/classify.js +28 -9
  53. package/dist/src/load/diagnostics.js +8 -1
  54. package/dist/src/load/discover.js +15 -7
  55. package/dist/src/load/expected-module.js +2 -2
  56. package/dist/src/load/plan.js +2 -2
  57. package/dist/src/load/specifiers.js +2 -4
  58. package/dist/src/load/synthesize.js +10 -5
  59. package/dist/src/load/workspace-manifests.js +1 -1
  60. package/dist/src/load/workspace-resolution.js +2 -2
  61. package/dist/src/matrix.js +12 -17
  62. package/dist/src/module-declarations.js +70 -85
  63. package/dist/src/overrides.js +134 -112
  64. package/dist/src/paths.js +56 -1
  65. package/dist/src/platform-calls.js +78 -0
  66. package/dist/src/platform-symbols.js +134 -0
  67. package/dist/src/references/read-rule.js +1 -0
  68. package/dist/src/references/read-rules.js +48 -0
  69. package/dist/src/references/sink.js +151 -0
  70. package/dist/src/references/sources.js +450 -0
  71. package/dist/src/references.js +111 -1116
  72. package/dist/src/resolve.js +1 -1
  73. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +7 -4
  74. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  75. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  76. package/dist/src/roots/published-reach.js +412 -0
  77. package/dist/src/roots/root-set.js +489 -0
  78. package/dist/src/roots/unseen-loads.js +236 -0
  79. package/dist/src/serialization-contract.js +9 -4
  80. package/dist/src/source-files.js +5 -9
  81. package/dist/src/stores.js +4 -2
  82. package/dist/src/suppress.js +30 -32
  83. package/dist/src/sweep.js +25 -13
  84. package/dist/src/symbol-keys.js +89 -83
  85. package/dist/src/tests/file-class.js +76 -0
  86. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  87. package/dist/src/{test-support.js → tests/support.js} +5 -12
  88. package/dist/src/type-parameter-reads.js +178 -139
  89. package/dist/src/unimplemented-callees.js +262 -242
  90. package/dist/src/verbs/analyze.js +15 -3
  91. package/dist/src/verbs/describe.js +5 -3
  92. package/dist/src/verbs/explain.js +50 -28
  93. package/dist/src/verbs/print-retained.js +2 -2
  94. package/jsr.json +1 -1
  95. package/package.json +3 -3
  96. package/src/alias-chain.ts +0 -7
  97. package/src/amd.ts +130 -0
  98. package/src/analysis.ts +156 -70
  99. package/src/augmentations.ts +41 -27
  100. package/src/automatic-types.ts +1 -1
  101. package/src/bindings.ts +460 -0
  102. package/src/calls.ts +25 -7
  103. package/src/commands/read.ts +6 -8
  104. package/src/commands/tools.ts +5 -11
  105. package/src/component-files.ts +4 -2
  106. package/src/conformance-record.ts +1 -1
  107. package/src/consumers.ts +12 -11
  108. package/src/contextual-uses.ts +2 -22
  109. package/src/contract/config.generated.ts +5 -2
  110. package/src/contract/contract.generated.ts +138 -2
  111. package/src/contract/report.generated.ts +6 -2
  112. package/src/dependencies.ts +117 -71
  113. package/src/destructuring.ts +1 -31
  114. package/src/element-access.ts +188 -182
  115. package/src/entries/build-scripts.ts +292 -0
  116. package/src/{configuration-files.ts → entries/configuration-files.ts} +33 -82
  117. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  118. package/src/{conventions.ts → entries/conventions.ts} +349 -523
  119. package/src/{html-entries.ts → entries/html-entries.ts} +19 -18
  120. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +5 -5
  121. package/src/entries/run-directories.ts +108 -0
  122. package/src/entries/sources.ts +266 -0
  123. package/src/entries/static-module.ts +858 -0
  124. package/src/entries/test-runners.ts +868 -0
  125. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  126. package/src/exempt.ts +3 -2
  127. package/src/extensions.ts +45 -0
  128. package/src/facts.ts +46 -0
  129. package/src/file-facts.ts +1 -1
  130. package/src/findings/boundary.ts +2 -0
  131. package/src/findings/completion.ts +43 -4
  132. package/src/findings/emitter.ts +1 -1
  133. package/src/findings/emitters.ts +14 -10
  134. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  135. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  136. package/src/findings/self-check.ts +41 -22
  137. package/src/findings/visibility-narrowing.ts +1 -1
  138. package/src/findings-pass.ts +5 -12
  139. package/src/framework-lifecycle.ts +43 -15
  140. package/src/fs/manifest.ts +25 -21
  141. package/src/generated-file.ts +240 -36
  142. package/src/graph.ts +27 -11
  143. package/src/import-bindings.ts +98 -29
  144. package/src/index/sites.ts +781 -0
  145. package/src/index.ts +1 -1
  146. package/src/indexed-access.ts +570 -527
  147. package/src/injection-container.ts +1 -1
  148. package/src/interface-satisfaction.ts +1 -1
  149. package/src/inventory/commonjs.ts +102 -17
  150. package/src/inventory/enumerate.ts +2 -1
  151. package/src/javascript-file.ts +29 -12
  152. package/src/keyof-comparisons.ts +131 -116
  153. package/src/load/classify.ts +49 -13
  154. package/src/load/diagnostics.ts +11 -1
  155. package/src/load/discover.ts +27 -11
  156. package/src/load/expected-module.ts +8 -2
  157. package/src/load/plan.ts +11 -2
  158. package/src/load/specifiers.ts +8 -5
  159. package/src/load/synthesize.ts +12 -6
  160. package/src/load/test-globals.ts +1 -1
  161. package/src/load/workspace-manifests.ts +1 -1
  162. package/src/load/workspace-resolution.ts +9 -2
  163. package/src/load/workspace-run.ts +1 -1
  164. package/src/matrix.ts +13 -17
  165. package/src/module-declarations.ts +74 -98
  166. package/src/overrides.ts +162 -134
  167. package/src/paths.ts +65 -1
  168. package/src/platform-calls.ts +119 -0
  169. package/src/platform-symbols.ts +189 -0
  170. package/src/references/read-rule.ts +60 -0
  171. package/src/references/read-rules.ts +55 -0
  172. package/src/references/sink.ts +213 -0
  173. package/src/references/sources.ts +632 -0
  174. package/src/references.ts +174 -1492
  175. package/src/report.ts +2 -2
  176. package/src/resolve.ts +1 -1
  177. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +10 -7
  178. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  179. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  180. package/src/roots/published-reach.ts +481 -0
  181. package/src/{roots.ts → roots/root-set.ts} +203 -630
  182. package/src/roots/unseen-loads.ts +345 -0
  183. package/src/serialization-contract.ts +9 -4
  184. package/src/source-files.ts +5 -10
  185. package/src/stores.ts +13 -2
  186. package/src/suppress.ts +48 -33
  187. package/src/sweep.ts +37 -14
  188. package/src/symbol-keys.ts +102 -102
  189. package/src/tests/file-class.ts +118 -0
  190. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  191. package/src/{test-support.ts → tests/support.ts} +15 -21
  192. package/src/type-parameter-reads.ts +187 -158
  193. package/src/unimplemented-callees.ts +297 -280
  194. package/src/verbs/analyze.ts +19 -5
  195. package/src/verbs/describe.ts +5 -2
  196. package/src/verbs/explain.ts +75 -32
  197. package/src/verbs/print-retained.ts +2 -2
  198. package/dist/src/build-scripts.js +0 -418
  199. package/dist/src/conventions.js +0 -907
  200. package/dist/src/entry-points.js +0 -444
  201. package/dist/src/own-body.js +0 -28
  202. package/dist/src/roots.js +0 -865
  203. package/dist/src/unseen-loads.js +0 -189
  204. package/src/build-scripts.ts +0 -536
  205. package/src/entry-points.ts +0 -584
  206. package/src/own-body.ts +0 -42
  207. package/src/unseen-loads.ts +0 -282
@@ -5,64 +5,34 @@
5
5
  * published export, which only supposes a consumer, stays a reference-counting candidate.
6
6
  */
7
7
 
8
- import {
9
- isArrowFunction,
10
- isAssertionExpression,
11
- isExpressionWithTypeArguments,
12
- isFunctionLikeDeclaration,
13
- isIdentifier,
14
- isMethodDeclaration,
15
- isObjectLiteralExpression,
16
- isParenthesizedExpression,
17
- isPropertyAssignment,
18
- isReturnStatement,
19
- isSatisfiesExpression,
20
- isPropertyAccessExpression,
21
- isQualifiedName,
22
- isTypeQueryNode,
23
- isTypeReferenceNode,
24
- isPropertyDeclaration,
25
- isVariableDeclaration,
26
- isVariableStatement,
27
- SyntaxKind,
28
- type Expression,
29
- type Node,
30
- type ObjectLiteralExpression,
31
- type SourceFile,
32
- } from "@typescript/native/ast";
33
- import { aliasChains } from "./alias-chain.ts";
34
- import { isGlobalBlock } from "./augmentations.ts";
35
- import {
36
- filesNamedBy,
37
- outsideChain,
38
- relativeSpecifiers,
39
- type ParseFile,
40
- } from "./configuration-files.ts";
41
- import { conventionEntries, type Conventions } from "./conventions.ts";
42
- import { entryPoints, type EntryRule } from "./entry-points.ts";
43
- import type { Host } from "./host.ts";
44
- import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
8
+ import type { SourceFile } from "@typescript/native/ast";
9
+ import { aliasChains } from "../alias-chain.ts";
10
+ import { filesNamedBy, outsideChain, type ParseFile } from "../entries/configuration-files.ts";
11
+ import type { EntryRule, SourceFacts } from "../entries/sources.ts";
12
+ import { relativeSpecifiers } from "../entries/static-module.ts";
13
+ import type { Host } from "../host.ts";
14
+ import { nodeKey, type Inventory } from "../inventory.ts";
45
15
  import type { Manifest, ManifestEntry } from "./manifest.ts";
46
- import { ancestors, dirnamePath, relativePath, resolvePath } from "./paths.ts";
47
- import { byPosition } from "./position.ts";
48
- import { UNANSWERED } from "./query.ts";
49
- import type { ProjectView } from "./session.ts";
50
- import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
51
- import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
52
- import { typeQueryAliases } from "./type-query-alias.ts";
53
- import { bytewise } from "./text/compare.ts";
54
- import { holdsManifest } from "./fs/manifest.ts";
55
- import { globExpression } from "./text/glob.ts";
56
- import { spelledFiles } from "./text/named-files.ts";
57
- import { commandFileTexts, workflowCommands } from "./workflow-steps.ts";
58
- import { readCommands } from "./commands/read.ts";
16
+ import { ancestors, dirnamePath, relativePath, resolvePath } from "../paths.ts";
17
+ import { byPosition } from "../position.ts";
18
+ import { UNANSWERED } from "../query.ts";
19
+ import type { ProjectView } from "../session.ts";
20
+ import { sourceFilesOf, type SourceFiles } from "../source-files.ts";
21
+ import { typeQueryAliases } from "../type-query-alias.ts";
22
+ import { bytewise } from "../text/compare.ts";
23
+ import { holdsManifest } from "../fs/manifest.ts";
24
+ import { globExpression } from "../text/glob.ts";
25
+ import { spelledFiles } from "../text/named-files.ts";
26
+ import type { Workflows } from "../entries/workflow-steps.ts";
27
+ import { extensionOf, OUTPUT_EXTENSIONS } from "../extensions.ts";
28
+ import { publishedAPI, releaseTagged } from "./published-reach.ts";
29
+ import { skippedRoots, type Skip } from "../type-errors.ts";
30
+ import { mergedGlobalInterfaces } from "../augmentations.ts";
59
31
 
60
32
  /** Why one declaration is a root. */
61
33
  export type RootKind =
62
34
  /** A file `ts.entry_files` names, and what it exports. */
63
35
  | "entry-file"
64
- /** A file an applied convention row's globs match, and what it exports. */
65
- | "convention"
66
36
  /** A file the manifest names as an importable entry point, and what it exports outside the published API. */
67
37
  | "manifest-entry"
68
38
  /** A file the manifest names as a command, and what it exports outside the published API. */
@@ -151,10 +121,10 @@ export interface RootOptions {
151
121
  readonly patterns: readonly string[];
152
122
  /** `ts.entry_files` from the resolved configuration. */
153
123
  readonly entryFiles: readonly string[];
154
- /** The convention rows the run applied. */
155
- readonly conventions?: Conventions | undefined;
156
- /** `ts.test_files` from the resolved configuration. */
157
- readonly testFiles: readonly string[];
124
+ /** What the project's entry sources enter, read once. */
125
+ readonly sources: SourceFacts;
126
+ /** The type errors that skipped a unit of the project's own files. */
127
+ readonly skips: readonly Skip[];
158
128
  /** The parser the configuration files no program holds are read with, where they are read. */
159
129
  readonly parse?: ParseFile | undefined;
160
130
  /** Whether the target is a library, so a consumer outside it reaches its published API. */
@@ -197,9 +167,6 @@ const READ_BACK_RULES: ReadonlySet<EntryRule> = new Set([
197
167
  "html-entry",
198
168
  ]);
199
169
 
200
- /** The extensions a manifest target's name drops: the ones a build writes. */
201
- const OUTPUT_EXTENSION = /\.(?:d\.ts|d\.mts|d\.cts|js|mjs|cjs|jsx)$/u;
202
-
203
170
  /** The extension a source file's name drops. */
204
171
  const SOURCE_EXTENSION = /\.(?:d\.)?[cm]?[jt]sx?$/u;
205
172
 
@@ -227,7 +194,11 @@ function readBackByName(
227
194
  return [];
228
195
  }
229
196
  const cut = below.indexOf("/");
230
- const name = (cut === -1 ? below : below.slice(cut + 1)).replace(OUTPUT_EXTENSION, "");
197
+ const written = cut === -1 ? below : below.slice(cut + 1);
198
+ const name = written.slice(
199
+ 0,
200
+ written.length - (extensionOf(written, OUTPUT_EXTENSIONS)?.length ?? 0),
201
+ );
231
202
  if (name === "") {
232
203
  return [];
233
204
  }
@@ -332,37 +303,20 @@ export function matchRef(pattern: string, ref: string): boolean {
332
303
  return matchGlob(Array.from(pattern), Array.from(ref));
333
304
  }
334
305
 
335
- /** The members of one declaration, indexed by the container that holds them. */
336
- function childrenOf(held: Inventory): ReadonlyMap<string, readonly InventorySymbol[]> {
337
- const byParent = new Map<string, InventorySymbol[]>();
338
- for (const symbol of held.symbols) {
339
- const siblings = byParent.get(symbol.parent);
340
- if (siblings === undefined) {
341
- byParent.set(symbol.parent, [symbol]);
342
- } else {
343
- siblings.push(symbol);
344
- }
345
- }
346
- return byParent;
347
- }
348
-
349
306
  /**
350
307
  * Per package, the own files its held texts name, as `spelledFiles` reads them, but those a
351
308
  * root enters with all they export: a command can use no more of such a file than the root
352
309
  * keeps live. A manifest's are its entries' held texts, and the target root's package has the
353
310
  * workflow steps, git hooks and makefile recipes beside its own, with the whole text of each
354
- * file `commandFileTexts` reads.
311
+ * file they are read from.
355
312
  */
356
313
  function scriptHolds(
357
314
  entries: readonly ManifestEntry[],
358
315
  files: SourceFiles,
359
- entered: readonly {
360
- readonly file: SourceFile;
361
- readonly exports: boolean;
362
- readonly names?: readonly string[];
363
- }[],
316
+ entered: readonly Entered[],
364
317
  host: Host,
365
318
  targetRoot: string,
319
+ workflows: Workflows,
366
320
  ): readonly HeldScripts[] {
367
321
  const rooted = new Set(
368
322
  entered.filter((one) => one.exports && one.names === undefined).map((one) => one.file.fileName),
@@ -374,13 +328,7 @@ function scriptHolds(
374
328
  const commands = new Map<string, string[]>([
375
329
  [
376
330
  resolvePath(cwd, targetRoot),
377
- [
378
- ...workflowCommands(host, targetRoot).flatMap(({ text, dir, writtenDirectory }) => [
379
- readCommands(text, dir).held,
380
- ...(writtenDirectory === undefined ? [] : [writtenDirectory]),
381
- ]),
382
- ...commandFileTexts(host, targetRoot),
383
- ],
331
+ [...workflows.readings.map((reading) => reading.held), ...workflows.texts],
384
332
  ],
385
333
  ]);
386
334
  for (const entry of entries) {
@@ -400,62 +348,24 @@ function scriptHolds(
400
348
  return held;
401
349
  }
402
350
 
403
- /**
404
- * The roots one project holds, and every configured string that named nothing in it. A
405
- * file a manifest member, an entry-file pattern or a tool's configuration names is a root
406
- * with each name of its export table and every link of a re-export chain to the
407
- * declaration it carries; in a library, the published API alone roots what a published
408
- * file exports. A file a runtime executes without reading its exports, a test file or a
409
- * worker, is a root by itself.
410
- */
411
- export function roots<Brand>(
412
- project: ProjectView<Brand>,
413
- held: Inventory,
414
- targetRoot: string,
415
- options: RootOptions,
416
- ): Roots {
417
- const files = sourceFilesOf(project, targetRoot);
418
- const declared = held.declarations;
419
- const fileIds = new Map<string, string>();
420
- for (const file of files.byName.values()) {
421
- const id = declared.get(nodeKey(file, file));
422
- if (id !== undefined) {
423
- fileIds.set(file.fileName, id);
424
- }
425
- }
426
- const found = new Map<string, Root>();
427
- const add = (id: string, kind: RootKind, source: string): void => {
428
- found.set([id, kind, source].join("\u0000"), { id, kind, source });
429
- };
351
+ /** One file a source enters, with the export names it roots where it roots only some. */
352
+ interface Entered {
353
+ readonly file: SourceFile;
354
+ readonly kind: RootKind;
355
+ readonly source: string;
356
+ readonly exports: boolean;
357
+ readonly names?: readonly string[];
358
+ }
430
359
 
431
- const entered: {
432
- file: SourceFile;
433
- kind: RootKind;
434
- source: string;
435
- exports: boolean;
436
- names?: readonly string[];
437
- }[] = [];
438
- const enter = (
439
- file: SourceFile,
440
- kind: RootKind,
441
- source: string,
442
- exports: boolean,
443
- names?: readonly string[],
444
- ): void => {
445
- entered.push({ file, kind, source, exports, ...(names === undefined ? {} : { names }) });
446
- };
360
+ /** One manifest entry, and whether a consumer outside the target reaches what it names. */
361
+ interface PackageEntry {
362
+ readonly entry: ManifestEntry;
363
+ readonly publishes: boolean;
364
+ }
447
365
 
448
- const points = entryPoints(
449
- project,
450
- files,
451
- options.testFiles,
452
- options.host,
453
- targetRoot,
454
- options.parse,
455
- options.conventions?.selected,
456
- );
457
- const published: SourceFile[] = [];
458
- const entries = [
366
+ /** The target's manifest entries, then those of its other packages. */
367
+ function packageEntries(options: RootOptions): readonly PackageEntry[] {
368
+ return [
459
369
  ...options.manifest.entries.map((entry) => ({
460
370
  entry,
461
371
  publishes:
@@ -470,24 +380,21 @@ export function roots<Brand>(
470
380
  (options.publishedAPI || options.publishedMembers?.has(entry.dir) === true),
471
381
  })),
472
382
  ];
473
- const scripted = entries.flatMap(({ entry }) =>
474
- entry.role === "script" && entry.held === undefined ? files.named(entry.path) : [],
475
- );
476
- const readBackTo = [
477
- ...scripted,
478
- ...points.filter((point) => READ_BACK_RULES.has(point.rule)).map((point) => point.file),
479
- ];
480
- const entryFilesOf = new Map<ManifestEntry, readonly SourceFile[]>();
481
- const entryFiles = (entry: ManifestEntry): readonly SourceFile[] => {
482
- const known = entryFilesOf.get(entry);
483
- if (known !== undefined) {
484
- return known;
485
- }
486
- const found = readEntryFiles(entry);
487
- entryFilesOf.set(entry, found);
488
- return found;
489
- };
490
- const readEntryFiles = (entry: ManifestEntry): readonly SourceFile[] => {
383
+ }
384
+
385
+ /**
386
+ * The program files one manifest entry names, read once per entry: its path, else its
387
+ * output's source, else a rooted file of `readBackTo` by name, else the chain it starts
388
+ * outside every program.
389
+ */
390
+ function entryFileReader(
391
+ options: RootOptions,
392
+ targetRoot: string,
393
+ files: SourceFiles,
394
+ readBackTo: readonly SourceFile[],
395
+ ): (entry: ManifestEntry) => readonly SourceFile[] {
396
+ const read = new Map<ManifestEntry, readonly SourceFile[]>();
397
+ const readEntry = (entry: ManifestEntry): readonly SourceFile[] => {
491
398
  const named = files.named(entry.path);
492
399
  if (named.length > 0 || entry.role === "script") {
493
400
  return named;
@@ -500,11 +407,54 @@ export function roots<Brand>(
500
407
  const byName = readBackByName(options.host, entry, readBackTo);
501
408
  return byName.length > 0 ? byName : throughOutside(options, targetRoot, files, entry);
502
409
  };
503
- for (const { entry, publishes } of entries) {
504
- if (publishes) {
505
- published.push(...entryFiles(entry));
410
+ return (entry) => {
411
+ const known = read.get(entry) ?? readEntry(entry);
412
+ read.set(entry, known);
413
+ return known;
414
+ };
415
+ }
416
+
417
+ /** The files `ts.entry_files` names, each with the pattern that names it. */
418
+ function patternedFiles(
419
+ files: SourceFiles,
420
+ patterns: readonly string[],
421
+ ): readonly { file: SourceFile; pattern: string }[] {
422
+ const patterned: { file: SourceFile; pattern: string }[] = [];
423
+ for (const pattern of patterns) {
424
+ const expression = globExpression(pattern);
425
+ for (const [path, file] of files.byPath) {
426
+ if (expression.test(path)) {
427
+ patterned.push({ file, pattern });
428
+ }
506
429
  }
507
430
  }
431
+ return patterned;
432
+ }
433
+
434
+ /**
435
+ * The files every root source enters, in the order manifest entries, entry-file patterns,
436
+ * entry sources; the files whose exports a consumer outside the target reaches; and the
437
+ * entries that name no file even read back by name to another entered file.
438
+ */
439
+ function enteredFiles(
440
+ options: RootOptions,
441
+ targetRoot: string,
442
+ files: SourceFiles,
443
+ entries: readonly PackageEntry[],
444
+ ): {
445
+ entered: readonly Entered[];
446
+ published: readonly SourceFile[];
447
+ unmappedEntries: readonly ManifestEntry[];
448
+ } {
449
+ const points = options.sources.entered;
450
+ const scripted = entries.flatMap(({ entry }) =>
451
+ entry.role === "script" && entry.held === undefined ? files.named(entry.path) : [],
452
+ );
453
+ const entryFiles = entryFileReader(options, targetRoot, files, [
454
+ ...scripted,
455
+ ...points.filter((point) => READ_BACK_RULES.has(point.rule)).map((point) => point.file),
456
+ ]);
457
+ const published = entries.flatMap(({ entry, publishes }) => (publishes ? entryFiles(entry) : []));
508
458
  if (options.publishedAPI) {
509
459
  // A library's bundle hands what its entry exports to every program that loads it.
510
460
  published.push(
@@ -512,15 +462,8 @@ export function roots<Brand>(
512
462
  );
513
463
  }
514
464
  const publishedNames = new Set(published.map((file) => file.fileName));
515
- const patterned: { file: SourceFile; pattern: string }[] = [];
516
- for (const pattern of options.entryFiles) {
517
- const expression = globExpression(pattern);
518
- for (const [path, file] of files.byPath) {
519
- if (expression.test(path)) {
520
- patterned.push({ file, pattern });
521
- }
522
- }
523
- }
465
+ const patterned = patternedFiles(files, options.entryFiles);
466
+ const entered: Entered[] = [];
524
467
  const unmapped: ManifestEntry[] = [];
525
468
  const mapped = patterned.map(({ file }) => file);
526
469
  for (const { entry } of entries) {
@@ -534,33 +477,41 @@ export function roots<Brand>(
534
477
  mapped.push(...named);
535
478
  for (const file of named) {
536
479
  const kind = ENTRY_KINDS[entry.role];
537
- enter(file, kind, entry.member, !publishedNames.has(file.fileName));
480
+ entered.push({
481
+ file,
482
+ kind,
483
+ source: entry.member,
484
+ exports: !publishedNames.has(file.fileName),
485
+ });
538
486
  }
539
487
  }
540
- const unmappedEntries = unmapped.filter(
541
- (entry) => readBackByName(options.host, entry, mapped).length === 0,
542
- );
543
488
  for (const { file, pattern } of patterned) {
544
- enter(file, "entry-file", pattern, true);
545
- }
546
- for (const { file, row } of options.conventions === undefined
547
- ? []
548
- : conventionEntries(options.conventions, files)) {
549
- enter(file, "convention", row, true);
489
+ entered.push({ file, kind: "entry-file", source: pattern, exports: true });
550
490
  }
551
491
  for (const point of points) {
552
- enter(point.file, point.rule, point.source, point.exports, point.names);
492
+ entered.push({
493
+ file: point.file,
494
+ kind: point.rule,
495
+ source: point.source,
496
+ exports: point.exports,
497
+ ...(point.names === undefined ? {} : { names: point.names }),
498
+ });
553
499
  }
554
- const heldScripts = scriptHolds(
555
- entries.map(({ entry }) => entry),
556
- files,
500
+ return {
557
501
  entered,
558
- options.host,
559
- targetRoot,
560
- );
561
- const heldFiles = [...new Set(heldScripts.flatMap((one) => one.files))].flatMap(
562
- (path) => files.byPath.get(path) ?? [],
563
- );
502
+ published,
503
+ unmappedEntries: unmapped.filter(
504
+ (entry) => readBackByName(options.host, entry, mapped).length === 0,
505
+ ),
506
+ };
507
+ }
508
+
509
+ /** Every declaration and container of a `typeof import(...)` global, with the type's text. */
510
+ function typeQueryRoots(
511
+ files: SourceFiles,
512
+ declared: Inventory["declarations"],
513
+ add: (id: string, kind: RootKind, source: string) => void,
514
+ ): void {
564
515
  for (const file of files.byPath.values()) {
565
516
  for (const alias of typeQueryAliases(file)) {
566
517
  const source = alias.type.getText().replace(/\s+/gu, " ");
@@ -572,6 +523,60 @@ export function roots<Brand>(
572
523
  }
573
524
  }
574
525
  }
526
+ }
527
+
528
+ /** The roots in the order {@link Roots.liveUnderReachability} states. */
529
+ function orderedRoots(found: Iterable<Root>, held: Inventory): readonly Root[] {
530
+ const sites = new Map(held.symbols.map((symbol) => [symbol.id, symbol.position]));
531
+ return [...found].sort((a, b) => {
532
+ const left = sites.get(a.id);
533
+ const right = sites.get(b.id);
534
+ const site = left === undefined || right === undefined ? 0 : byPosition(left, right);
535
+ return site || bytewise(a.kind, b.kind) || bytewise(a.source, b.source);
536
+ });
537
+ }
538
+
539
+ /**
540
+ * The roots one project holds, and every configured string that named nothing in it. A
541
+ * file a manifest member, an entry-file pattern or a tool's configuration names is a root
542
+ * with each name of its export table and every re-export link to what it carries; in a
543
+ * library, the published API alone roots what a published file exports. A test file is a
544
+ * root by itself, and so is each declaration a type error's skip keeps and each global
545
+ * interface merging into one declared outside the project.
546
+ */
547
+ export function rootSet<Brand>(
548
+ project: ProjectView<Brand>,
549
+ held: Inventory,
550
+ targetRoot: string,
551
+ options: RootOptions,
552
+ ): Roots {
553
+ const files = sourceFilesOf(project, targetRoot);
554
+ const fileIds = new Map<string, string>();
555
+ for (const file of files.byName.values()) {
556
+ const id = held.declarations.get(nodeKey(file, file));
557
+ if (id !== undefined) {
558
+ fileIds.set(file.fileName, id);
559
+ }
560
+ }
561
+ const found = new Map<string, Root>();
562
+ const add = (id: string, kind: RootKind, source: string): void => {
563
+ found.set([id, kind, source].join("\u0000"), { id, kind, source });
564
+ };
565
+
566
+ const entries = packageEntries(options);
567
+ const { entered, published, unmappedEntries } = enteredFiles(options, targetRoot, files, entries);
568
+ const heldScripts = scriptHolds(
569
+ entries.map(({ entry }) => entry),
570
+ files,
571
+ entered,
572
+ options.host,
573
+ targetRoot,
574
+ options.sources.workflows,
575
+ );
576
+ const heldFiles = [...new Set(heldScripts.flatMap((one) => one.files))].flatMap(
577
+ (path) => files.byPath.get(path) ?? [],
578
+ );
579
+ typeQueryRoots(files, held.declarations, add);
575
580
 
576
581
  const {
577
582
  tables: exportsOf,
@@ -613,17 +618,15 @@ export function roots<Brand>(
613
618
  }
614
619
 
615
620
  const unmatched = configured(held, options.patterns, add);
616
-
617
- const sites = new Map(held.symbols.map((symbol) => [symbol.id, symbol.position]));
618
- const ordered = [...found.values()].sort((a, b) => {
619
- const left = sites.get(a.id);
620
- const right = sites.get(b.id);
621
- const site = left === undefined || right === undefined ? 0 : byPosition(left, right);
622
- return site || bytewise(a.kind, b.kind) || bytewise(a.source, b.source);
623
- });
621
+ for (const id of skippedRoots(project, held, options.skips)) {
622
+ add(id, "type-error", "");
623
+ }
624
+ for (const id of mergedGlobalInterfaces(project, held, files).kept) {
625
+ add(id, "global-augmentation", "");
626
+ }
624
627
 
625
628
  return {
626
- liveUnderReachability: ordered,
629
+ liveUnderReachability: orderedRoots(found.values(), held),
627
630
  unmatched,
628
631
  unanswered: [
629
632
  ...(answered ? api.guessed : held.symbols.map((symbol) => symbol.id)),
@@ -698,436 +701,6 @@ function exportTables<Brand>(
698
701
  return { tables, named, answered };
699
702
  }
700
703
 
701
- /** A release tag of a documentation comment: `@` and its name, between white space or the ends. */
702
- const RELEASE_TAG = /(?:^|\s)@(?:public|beta|alpha)(?=\s|$)/u;
703
-
704
- /**
705
- * Whether the documentation comment of the statement that starts at `start`, the last
706
- * `/** *\/` comment before it with only white space between the two, holds a release tag.
707
- */
708
- function releaseTagIn(text: string, from: number, start: number): boolean {
709
- const leading = text.slice(from, start);
710
- const open = leading.lastIndexOf("/**");
711
- const close = open < 0 ? -1 : leading.indexOf("*/", open + 3);
712
- if (close < 0 || leading.slice(close + 2).trim() !== "") {
713
- return false;
714
- }
715
- return RELEASE_TAG.test(leading.slice(open + 3, close));
716
- }
717
-
718
- /** The exported top-level declarations of the program's files a release tag marks. */
719
- function releaseTagged(
720
- held: Inventory,
721
- files: { readonly byPath: ReadonlyMap<string, SourceFile> },
722
- ): readonly string[] {
723
- const exported = new Set(held.symbols.filter((symbol) => symbol.exported).map((one) => one.id));
724
- const found: string[] = [];
725
- for (const file of files.byPath.values()) {
726
- for (const statement of file.statements) {
727
- if (!releaseTagIn(file.text, statement.pos, statement.getStart())) {
728
- continue;
729
- }
730
- const nodes: readonly Node[] = isVariableStatement(statement)
731
- ? statement.declarationList.declarations
732
- : [statement];
733
- for (const node of nodes) {
734
- const id = held.declarations.get(nodeKey(file, node));
735
- if (id !== undefined && exported.has(id)) {
736
- found.push(id);
737
- }
738
- }
739
- }
740
- }
741
- return found;
742
- }
743
-
744
- /** The kinds of container whose export table, not its members' visibility, says what it publishes. */
745
- const EXPORTING_CONTAINERS: ReadonlySet<string> = new Set(["file", "namespace"]);
746
-
747
- /**
748
- * Every declaration a consumer of the library can name, a published file itself excluded:
749
- * what the published files export, with a namespace's exported members and any other
750
- * declaration's public and protected ones; a declaration a published one's type names; an
751
- * exported declaration a release tag marks; each declaration a `declare global` block holds
752
- * in a published file or one holding a published declaration; and, as `guessed`, each
753
- * declaration spelled as a type name the checker leaves unanswered.
754
- */
755
- function publishedAPI<Brand>(
756
- project: ProjectView<Brand>,
757
- published: readonly SourceFile[],
758
- exportsOf: ReadonlyMap<string, readonly string[]>,
759
- held: Inventory,
760
- byPath: ReadonlyMap<string, SourceFile>,
761
- tagged: readonly string[],
762
- ): { readonly ids: readonly string[]; readonly guessed: readonly string[] } {
763
- const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
764
- const children = childrenOf(held);
765
- const nodes = declarationNodes(held, byPath);
766
- const chains = aliasChains(project, held);
767
- const reached = new Set<string>();
768
- const entered = new Set<string>();
769
- const guessed = new Set<string>();
770
- const pending: string[] = [];
771
- const reach = (id: string): void => {
772
- const file = byId.get(id)?.kind === "file";
773
- if (!(file ? entered : reached).has(id)) {
774
- (file ? entered : reached).add(id);
775
- pending.push(id);
776
- }
777
- };
778
- for (const id of tagged) {
779
- reach(id);
780
- }
781
- for (const file of published) {
782
- for (const id of exportsOf.get(file.fileName) ?? []) {
783
- reach(id);
784
- }
785
- }
786
- const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
787
- const blocks = held.symbols.filter((one) => isGlobalBlock(one, held, files));
788
- const fileOf = (id: string): string | undefined => {
789
- let at = byId.get(id);
790
- while (at !== undefined && at.kind !== "file") {
791
- at = byId.get(at.parent);
792
- }
793
- return at?.id;
794
- };
795
- const publishedFiles = published.flatMap(
796
- (file) => held.declarations.get(nodeKey(file, file)) ?? [],
797
- );
798
- const globalsReached = (): boolean => {
799
- const typed = new Set([...publishedFiles, ...[...reached].flatMap((id) => fileOf(id) ?? [])]);
800
- const before = pending.length;
801
- for (const block of blocks.filter((one) => typed.has(one.parent))) {
802
- reach(block.id);
803
- for (const member of children.get(block.id) ?? []) {
804
- if (member.kind !== "type-parameter") {
805
- reach(member.id);
806
- }
807
- }
808
- }
809
- return pending.length > before;
810
- };
811
- do {
812
- while (pending.length > 0) {
813
- const round: string[] = [];
814
- for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
815
- round.push(next);
816
- const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
817
- for (const member of children.get(next) ?? []) {
818
- const reachable = exporting
819
- ? member.exported
820
- : member.visibility !== "private" && member.visibility !== "private-name";
821
- if (member.kind !== "type-parameter" && reachable) {
822
- reach(member.id);
823
- }
824
- }
825
- }
826
- const named = round.flatMap((id) => {
827
- const symbol = byId.get(id);
828
- return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
829
- });
830
- const inferred = inferredTypeSymbols(
831
- project,
832
- round.flatMap((id) => {
833
- const symbol = byId.get(id);
834
- return symbol === undefined ? [] : nodes.nodesOf(symbol);
835
- }),
836
- );
837
- for (const symbol of inferred.types) {
838
- for (const target of chains.chainOf(symbol)) {
839
- reach(target.id);
840
- }
841
- }
842
- for (const symbol of inferred.members) {
843
- for (const target of chains.chainOf(symbol)) {
844
- const member = byId.get(target.id);
845
- if (member?.visibility === "public" || member?.visibility === "protected") {
846
- reach(target.id);
847
- }
848
- }
849
- }
850
- project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
851
- if (symbol === UNANSWERED) {
852
- for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
853
- guessed.add(id);
854
- reach(id);
855
- }
856
- } else if (symbol !== undefined) {
857
- for (const target of chains.chainOf(symbol)) {
858
- reach(target.id);
859
- }
860
- }
861
- });
862
- }
863
- } while (globalsReached());
864
- return { ids: [...reached], guessed: [...guessed] };
865
- }
866
-
867
- /** The type names declarations of the inventory write, each file walked once, when first asked. */
868
- function declarationNodes(
869
- held: Inventory,
870
- byPath: ReadonlyMap<string, SourceFile>,
871
- ): {
872
- typeNamesOf(symbol: InventorySymbol): readonly Node[];
873
- nodesOf(symbol: InventorySymbol): readonly Node[];
874
- } {
875
- const walked = new Map<string, { byId: Map<string, Node[]>; nodes: Set<Node> }>();
876
- const nodesIn = (path: string): { byId: Map<string, Node[]>; nodes: Set<Node> } => {
877
- const known = walked.get(path);
878
- if (known !== undefined) {
879
- return known;
880
- }
881
- const found = { byId: new Map<string, Node[]>(), nodes: new Set<Node>() };
882
- const file = byPath.get(path);
883
- if (file !== undefined) {
884
- const visit = (node: Node): void => {
885
- const id = held.declarations.get(nodeKey(file, node));
886
- if (id !== undefined) {
887
- found.nodes.add(node);
888
- found.byId.set(id, [...(found.byId.get(id) ?? []), node]);
889
- }
890
- node.forEachChild(visit);
891
- };
892
- visit(file);
893
- }
894
- walked.set(path, found);
895
- return found;
896
- };
897
- return {
898
- typeNamesOf: (symbol) => {
899
- const { byId, nodes } = nodesIn(symbol.position.path);
900
- return (byId.get(symbol.id) ?? []).flatMap((declaration) => typeNamesOf(declaration, nodes));
901
- },
902
- nodesOf: (symbol) => nodesIn(symbol.position.path).byId.get(symbol.id) ?? [],
903
- };
904
- }
905
-
906
- /** How deep the type arguments and constituents of an inferred type are followed. */
907
- const INFERRED_DEPTH = 4;
908
-
909
- /**
910
- * The values one function body returns, each through the type assertions around it: the
911
- * value a caller receives carries the operand's type whatever type the assertion or the
912
- * signature names. A nested function's returns are its own.
913
- */
914
- function returnedValues(body: Node): readonly Expression[] {
915
- const found: Expression[] = [];
916
- const add = (value: Expression): void => {
917
- for (let at: Expression | undefined = value; at !== undefined;) {
918
- found.push(at);
919
- at =
920
- isAssertionExpression(at) || isSatisfiesExpression(at) || isParenthesizedExpression(at)
921
- ? at.expression
922
- : undefined;
923
- }
924
- };
925
- if (!isFunctionLikeDeclaration(body.parent) || body.parent.body !== body) {
926
- return found;
927
- }
928
- if (isArrowFunction(body.parent) && body.kind !== SyntaxKind.Block) {
929
- add(body as Expression);
930
- return found;
931
- }
932
- const visit = (node: Node): void => {
933
- if (isFunctionLikeDeclaration(node)) {
934
- return;
935
- }
936
- if (isReturnStatement(node) && node.expression !== undefined) {
937
- add(node.expression);
938
- }
939
- node.forEachChild(visit);
940
- };
941
- body.forEachChild(visit);
942
- return found;
943
- }
944
-
945
- /** The object literal one returned value is, written there or as the initializer of a variable `body` declares. */
946
- function returnedLiteral(value: Expression, body: Node): ObjectLiteralExpression | undefined {
947
- if (isObjectLiteralExpression(value)) {
948
- return value;
949
- }
950
- if (!isIdentifier(value)) {
951
- return undefined;
952
- }
953
- let found: ObjectLiteralExpression | undefined;
954
- const visit = (node: Node): void => {
955
- if (
956
- isVariableDeclaration(node) &&
957
- isIdentifier(node.name) &&
958
- node.name.text === value.text &&
959
- node.initializer !== undefined
960
- ) {
961
- let at: Expression = node.initializer;
962
- while (
963
- isAssertionExpression(at) ||
964
- isSatisfiesExpression(at) ||
965
- isParenthesizedExpression(at)
966
- ) {
967
- at = at.expression;
968
- }
969
- found = isObjectLiteralExpression(at) ? at : found;
970
- }
971
- if (!isFunctionLikeDeclaration(node)) {
972
- node.forEachChild(visit);
973
- }
974
- };
975
- body.forEachChild(visit);
976
- return found;
977
- }
978
-
979
- /**
980
- * The values one function body returns, and those each method of an object literal it
981
- * returns returns, to a fixed depth: a caller holding the literal calls its methods, and
982
- * receives what they return.
983
- */
984
- function returnedThroughLiterals(body: Node, depth = 0): readonly Expression[] {
985
- const values = returnedValues(body);
986
- if (depth >= INFERRED_DEPTH) {
987
- return values;
988
- }
989
- const found = [...values];
990
- for (const value of values) {
991
- for (const property of returnedLiteral(value, body)?.properties ?? []) {
992
- const fn = isMethodDeclaration(property)
993
- ? property
994
- : isPropertyAssignment(property) && isFunctionLikeDeclaration(property.initializer)
995
- ? property.initializer
996
- : undefined;
997
- if (fn?.body !== undefined) {
998
- found.push(...returnedThroughLiterals(fn.body, depth + 1));
999
- }
1000
- }
1001
- }
1002
- return found;
1003
- }
1004
-
1005
- /**
1006
- * The declarations of the types the compiler infers where a declaration writes none, and
1007
- * of the values a function returns: the type of a variable or a property with no
1008
- * annotation, the result of a function or a method with no result annotation, and the type
1009
- * of each value its body returns, each with the types its type arguments and its union or
1010
- * intersection constituents name, to a fixed depth; and as `members` the properties of each
1011
- * returned value's type. A question the checker leaves unanswered names nothing.
1012
- */
1013
- function inferredTypeSymbols<Brand>(
1014
- project: ProjectView<Brand>,
1015
- declarations: readonly Node[],
1016
- ): { readonly types: readonly TSSymbol[]; readonly members: readonly TSSymbol[] } {
1017
- const { queries } = project;
1018
- const valued: Node[] = declarations.flatMap((node) =>
1019
- (isVariableDeclaration(node) || isPropertyDeclaration(node)) &&
1020
- node.type === undefined &&
1021
- !isFunctionLikeDeclaration(node.initializer ?? node)
1022
- ? [node.name]
1023
- : [],
1024
- );
1025
- const returned = declarations.flatMap((node) => {
1026
- const signature = isFunctionLikeDeclaration(node)
1027
- ? node
1028
- : isVariableDeclaration(node) &&
1029
- node.initializer !== undefined &&
1030
- isFunctionLikeDeclaration(node.initializer)
1031
- ? node.initializer
1032
- : undefined;
1033
- return signature?.body === undefined ? [] : returnedThroughLiterals(signature.body);
1034
- });
1035
- const types: Type[] = [];
1036
- const returnedTypes: Type[] = [];
1037
- queries.typesAt([...valued, ...returned]).forEach((type, at) => {
1038
- if (type !== UNANSWERED && type !== undefined) {
1039
- (at < valued.length ? types : returnedTypes).push(type);
1040
- }
1041
- });
1042
- types.push(...returnedTypes);
1043
- for (const node of declarations) {
1044
- const signature = isFunctionLikeDeclaration(node)
1045
- ? node
1046
- : isVariableDeclaration(node) &&
1047
- node.type === undefined &&
1048
- node.initializer !== undefined &&
1049
- isFunctionLikeDeclaration(node.initializer)
1050
- ? node.initializer
1051
- : undefined;
1052
- if (signature === undefined || signature.type !== undefined || signature.body === undefined) {
1053
- continue;
1054
- }
1055
- const typed = queries.signatureOf(signature);
1056
- const result =
1057
- typed === UNANSWERED || typed === undefined ? undefined : queries.returnTypeOf(typed);
1058
- if (result !== UNANSWERED && result !== undefined) {
1059
- types.push(result);
1060
- }
1061
- }
1062
- const found = new Map<number, TSSymbol>();
1063
- const seen = new Set<Type>();
1064
- const visit = (type: Type, depth: number): void => {
1065
- if (depth > INFERRED_DEPTH || seen.has(type)) {
1066
- return;
1067
- }
1068
- seen.add(type);
1069
- const symbol = queries.symbolOfType(type);
1070
- if (symbol !== UNANSWERED && symbol !== undefined) {
1071
- found.set(symbol.id, symbol);
1072
- }
1073
- const parts = queries.constituents(type);
1074
- const args = queries.typeArguments(type);
1075
- for (const next of [
1076
- ...(parts === UNANSWERED || (parts.length === 1 && parts[0] === type) ? [] : parts),
1077
- ...(args === UNANSWERED ? [] : args),
1078
- ]) {
1079
- visit(next, depth + 1);
1080
- }
1081
- };
1082
- types.forEach((type) => {
1083
- visit(type, 0);
1084
- });
1085
- // A returned value carries each property of its type, one an anonymous type declares or
1086
- // an intersection merges included, which no type name reaches.
1087
- const members = new Map<number, TSSymbol>();
1088
- for (const type of returnedTypes) {
1089
- const parts = queries.constituents(type);
1090
- for (const one of [type, ...(parts === UNANSWERED ? [] : parts)]) {
1091
- const properties = queries.propertiesOf(one);
1092
- for (const property of properties === UNANSWERED ? [] : properties) {
1093
- members.set(property.id, property);
1094
- }
1095
- }
1096
- }
1097
- return { types: [...found.values()], members: [...members.values()] };
1098
- }
1099
-
1100
- /**
1101
- * The names of types one declaration's own syntax writes: in a type reference, a
1102
- * heritage clause or a type query. A function body is not read, and neither is a
1103
- * declaration nested in this one, which `nested` holds and which is read on its own.
1104
- */
1105
- function typeNamesOf(declaration: Node, nested: ReadonlySet<Node>): readonly Node[] {
1106
- const found: Node[] = [];
1107
- const visit = (node: Node): void => {
1108
- if (node !== declaration && nested.has(node)) {
1109
- return;
1110
- }
1111
- if (isTypeReferenceNode(node)) {
1112
- found.push(isQualifiedName(node.typeName) ? node.typeName.right : node.typeName);
1113
- } else if (isExpressionWithTypeArguments(node)) {
1114
- found.push(
1115
- isPropertyAccessExpression(node.expression) ? node.expression.name : node.expression,
1116
- );
1117
- } else if (isTypeQueryNode(node)) {
1118
- found.push(isQualifiedName(node.exprName) ? node.exprName.right : node.exprName);
1119
- }
1120
- const body = isFunctionLikeDeclaration(node) ? node.body : undefined;
1121
- node.forEachChild((child) => {
1122
- if (child !== body) {
1123
- visit(child);
1124
- }
1125
- });
1126
- };
1127
- visit(declaration);
1128
- return found;
1129
- }
1130
-
1131
704
  /**
1132
705
  * Keeps every declaration the configuration names and returns every string that
1133
706
  * named none, in configuration order. A string the configuration lists twice is one