@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.10

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 (288) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/mapper-connection.js +2 -1
  3. package/dist/bin/node-host.js +24 -5
  4. package/dist/src/analysis.js +245 -309
  5. package/dist/src/augmentations.js +124 -18
  6. package/dist/src/automatic-types.js +170 -0
  7. package/dist/src/build-scripts.js +425 -0
  8. package/dist/src/component-layer.js +4 -2
  9. package/dist/src/components.js +9 -9
  10. package/dist/src/config.js +6 -81
  11. package/dist/src/configuration-files.js +78 -188
  12. package/dist/src/configured-declarations.js +15 -20
  13. package/dist/src/conformance-record.js +2 -2
  14. package/dist/src/consumers.js +3 -30
  15. package/dist/src/contextual-uses.js +56 -9
  16. package/dist/src/contract/config.generated.js +558 -0
  17. package/dist/src/contract/contract.generated.js +1244 -0
  18. package/dist/src/contract/report.generated.js +862 -0
  19. package/dist/src/convention-rows.js +146 -1
  20. package/dist/src/conventions.js +136 -70
  21. package/dist/src/decorator.js +21 -18
  22. package/dist/src/dependencies.js +280 -62
  23. package/dist/src/edges.js +38 -122
  24. package/dist/src/element-access.js +4 -6
  25. package/dist/src/entry-point-gaps.js +9 -18
  26. package/dist/src/entry-points.js +106 -21
  27. package/dist/src/enum-group.js +3 -10
  28. package/dist/src/erased-stores.js +4 -6
  29. package/dist/src/exempt-classes.js +1 -63
  30. package/dist/src/exempt.js +21 -40
  31. package/dist/src/failure.js +36 -0
  32. package/dist/src/file-facts.js +2 -25
  33. package/dist/src/findings/completion.js +225 -63
  34. package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
  35. package/dist/src/findings/emitters.js +6 -6
  36. package/dist/src/findings/interfaces.js +4 -5
  37. package/dist/src/findings/intra-function.js +53 -19
  38. package/dist/src/findings/non-code-artifacts.js +71 -39
  39. package/dist/src/findings/reads-and-writes.js +16 -13
  40. package/dist/src/findings/self-check.js +10 -12
  41. package/dist/src/findings/unused-declarations.js +57 -28
  42. package/dist/src/findings/visibility-narrowing.js +40 -18
  43. package/dist/src/findings-pass.js +8 -15
  44. package/dist/src/framework-lifecycle.js +7 -7
  45. package/dist/src/fs/document.js +52 -0
  46. package/dist/src/fs/manifest.js +72 -0
  47. package/dist/src/fs/tree.js +88 -0
  48. package/dist/src/generated-file.js +18 -4
  49. package/dist/src/git-ignore.js +99 -0
  50. package/dist/src/graph.js +9 -4
  51. package/dist/src/html-entries.js +6 -33
  52. package/dist/src/import-bindings.js +97 -0
  53. package/dist/src/in-operands.js +2 -1
  54. package/dist/src/index.js +1 -1
  55. package/dist/src/indexed-access.js +130 -28
  56. package/dist/src/injection-container.js +1 -1
  57. package/dist/src/interface-satisfaction.js +30 -12
  58. package/dist/src/intra-function-parts.js +341 -69
  59. package/dist/src/inventory.js +54 -29
  60. package/dist/src/javascript-file.js +33 -0
  61. package/dist/src/json-document.js +198 -67
  62. package/dist/src/keyof-comparisons.js +4 -6
  63. package/dist/src/kinds.js +3 -366
  64. package/dist/src/load/classify.js +270 -0
  65. package/dist/src/load/diagnostics.js +80 -0
  66. package/dist/src/load/discover.js +277 -0
  67. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  68. package/dist/src/load/expected-module.js +136 -0
  69. package/dist/src/load/load.js +27 -0
  70. package/dist/src/load/module-lookup.js +37 -0
  71. package/dist/src/load/plan.js +313 -0
  72. package/dist/src/load/specifiers.js +115 -0
  73. package/dist/src/load/synthesize.js +140 -0
  74. package/dist/src/load/test-globals.js +207 -0
  75. package/dist/src/load/test-joins.js +82 -0
  76. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  77. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +88 -61
  78. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
  79. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  80. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  81. package/dist/src/manifest.js +49 -54
  82. package/dist/src/matrix.js +94 -13
  83. package/dist/src/mdx-entries.js +174 -0
  84. package/dist/src/module-declarations.js +9 -13
  85. package/dist/src/outside-tests.js +1 -1
  86. package/dist/src/overrides.js +41 -10
  87. package/dist/src/partial.js +2 -1
  88. package/dist/src/paths.js +26 -15
  89. package/dist/src/position.js +2 -1
  90. package/dist/src/print.js +11 -78
  91. package/dist/src/product.js +13 -0
  92. package/dist/src/query.js +1 -0
  93. package/dist/src/ref.js +11 -0
  94. package/dist/src/references.js +52 -31
  95. package/dist/src/report-schema.js +51 -0
  96. package/dist/src/report.js +59 -41
  97. package/dist/src/reporters/annotations.js +15 -8
  98. package/dist/src/reporters/sarif.js +16 -10
  99. package/dist/src/reporters/template.js +4 -19
  100. package/dist/src/resolve.js +245 -727
  101. package/dist/src/roots.js +374 -116
  102. package/dist/src/run.js +66 -77
  103. package/dist/src/schema.js +320 -342
  104. package/dist/src/scope.js +38 -115
  105. package/dist/src/serialization-contract.js +649 -29
  106. package/dist/src/session.js +41 -29
  107. package/dist/src/source-files.js +40 -15
  108. package/dist/src/suppress-file.js +34 -29
  109. package/dist/src/suppress-inline.js +21 -4
  110. package/dist/src/suppress.js +40 -56
  111. package/dist/src/sweep.js +32 -17
  112. package/dist/src/symbol-keys.js +105 -6
  113. package/dist/src/template-field.js +9 -19
  114. package/dist/src/test-support.js +119 -18
  115. package/dist/src/text/compare.js +26 -0
  116. package/dist/src/text/glob.js +194 -0
  117. package/dist/src/text/named-files.js +54 -0
  118. package/dist/src/text/shell.js +159 -0
  119. package/dist/src/text/yaml.js +393 -0
  120. package/dist/src/type-errors.js +27 -221
  121. package/dist/src/type-parameter-reads.js +172 -0
  122. package/dist/src/unseen-loads.js +100 -0
  123. package/dist/src/unwrap.js +23 -0
  124. package/dist/src/usage.js +10 -0
  125. package/dist/src/verbs/analyze.js +181 -125
  126. package/dist/src/verbs/describe.js +9 -16
  127. package/dist/src/verbs/explain.js +14 -9
  128. package/dist/src/verbs/print-config.js +2 -1
  129. package/dist/src/verbs/print-projects.js +6 -4
  130. package/dist/src/verbs/print-retained.js +4 -2
  131. package/dist/src/verbs/print-roots.js +4 -2
  132. package/dist/src/verbs/verb.js +1 -5
  133. package/dist/src/verbs/version.js +14 -3
  134. package/dist/src/workflow-steps.js +128 -33
  135. package/jsr.json +1 -1
  136. package/package.json +5 -4
  137. package/src/analysis.ts +333 -457
  138. package/src/augmentations.ts +152 -23
  139. package/src/automatic-types.ts +228 -0
  140. package/src/build-scripts.ts +543 -0
  141. package/src/component-layer.ts +4 -2
  142. package/src/components.ts +9 -10
  143. package/src/config.ts +31 -299
  144. package/src/configuration-files.ts +100 -191
  145. package/src/configured-declarations.ts +18 -23
  146. package/src/conformance-record.ts +2 -2
  147. package/src/consumers.ts +6 -57
  148. package/src/contextual-uses.ts +64 -12
  149. package/src/contract/config.generated.ts +818 -0
  150. package/src/contract/contract.generated.ts +1308 -0
  151. package/src/contract/report.generated.ts +1237 -0
  152. package/src/convention-rows.ts +186 -1
  153. package/src/conventions.ts +166 -90
  154. package/src/decorator.ts +28 -18
  155. package/src/dependencies.ts +356 -78
  156. package/src/edges.ts +41 -140
  157. package/src/element-access.ts +4 -7
  158. package/src/entry-point-gaps.ts +11 -20
  159. package/src/entry-points.ts +124 -28
  160. package/src/enum-group.ts +2 -11
  161. package/src/erased-stores.ts +3 -7
  162. package/src/exempt-classes.ts +1 -75
  163. package/src/exempt.ts +36 -50
  164. package/src/failure.ts +63 -0
  165. package/src/file-facts.ts +9 -24
  166. package/src/finding.ts +24 -9
  167. package/src/findings/boundary.ts +2 -0
  168. package/src/findings/completion.ts +297 -77
  169. package/src/findings/dependencies-and-module-machinery.ts +90 -31
  170. package/src/findings/emitter.ts +8 -9
  171. package/src/findings/emitters.ts +8 -8
  172. package/src/findings/interfaces.ts +4 -6
  173. package/src/findings/intra-function.ts +64 -20
  174. package/src/findings/non-code-artifacts.ts +88 -50
  175. package/src/findings/reads-and-writes.ts +20 -16
  176. package/src/findings/self-check.ts +11 -17
  177. package/src/findings/unused-declarations.ts +65 -28
  178. package/src/findings/visibility-narrowing.ts +51 -19
  179. package/src/findings-pass.ts +10 -19
  180. package/src/framework-lifecycle.ts +7 -7
  181. package/src/fs/document.ts +70 -0
  182. package/src/fs/manifest.ts +92 -0
  183. package/src/fs/tree.ts +110 -0
  184. package/src/generated-file.ts +23 -4
  185. package/src/git-ignore.ts +122 -0
  186. package/src/graph.ts +11 -3
  187. package/src/host.ts +17 -14
  188. package/src/html-entries.ts +6 -33
  189. package/src/import-bindings.ts +128 -0
  190. package/src/in-operands.ts +2 -1
  191. package/src/index.ts +1 -1
  192. package/src/indexed-access.ts +152 -30
  193. package/src/injection-container.ts +1 -1
  194. package/src/interface-satisfaction.ts +33 -13
  195. package/src/intra-function-parts.ts +449 -85
  196. package/src/inventory.ts +73 -27
  197. package/src/javascript-file.ts +37 -0
  198. package/src/json-document.ts +241 -76
  199. package/src/keyof-comparisons.ts +3 -7
  200. package/src/kinds.ts +5 -413
  201. package/src/load/classify.ts +397 -0
  202. package/src/load/diagnostics.ts +144 -0
  203. package/src/load/discover.ts +455 -0
  204. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  205. package/src/load/expected-module.ts +188 -0
  206. package/src/load/load.ts +73 -0
  207. package/src/load/module-lookup.ts +50 -0
  208. package/src/load/plan.ts +504 -0
  209. package/src/load/specifiers.ts +168 -0
  210. package/src/load/synthesize.ts +211 -0
  211. package/src/load/test-globals.ts +251 -0
  212. package/src/load/test-joins.ts +98 -0
  213. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  214. package/src/{workspace-programs.ts → load/workspace-programs.ts} +127 -80
  215. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
  216. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  217. package/src/{workspace.ts → load/workspace.ts} +90 -134
  218. package/src/manifest.ts +81 -75
  219. package/src/matrix.ts +154 -18
  220. package/src/mdx-entries.ts +218 -0
  221. package/src/module-declarations.ts +11 -13
  222. package/src/outside-tests.ts +1 -1
  223. package/src/overrides.ts +51 -12
  224. package/src/partial.ts +3 -2
  225. package/src/paths.ts +27 -15
  226. package/src/position.ts +2 -1
  227. package/src/print.ts +12 -91
  228. package/src/product.ts +18 -0
  229. package/src/query.ts +11 -0
  230. package/src/ref.ts +13 -0
  231. package/src/references.ts +62 -32
  232. package/src/report-schema.ts +68 -0
  233. package/src/report.ts +105 -215
  234. package/src/reporters/annotations.ts +22 -11
  235. package/src/reporters/json.ts +1 -1
  236. package/src/reporters/reporter.ts +2 -4
  237. package/src/reporters/reporters.ts +1 -1
  238. package/src/reporters/sarif.ts +29 -16
  239. package/src/reporters/template.ts +4 -20
  240. package/src/reporters/text.ts +3 -3
  241. package/src/resolve.ts +283 -1111
  242. package/src/roots.ts +490 -137
  243. package/src/run.ts +75 -91
  244. package/src/schema.ts +391 -387
  245. package/src/scope.ts +46 -138
  246. package/src/serialization-contract.ts +818 -26
  247. package/src/session.ts +77 -54
  248. package/src/source-files.ts +67 -17
  249. package/src/suppress-file.ts +36 -34
  250. package/src/suppress-inline.ts +20 -3
  251. package/src/suppress.ts +52 -63
  252. package/src/sweep.ts +53 -18
  253. package/src/symbol-keys.ts +131 -7
  254. package/src/template-field.ts +15 -25
  255. package/src/test-support.ts +155 -18
  256. package/src/text/compare.ts +27 -0
  257. package/src/text/glob.ts +210 -0
  258. package/src/text/named-files.inputs.json +1522 -0
  259. package/src/text/named-files.ts +68 -0
  260. package/src/text/shell.ts +161 -0
  261. package/src/text/yaml.ts +423 -0
  262. package/src/type-errors.ts +35 -273
  263. package/src/type-parameter-reads.ts +227 -0
  264. package/src/unseen-loads.ts +134 -0
  265. package/src/unwrap.ts +34 -0
  266. package/src/usage.ts +10 -0
  267. package/src/verbs/analyze.ts +245 -161
  268. package/src/verbs/describe.ts +14 -15
  269. package/src/verbs/explain.ts +15 -13
  270. package/src/verbs/print-config.ts +2 -1
  271. package/src/verbs/print-projects.ts +6 -4
  272. package/src/verbs/print-retained.ts +4 -2
  273. package/src/verbs/print-roots.ts +4 -2
  274. package/src/verbs/verb.ts +2 -8
  275. package/src/verbs/version.ts +15 -3
  276. package/src/workflow-steps.ts +149 -34
  277. package/dist/src/discover.js +0 -347
  278. package/dist/src/glob.js +0 -65
  279. package/dist/src/pnpm-workspace.js +0 -120
  280. package/dist/src/setup-failure.js +0 -23
  281. package/dist/src/test-joins.js +0 -101
  282. package/dist/src/version.js +0 -7
  283. package/src/discover.ts +0 -510
  284. package/src/glob.ts +0 -67
  285. package/src/pnpm-workspace.ts +0 -135
  286. package/src/setup-failure.ts +0 -39
  287. package/src/test-joins.ts +0 -120
  288. package/src/version.ts +0 -7
@@ -7,11 +7,21 @@
7
7
  * as `import.meta` or `process.env`, so a use of its member is a use of it.
8
8
  */
9
9
 
10
- import type { Inventory, InventorySymbol } from "./inventory.ts";
10
+ import {
11
+ isInterfaceDeclaration,
12
+ isModuleBlock,
13
+ isModuleDeclaration,
14
+ type Node,
15
+ } from "@typescript/native/ast";
16
+ import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
17
+ import { referenceKey } from "./matrix.ts";
11
18
  import type { Reference } from "./references.ts";
19
+ import type { ProjectView } from "./session.ts";
20
+ import type { SourceFiles } from "./source-files.ts";
21
+ import type { NodeHandle } from "@typescript/native/sync";
12
22
 
13
23
  /** Whether one declaration is a `declare global` block: a module-level namespace named `global`. */
14
- function isGlobalBlock(
24
+ export function isGlobalBlock(
15
25
  symbol: InventorySymbol,
16
26
  held: Inventory,
17
27
  files: ReadonlySet<string>,
@@ -25,25 +35,24 @@ function isGlobalBlock(
25
35
  }
26
36
 
27
37
  /**
28
- * For each reference to a declaration an augmentation block, or the global scope of a
29
- * file in `scripts`, holds, a read at the same place of every interface and namespace
30
- * between it and the block or the file, and of the block itself. `scripts` holds the
31
- * files that are no module, by identifier.
38
+ * Per declaration, the interfaces and namespaces between it and the augmentation block or
39
+ * the file in `scripts` that holds it, with the block itself, or none where no such block
40
+ * or file holds it; and whether that holder is the global scope rather than a module
41
+ * declaration. `scripts` holds the files that are no module, by identifier.
32
42
  */
33
- export function augmentationReferences(
43
+ function holdersOf(
34
44
  held: Inventory,
35
- found: readonly Reference[],
36
- scripts: ReadonlySet<string> = new Set(),
37
- ): readonly Reference[] {
45
+ scripts: ReadonlySet<string>,
46
+ ): ((id: string) => { readonly ids: readonly string[]; readonly global: boolean }) | undefined {
38
47
  const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
39
48
  const globals = new Set(
40
49
  held.symbols.filter((one) => isGlobalBlock(one, held, files)).map((one) => one.id),
41
50
  );
42
51
  if (globals.size === 0 && held.modules.size === 0 && scripts.size === 0) {
43
- return [];
52
+ return undefined;
44
53
  }
45
54
  const byId = new Map(held.symbols.map((one) => [one.id, one]));
46
- const holders = (id: string): string[] => {
55
+ return (id) => {
47
56
  const ids: string[] = [];
48
57
  for (
49
58
  let at = byId.get(byId.get(id)?.parent ?? "");
@@ -51,25 +60,145 @@ export function augmentationReferences(
51
60
  at = byId.get(at.parent)
52
61
  ) {
53
62
  if (globals.has(at.id) || held.modules.has(at.id)) {
54
- return [...ids, at.id];
63
+ return { ids: [...ids, at.id], global: globals.has(at.id) };
55
64
  }
56
65
  if (scripts.has(at.id)) {
57
- return ids;
66
+ return { ids, global: true };
58
67
  }
59
68
  if (at.kind === "interface" || at.kind === "namespace") {
60
69
  ids.push(at.id);
61
70
  }
62
71
  }
72
+ return { ids: [], global: false };
73
+ };
74
+ }
75
+
76
+ /**
77
+ * The ambient interfaces of the global scope among `interfaces`: each one a declaration
78
+ * file or a `declare` holds that a `declare global` block, or the top level of a file in
79
+ * `scripts`, declares, directly or inside a namespace there.
80
+ */
81
+ export function ambientGlobalInterfaces(
82
+ held: Inventory,
83
+ interfaces: readonly string[],
84
+ scripts: ReadonlySet<string>,
85
+ ): readonly string[] {
86
+ const holders = holdersOf(held, scripts);
87
+ return holders === undefined
88
+ ? []
89
+ : interfaces.filter((id) => held.ambient.has(id) && holders(id).global);
90
+ }
91
+
92
+ /**
93
+ * Each ambient global interface of the project's own files that merges into an interface
94
+ * declared outside them, with every interface, namespace and block between it and the
95
+ * global scope: the outside declaration names it wherever a value is typed by it. Where
96
+ * every outside declaration is a package's, not the compiler's library, and declares no
97
+ * member, each member it declares as well: such an interface is a slot the package reads
98
+ * by type, as `keyof` does, in declarations the analysis does not walk.
99
+ */
100
+ export function mergedGlobalInterfaces<Brand>(
101
+ project: ProjectView<Brand>,
102
+ held: Inventory,
103
+ files: SourceFiles,
104
+ ): readonly string[] {
105
+ const scripts = new Set<string>();
106
+ const names = new Map<string, Node>();
107
+ for (const file of files.byName.values()) {
108
+ const fileId = held.declarations.get(nodeKey(file, file));
109
+ if (fileId !== undefined && file.externalModuleIndicator === undefined) {
110
+ scripts.add(fileId);
111
+ }
112
+ const visit = (node: Node): void => {
113
+ if (isInterfaceDeclaration(node)) {
114
+ const id = held.declarations.get(nodeKey(file, node));
115
+ if (id !== undefined) {
116
+ names.set(id, node.name);
117
+ }
118
+ } else if (isModuleDeclaration(node) || isModuleBlock(node)) {
119
+ node.forEachChild(visit);
120
+ }
121
+ };
122
+ file.forEachChild(visit);
123
+ }
124
+ const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
125
+ const asked = [...names].filter(([id]) => global.has(id));
126
+ if (asked.length === 0) {
63
127
  return [];
128
+ }
129
+ const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
130
+ const holders = holdersOf(held, scripts);
131
+ const empty = (handle: NodeHandle): boolean => {
132
+ if (project.program.getSourceFileMetadata(handle.path)?.isDefaultLibrary === true) {
133
+ return false;
134
+ }
135
+ const node = project.declarationAt(handle)?.node;
136
+ return node !== undefined && isInterfaceDeclaration(node) && node.members.length === 0;
64
137
  };
65
- return found.flatMap((reference) =>
66
- holders(reference.to).map((to) => ({
67
- from: reference.from,
68
- to,
69
- position: reference.position,
70
- use: "read" as const,
71
- resolution: reference.resolution,
72
- test: reference.test,
73
- })),
138
+ return [
139
+ ...new Set(
140
+ asked.flatMap(([id], at) => {
141
+ const symbol = answers[at];
142
+ if (typeof symbol !== "object") {
143
+ return [];
144
+ }
145
+ const outside = symbol.declarations.filter((handle) => !files.byName.has(handle.path));
146
+ if (outside.length === 0) {
147
+ return [];
148
+ }
149
+ const members = outside.every(empty)
150
+ ? held.symbols.filter((one) => one.parent === id).map((one) => one.id)
151
+ : [];
152
+ return [id, ...(holders?.(id).ids ?? []), ...members];
153
+ }),
154
+ ),
155
+ ];
156
+ }
157
+
158
+ /**
159
+ * For each reference to a declaration an augmentation block, or the global scope of a
160
+ * file in `scripts` (the files that are no module), holds, a read at the same place of
161
+ * every interface and namespace between it and the block or the file, and of the block
162
+ * itself. A read through an interface of `merged`, an ambient global interface merging
163
+ * into a language or library one, is a production read whoever writes it: it types a
164
+ * value the runtime provides, so a test reading a member needs it as code does.
165
+ */
166
+ export function augmentationReferences(
167
+ held: Inventory,
168
+ found: readonly Reference[],
169
+ scripts: ReadonlySet<string> = new Set(),
170
+ merged: ReadonlySet<string> = new Set(),
171
+ ): readonly Reference[] {
172
+ const holders = holdersOf(held, scripts);
173
+ if (holders === undefined) {
174
+ return [];
175
+ }
176
+ const fileOf = new Map(
177
+ held.symbols.filter((one) => one.kind === "file").map((one) => [one.position.path, one.id]),
74
178
  );
179
+ // Two uses at one place of declarations one block holds read the block once there.
180
+ const made = new Set(found.map(referenceKey));
181
+ const reads = new Map<string, Reference>();
182
+ for (const reference of found) {
183
+ const { ids } = holders(reference.to);
184
+ // A test's read through such an interface is held by the test file, which the test
185
+ // runner loads whichever of its declarations runs.
186
+ const typed = reference.test && ids.some((id) => merged.has(id));
187
+ const from = typed ? (fileOf.get(reference.position.path) ?? reference.from) : reference.from;
188
+ for (const to of ids) {
189
+ const read: Reference = {
190
+ from,
191
+ to,
192
+ position: reference.position,
193
+ use: "read",
194
+ resolution: reference.resolution,
195
+ test: reference.test && !typed,
196
+ };
197
+ const key = referenceKey(read);
198
+ if (!made.has(key) && !reads.has(key)) {
199
+ reads.set(key, read);
200
+ }
201
+ }
202
+ }
203
+ return [...reads.values()];
75
204
  }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * The global names and ambient modules of the `@types` packages a compiler before version 6
3
+ * includes automatically: every package of each type root when the configuration lists no
4
+ * `types`. Version 6 and later include none, so the analysis program holds none of their
5
+ * declarations, and a use of one resolves to nothing; each such use is a use of the package.
6
+ */
7
+
8
+ import {
9
+ isClassDeclaration,
10
+ isEnumDeclaration,
11
+ isExportAssignment,
12
+ isExportDeclaration,
13
+ isFunctionDeclaration,
14
+ isIdentifier,
15
+ isImportDeclaration,
16
+ isImportEqualsDeclaration,
17
+ isInterfaceDeclaration,
18
+ isModuleBlock,
19
+ isModuleDeclaration,
20
+ isStringLiteral,
21
+ isTypeAliasDeclaration,
22
+ isVariableStatement,
23
+ SyntaxKind,
24
+ type Node,
25
+ type SourceFile,
26
+ type Statement,
27
+ } from "@typescript/native/ast";
28
+ import type { ParseFile } from "./configuration-files.ts";
29
+ import type { Host } from "./host.ts";
30
+ import { ancestors, dirnamePath, joinPath, resolvePath } from "./paths.ts";
31
+ import { entriesOf } from "./fs/tree.ts";
32
+ import { manifestAt } from "./fs/manifest.ts";
33
+
34
+ const TYPE_ROOT = "node_modules/@types";
35
+
36
+ /** What the automatically included `@types` packages of one configuration declare. */
37
+ export interface AutomaticTypes {
38
+ /** Per global name, the packages that declare it. */
39
+ readonly globals: ReadonlyMap<string, readonly string[]>;
40
+ /** Per ambient module name, the packages that declare it. */
41
+ readonly modules: ReadonlyMap<string, readonly string[]>;
42
+ }
43
+
44
+ const NONE: AutomaticTypes = { globals: new Map(), modules: new Map() };
45
+
46
+ /** What one installed type package declares, by its directory. */
47
+ interface PackageDeclarations {
48
+ readonly globals: readonly string[];
49
+ readonly modules: readonly string[];
50
+ }
51
+
52
+ /**
53
+ * The packages each host has read, so a run's configurations share one reading of a type
54
+ * root; a host reads one unchanging tree for its whole life.
55
+ */
56
+ const READ = new WeakMap<Host, Map<string, PackageDeclarations>>();
57
+
58
+ /** Whether one file is a module: it imports, exports or assigns an export at its top level. */
59
+ function isModuleFile(file: SourceFile): boolean {
60
+ return file.statements.some(
61
+ (statement) =>
62
+ isImportDeclaration(statement) ||
63
+ isImportEqualsDeclaration(statement) ||
64
+ isExportDeclaration(statement) ||
65
+ isExportAssignment(statement) ||
66
+ ((statement as { readonly modifiers?: readonly Node[] }).modifiers ?? []).some(
67
+ (modifier) => modifier.kind === SyntaxKind.ExportKeyword,
68
+ ),
69
+ );
70
+ }
71
+
72
+ /** The names one statement of a global scope declares, and the ambient modules it declares. */
73
+ function declared(statement: Statement, globals: string[], modules: string[]): void {
74
+ if (isVariableStatement(statement)) {
75
+ for (const declaration of statement.declarationList.declarations) {
76
+ if (isIdentifier(declaration.name)) {
77
+ globals.push(declaration.name.text);
78
+ }
79
+ }
80
+ } else if (
81
+ isFunctionDeclaration(statement) ||
82
+ isClassDeclaration(statement) ||
83
+ isInterfaceDeclaration(statement) ||
84
+ isTypeAliasDeclaration(statement) ||
85
+ isEnumDeclaration(statement)
86
+ ) {
87
+ if (statement.name !== undefined) {
88
+ globals.push(statement.name.text);
89
+ }
90
+ } else if (isModuleDeclaration(statement)) {
91
+ if (isStringLiteral(statement.name)) {
92
+ modules.push(statement.name.text);
93
+ } else {
94
+ globals.push(statement.name.text);
95
+ }
96
+ }
97
+ }
98
+
99
+ /**
100
+ * What one declaration file declares in the global scope: its top level where it is no
101
+ * module, and the body of each `declare global` block where it is one.
102
+ */
103
+ function fileDeclarations(file: SourceFile, globals: string[], modules: string[]): void {
104
+ if (!isModuleFile(file)) {
105
+ file.statements.forEach((statement) => {
106
+ declared(statement, globals, modules);
107
+ });
108
+ return;
109
+ }
110
+ for (const statement of file.statements) {
111
+ if (
112
+ isModuleDeclaration(statement) &&
113
+ isIdentifier(statement.name) &&
114
+ statement.name.text === "global" &&
115
+ statement.body !== undefined &&
116
+ isModuleBlock(statement.body)
117
+ ) {
118
+ statement.body.statements.forEach((inner) => {
119
+ declared(inner, globals, modules);
120
+ });
121
+ }
122
+ }
123
+ }
124
+
125
+ /** The entry declaration file of one installed type package's directory. */
126
+ function entryOf(host: Host, dir: string): string {
127
+ const manifest = manifestAt(host, dir);
128
+ const named = manifest?.["types"] ?? manifest?.["typings"];
129
+ if (typeof named === "string" && named !== "") {
130
+ const path = resolvePath(dir, named);
131
+ return path.endsWith(".ts") ? path : `${path}.d.ts`;
132
+ }
133
+ return joinPath(dir, "index.d.ts");
134
+ }
135
+
136
+ /**
137
+ * Every global name and ambient module one installed type package declares: in its entry
138
+ * file and each file a `/// <reference path>` of a file read reaches, inside the package.
139
+ */
140
+ function packageDeclarations(host: Host, dir: string, parse: ParseFile): PackageDeclarations {
141
+ const known = READ.get(host) ?? new Map<string, PackageDeclarations>();
142
+ READ.set(host, known);
143
+ const held = known.get(dir);
144
+ if (held !== undefined) {
145
+ return held;
146
+ }
147
+ const globals: string[] = [];
148
+ const modules: string[] = [];
149
+ const seen = new Set<string>();
150
+ const pending = [entryOf(host, dir)];
151
+ for (let path = pending.pop(); path !== undefined; path = pending.pop()) {
152
+ if (seen.has(path) || !path.startsWith(`${dir}/`)) {
153
+ continue;
154
+ }
155
+ seen.add(path);
156
+ let text: string;
157
+ try {
158
+ text = host.readFile(path);
159
+ } catch {
160
+ continue;
161
+ }
162
+ const file = parse(path, text);
163
+ fileDeclarations(file, globals, modules);
164
+ for (const reference of file.referencedFiles) {
165
+ pending.push(resolvePath(dirnamePath(path), reference.fileName));
166
+ }
167
+ }
168
+ const found = { globals, modules };
169
+ known.set(dir, found);
170
+ return found;
171
+ }
172
+
173
+ /**
174
+ * What the `@types` packages a compiler before version 6 would include for one
175
+ * configuration declare: none where its options list `types`, else every package of each
176
+ * `node_modules/@types` directory at or above the configuration's directory, the nearest
177
+ * holding a name winning.
178
+ */
179
+ export function automaticTypes(
180
+ host: Host,
181
+ configFile: string,
182
+ options: Readonly<Record<string, unknown>>,
183
+ parse: ParseFile,
184
+ ): AutomaticTypes {
185
+ if (options["types"] !== undefined) {
186
+ return NONE;
187
+ }
188
+ const globals = new Map<string, string[]>();
189
+ const modules = new Map<string, string[]>();
190
+ const add = (into: Map<string, string[]>, name: string, named: string): void => {
191
+ const held = into.get(name) ?? [];
192
+ if (!held.includes(named)) {
193
+ into.set(name, [...held, named]);
194
+ }
195
+ };
196
+ const read = new Set<string>();
197
+ for (const root of typeRoots(configFile, options)) {
198
+ for (const entry of entriesOf(host, root)) {
199
+ if (read.has(entry.name) || host.kindOf(joinPath(root, entry.name)) !== "directory") {
200
+ continue;
201
+ }
202
+ read.add(entry.name);
203
+ const named = `@types/${entry.name}`;
204
+ const found = packageDeclarations(host, joinPath(root, entry.name), parse);
205
+ found.globals.forEach((name) => {
206
+ add(globals, name, named);
207
+ });
208
+ found.modules.forEach((name) => {
209
+ add(modules, name, named);
210
+ });
211
+ }
212
+ }
213
+ return { globals, modules };
214
+ }
215
+
216
+ /**
217
+ * The type roots of one configuration that a package manager installs into: those its
218
+ * `typeRoots` lists, else each `node_modules/@types` directory at or above its directory.
219
+ */
220
+ function typeRoots(configFile: string, options: Readonly<Record<string, unknown>>): string[] {
221
+ const listed = options["typeRoots"];
222
+ if (Array.isArray(listed)) {
223
+ return listed.filter(
224
+ (root): root is string => typeof root === "string" && root.endsWith(`/${TYPE_ROOT}`),
225
+ );
226
+ }
227
+ return ancestors(dirnamePath(configFile)).map((dir) => joinPath(dir, TYPE_ROOT));
228
+ }