@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11

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 (276) 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 +181 -341
  5. package/dist/src/augmentations.js +106 -13
  6. package/dist/src/automatic-types.js +10 -29
  7. package/dist/src/build-scripts.js +425 -0
  8. package/dist/src/component-layer.js +4 -2
  9. package/dist/src/components.js +2 -3
  10. package/dist/src/config.js +6 -82
  11. package/dist/src/configuration-files.js +28 -176
  12. package/dist/src/configured-declarations.js +15 -20
  13. package/dist/src/conformance-record.js +1 -1
  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 +73 -3
  20. package/dist/src/conventions.js +36 -66
  21. package/dist/src/dependencies.js +103 -40
  22. package/dist/src/edges.js +38 -122
  23. package/dist/src/element-access.js +4 -6
  24. package/dist/src/entry-point-gaps.js +2 -8
  25. package/dist/src/entry-points.js +11 -27
  26. package/dist/src/enum-group.js +3 -10
  27. package/dist/src/erased-stores.js +4 -6
  28. package/dist/src/exempt-classes.js +1 -63
  29. package/dist/src/exempt.js +21 -40
  30. package/dist/src/failure.js +36 -0
  31. package/dist/src/file-facts.js +2 -25
  32. package/dist/src/findings/completion.js +139 -72
  33. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  34. package/dist/src/findings/emitters.js +5 -5
  35. package/dist/src/findings/interfaces.js +4 -5
  36. package/dist/src/findings/intra-function.js +13 -10
  37. package/dist/src/findings/non-code-artifacts.js +7 -8
  38. package/dist/src/findings/reads-and-writes.js +16 -13
  39. package/dist/src/findings/self-check.js +10 -12
  40. package/dist/src/findings/unused-declarations.js +41 -32
  41. package/dist/src/findings/visibility-narrowing.js +19 -16
  42. package/dist/src/findings-pass.js +8 -15
  43. package/dist/src/framework-lifecycle.js +7 -7
  44. package/dist/src/fs/document.js +52 -0
  45. package/dist/src/fs/manifest.js +72 -0
  46. package/dist/src/fs/tree.js +88 -0
  47. package/dist/src/generated-file.js +7 -4
  48. package/dist/src/git-ignore.js +39 -26
  49. package/dist/src/graph.js +5 -3
  50. package/dist/src/html-entries.js +6 -33
  51. package/dist/src/in-operands.js +2 -1
  52. package/dist/src/index.js +1 -1
  53. package/dist/src/injection-container.js +1 -1
  54. package/dist/src/intra-function-parts.js +63 -43
  55. package/dist/src/inventory.js +2 -21
  56. package/dist/src/javascript-file.js +33 -0
  57. package/dist/src/json-document.js +198 -67
  58. package/dist/src/keyof-comparisons.js +4 -6
  59. package/dist/src/kinds.js +3 -366
  60. package/dist/src/load/classify.js +270 -0
  61. package/dist/src/load/diagnostics.js +80 -0
  62. package/dist/src/load/discover.js +277 -0
  63. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  64. package/dist/src/load/expected-module.js +136 -0
  65. package/dist/src/load/load.js +27 -0
  66. package/dist/src/load/module-lookup.js +37 -0
  67. package/dist/src/load/plan.js +313 -0
  68. package/dist/src/load/specifiers.js +115 -0
  69. package/dist/src/load/synthesize.js +140 -0
  70. package/dist/src/load/test-globals.js +207 -0
  71. package/dist/src/load/test-joins.js +82 -0
  72. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  73. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  74. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
  75. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  76. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  77. package/dist/src/manifest.js +49 -54
  78. package/dist/src/matrix.js +76 -4
  79. package/dist/src/mdx-entries.js +3 -26
  80. package/dist/src/outside-tests.js +1 -1
  81. package/dist/src/partial.js +2 -1
  82. package/dist/src/paths.js +26 -15
  83. package/dist/src/position.js +2 -1
  84. package/dist/src/print.js +11 -79
  85. package/dist/src/product.js +13 -0
  86. package/dist/src/query.js +1 -0
  87. package/dist/src/ref.js +1 -0
  88. package/dist/src/references.js +48 -30
  89. package/dist/src/report-schema.js +51 -0
  90. package/dist/src/report.js +57 -41
  91. package/dist/src/reporters/annotations.js +15 -8
  92. package/dist/src/reporters/sarif.js +16 -10
  93. package/dist/src/reporters/template.js +4 -19
  94. package/dist/src/resolve.js +245 -729
  95. package/dist/src/roots.js +273 -87
  96. package/dist/src/run.js +66 -77
  97. package/dist/src/schema.js +320 -343
  98. package/dist/src/scope.js +38 -115
  99. package/dist/src/serialization-contract.js +595 -21
  100. package/dist/src/session.js +34 -29
  101. package/dist/src/source-files.js +1 -1
  102. package/dist/src/suppress-file.js +25 -28
  103. package/dist/src/suppress-inline.js +3 -2
  104. package/dist/src/suppress.js +33 -51
  105. package/dist/src/sweep.js +7 -9
  106. package/dist/src/symbol-keys.js +3 -5
  107. package/dist/src/template-field.js +9 -19
  108. package/dist/src/test-support.js +8 -11
  109. package/dist/src/text/compare.js +26 -0
  110. package/dist/src/text/glob.js +194 -0
  111. package/dist/src/text/named-files.js +54 -0
  112. package/dist/src/text/shell.js +159 -0
  113. package/dist/src/text/yaml.js +393 -0
  114. package/dist/src/type-errors.js +11 -232
  115. package/dist/src/unseen-loads.js +100 -0
  116. package/dist/src/unwrap.js +23 -0
  117. package/dist/src/usage.js +10 -0
  118. package/dist/src/verbs/analyze.js +166 -126
  119. package/dist/src/verbs/describe.js +9 -16
  120. package/dist/src/verbs/explain.js +10 -7
  121. package/dist/src/verbs/print-config.js +2 -1
  122. package/dist/src/verbs/print-projects.js +6 -4
  123. package/dist/src/verbs/print-retained.js +4 -2
  124. package/dist/src/verbs/print-roots.js +4 -2
  125. package/dist/src/verbs/verb.js +1 -5
  126. package/dist/src/verbs/version.js +14 -3
  127. package/dist/src/workflow-steps.js +62 -64
  128. package/jsr.json +1 -1
  129. package/package.json +5 -4
  130. package/src/analysis.ts +245 -502
  131. package/src/augmentations.ts +135 -17
  132. package/src/automatic-types.ts +11 -29
  133. package/src/build-scripts.ts +543 -0
  134. package/src/component-layer.ts +4 -2
  135. package/src/components.ts +2 -4
  136. package/src/config.ts +31 -302
  137. package/src/configuration-files.ts +34 -179
  138. package/src/configured-declarations.ts +18 -23
  139. package/src/conformance-record.ts +1 -1
  140. package/src/consumers.ts +6 -57
  141. package/src/contextual-uses.ts +64 -12
  142. package/src/contract/config.generated.ts +818 -0
  143. package/src/contract/contract.generated.ts +1308 -0
  144. package/src/contract/report.generated.ts +1237 -0
  145. package/src/convention-rows.ts +85 -3
  146. package/src/conventions.ts +46 -84
  147. package/src/dependencies.ts +118 -46
  148. package/src/edges.ts +41 -140
  149. package/src/element-access.ts +4 -7
  150. package/src/entry-point-gaps.ts +3 -9
  151. package/src/entry-points.ts +12 -38
  152. package/src/enum-group.ts +2 -11
  153. package/src/erased-stores.ts +3 -7
  154. package/src/exempt-classes.ts +1 -75
  155. package/src/exempt.ts +36 -50
  156. package/src/failure.ts +63 -0
  157. package/src/file-facts.ts +9 -24
  158. package/src/finding.ts +14 -9
  159. package/src/findings/boundary.ts +2 -0
  160. package/src/findings/completion.ts +181 -84
  161. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  162. package/src/findings/emitter.ts +8 -9
  163. package/src/findings/emitters.ts +7 -7
  164. package/src/findings/interfaces.ts +4 -6
  165. package/src/findings/intra-function.ts +13 -11
  166. package/src/findings/non-code-artifacts.ts +9 -11
  167. package/src/findings/reads-and-writes.ts +20 -16
  168. package/src/findings/self-check.ts +11 -17
  169. package/src/findings/unused-declarations.ts +45 -32
  170. package/src/findings/visibility-narrowing.ts +20 -17
  171. package/src/findings-pass.ts +10 -19
  172. package/src/framework-lifecycle.ts +7 -7
  173. package/src/fs/document.ts +70 -0
  174. package/src/fs/manifest.ts +92 -0
  175. package/src/fs/tree.ts +110 -0
  176. package/src/generated-file.ts +7 -4
  177. package/src/git-ignore.ts +50 -26
  178. package/src/graph.ts +5 -3
  179. package/src/host.ts +12 -5
  180. package/src/html-entries.ts +6 -33
  181. package/src/in-operands.ts +2 -1
  182. package/src/index.ts +1 -1
  183. package/src/injection-container.ts +1 -1
  184. package/src/intra-function-parts.ts +73 -44
  185. package/src/inventory.ts +2 -20
  186. package/src/javascript-file.ts +37 -0
  187. package/src/json-document.ts +241 -76
  188. package/src/keyof-comparisons.ts +3 -7
  189. package/src/kinds.ts +5 -413
  190. package/src/load/classify.ts +397 -0
  191. package/src/load/diagnostics.ts +144 -0
  192. package/src/load/discover.ts +455 -0
  193. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  194. package/src/load/expected-module.ts +188 -0
  195. package/src/load/load.ts +73 -0
  196. package/src/load/module-lookup.ts +50 -0
  197. package/src/load/plan.ts +504 -0
  198. package/src/load/specifiers.ts +168 -0
  199. package/src/load/synthesize.ts +211 -0
  200. package/src/load/test-globals.ts +251 -0
  201. package/src/load/test-joins.ts +98 -0
  202. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  203. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  204. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
  205. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  206. package/src/{workspace.ts → load/workspace.ts} +90 -134
  207. package/src/manifest.ts +81 -75
  208. package/src/matrix.ts +122 -4
  209. package/src/mdx-entries.ts +3 -26
  210. package/src/outside-tests.ts +1 -1
  211. package/src/partial.ts +3 -2
  212. package/src/paths.ts +27 -15
  213. package/src/position.ts +2 -1
  214. package/src/print.ts +12 -92
  215. package/src/product.ts +18 -0
  216. package/src/query.ts +11 -0
  217. package/src/ref.ts +1 -0
  218. package/src/references.ts +52 -31
  219. package/src/report-schema.ts +68 -0
  220. package/src/report.ts +101 -224
  221. package/src/reporters/annotations.ts +22 -11
  222. package/src/reporters/json.ts +1 -1
  223. package/src/reporters/reporter.ts +2 -4
  224. package/src/reporters/reporters.ts +1 -1
  225. package/src/reporters/sarif.ts +29 -16
  226. package/src/reporters/template.ts +4 -20
  227. package/src/reporters/text.ts +3 -3
  228. package/src/resolve.ts +283 -1114
  229. package/src/roots.ts +339 -104
  230. package/src/run.ts +75 -91
  231. package/src/schema.ts +391 -388
  232. package/src/scope.ts +46 -138
  233. package/src/serialization-contract.ts +763 -22
  234. package/src/session.ts +70 -54
  235. package/src/source-files.ts +1 -1
  236. package/src/suppress-file.ts +24 -33
  237. package/src/suppress-inline.ts +2 -2
  238. package/src/suppress.ts +43 -58
  239. package/src/sweep.ts +23 -9
  240. package/src/symbol-keys.ts +2 -5
  241. package/src/template-field.ts +15 -25
  242. package/src/test-support.ts +17 -13
  243. package/src/text/compare.ts +27 -0
  244. package/src/text/glob.ts +210 -0
  245. package/src/text/named-files.inputs.json +1522 -0
  246. package/src/text/named-files.ts +68 -0
  247. package/src/text/shell.ts +161 -0
  248. package/src/text/yaml.ts +423 -0
  249. package/src/type-errors.ts +18 -288
  250. package/src/unseen-loads.ts +134 -0
  251. package/src/unwrap.ts +34 -0
  252. package/src/usage.ts +10 -0
  253. package/src/verbs/analyze.ts +224 -162
  254. package/src/verbs/describe.ts +14 -15
  255. package/src/verbs/explain.ts +10 -11
  256. package/src/verbs/print-config.ts +2 -1
  257. package/src/verbs/print-projects.ts +6 -4
  258. package/src/verbs/print-retained.ts +4 -2
  259. package/src/verbs/print-roots.ts +4 -2
  260. package/src/verbs/verb.ts +2 -8
  261. package/src/verbs/version.ts +15 -3
  262. package/src/workflow-steps.ts +74 -64
  263. package/dist/src/base-url.js +0 -18
  264. package/dist/src/discover.js +0 -468
  265. package/dist/src/glob.js +0 -65
  266. package/dist/src/pnpm-workspace.js +0 -120
  267. package/dist/src/setup-failure.js +0 -23
  268. package/dist/src/test-joins.js +0 -101
  269. package/dist/src/version.js +0 -7
  270. package/src/base-url.ts +0 -24
  271. package/src/discover.ts +0 -653
  272. package/src/glob.ts +0 -67
  273. package/src/pnpm-workspace.ts +0 -135
  274. package/src/setup-failure.ts +0 -39
  275. package/src/test-joins.ts +0 -120
  276. package/src/version.ts +0 -7
package/dist/src/roots.js CHANGED
@@ -1,24 +1,26 @@
1
1
  /**
2
- * The root set of one project: every declaration the analysis keeps live without a
3
- * reference, and why.
4
- *
5
- * Every root seeds REACHABILITY, and no root counts as a reference. A root of a kind
6
- * that names a caller is live under reference counting as well, which the sweep
7
- * decides; the published API only supposes a consumer, so a published export is live
8
- * under reachability and still a candidate under reference counting.
2
+ * The root set of one project: every declaration kept live without a reference, and why.
3
+ * Every root seeds reachability and none counts as a reference; a root of a kind that
4
+ * names a caller is live under reference counting too, which the sweep decides, so a
5
+ * published export, which only supposes a consumer, stays a reference-counting candidate.
9
6
  */
10
- import { isExpressionWithTypeArguments, isFunctionLikeDeclaration, isPropertyAccessExpression, isQualifiedName, isTypeQueryNode, isTypeReferenceNode, isPropertyDeclaration, isVariableDeclaration, isVariableStatement, } from "@typescript/native/ast";
7
+ import { isArrowFunction, isAssertionExpression, isExpressionWithTypeArguments, isFunctionLikeDeclaration, isIdentifier, isMethodDeclaration, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAssignment, isReturnStatement, isSatisfiesExpression, isPropertyAccessExpression, isQualifiedName, isTypeQueryNode, isTypeReferenceNode, isPropertyDeclaration, isVariableDeclaration, isVariableStatement, SyntaxKind, } from "@typescript/native/ast";
11
8
  import { aliasChains } from "./alias-chain.js";
9
+ import { isGlobalBlock } from "./augmentations.js";
12
10
  import { filesNamedBy, outsideChain, relativeSpecifiers, } from "./configuration-files.js";
13
11
  import { conventionEntries } from "./conventions.js";
14
12
  import { entryPoints } from "./entry-points.js";
15
- import { globExpression } from "./glob.js";
16
13
  import { nodeKey } from "./inventory.js";
17
- import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
14
+ import { ancestors, dirnamePath, relativePath, resolvePath } from "./paths.js";
18
15
  import { byPosition } from "./position.js";
19
16
  import { UNANSWERED } from "./query.js";
20
17
  import { sourceFilesOf } from "./source-files.js";
21
18
  import { typeQueryAliases } from "./type-query-alias.js";
19
+ import { bytewise } from "./text/compare.js";
20
+ import { holdsManifest } from "./fs/manifest.js";
21
+ import { globExpression } from "./text/glob.js";
22
+ import { spelledFiles } from "./text/named-files.js";
23
+ import { commandDirectories, workflowCommands } from "./workflow-steps.js";
22
24
  /** Whether one declaration is written below the directory of a published workspace member. */
23
25
  function inPublishedMember(held, id, root, members) {
24
26
  const path = held.symbols.find((symbol) => symbol.id === id)?.position.path;
@@ -46,13 +48,8 @@ const OUTPUT_EXTENSION = /\.(?:d\.ts|d\.mts|d\.cts|js|mjs|cjs|jsx)$/u;
46
48
  const SOURCE_EXTENSION = /\.(?:d\.)?[cm]?[jt]sx?$/u;
47
49
  /** Whether no `package.json` sits between one file and the manifest's directory, which holds it. */
48
50
  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;
51
+ const between = ancestors(dirnamePath(file), dir);
52
+ return between.length > 0 && !between.slice(0, -1).some((at) => holdsManifest(host, at));
56
53
  }
57
54
  /**
58
55
  * The rooted files one manifest target reads back to by name: its path below the
@@ -162,13 +159,6 @@ export function matchRef(pattern, ref) {
162
159
  }
163
160
  return matchGlob(Array.from(pattern), Array.from(ref));
164
161
  }
165
- /** Two strings ordered bytewise, which is the order every set of a run is read in. */
166
- function compare(a, b) {
167
- if (a === b) {
168
- return 0;
169
- }
170
- return a < b ? -1 : 1;
171
- }
172
162
  /** The members of one declaration, indexed by the container that holds them. */
173
163
  function childrenOf(held) {
174
164
  const byParent = new Map();
@@ -184,15 +174,47 @@ function childrenOf(held) {
184
174
  return byParent;
185
175
  }
186
176
  /**
187
- * The roots one project holds, and every configured string that named nothing in it.
188
- *
189
- * A file a manifest member, an entry-file pattern or a tool's configuration names
190
- * is a root with everything its module exports: each name of the module's export
191
- * table, and where a name is a re-export, every link of the chain to the declaration
192
- * it carries, so a re-exported declaration is a root of the file that publishes it.
193
- * In a library, what a published file exports is rooted by the published API alone.
194
- * A file a runtime executes without reading its exports, a test file or a worker, is
195
- * a root by itself.
177
+ * Per package, the own files its command strings name as plain text, but those a root enters
178
+ * with all they export: a command can use no more of such a file than the root keeps live. A
179
+ * manifest's are its entries' whole command strings, and the target root's package has the
180
+ * workflow steps, git hooks and makefile recipes beside its own, with the directories its
181
+ * workflow steps and lefthook commands run in, where a `.` names that directory's index file.
182
+ */
183
+ function scriptHolds(entries, files, entered, host, targetRoot) {
184
+ const rooted = new Set(entered.filter((one) => one.exports && one.names === undefined).map((one) => one.file.fileName));
185
+ const spelled = [...files.byName.values()]
186
+ .filter((file) => !rooted.has(file.fileName))
187
+ .map((file) => ({ file, paths: [file.fileName, ...files.writtenAs(file)] }));
188
+ const cwd = host.workingDirectory();
189
+ const commands = new Map([
190
+ [
191
+ resolvePath(cwd, targetRoot),
192
+ [...workflowCommands(host, targetRoot), ...commandDirectories(host, targetRoot)],
193
+ ],
194
+ ]);
195
+ for (const entry of entries) {
196
+ if (entry.command !== undefined) {
197
+ const dir = resolvePath(cwd, entry.dir);
198
+ commands.set(dir, [...(commands.get(dir) ?? []), entry.command]);
199
+ }
200
+ }
201
+ const held = [];
202
+ for (const [dir, texts] of commands) {
203
+ const named = spelledFiles(texts.join("\n"), dir, spelled);
204
+ if (named.length > 0) {
205
+ const paths = named.map((file) => relativePath(targetRoot, file.fileName) ?? file.fileName);
206
+ held.push({ dir, files: paths });
207
+ }
208
+ }
209
+ return held;
210
+ }
211
+ /**
212
+ * The roots one project holds, and every configured string that named nothing in it. A
213
+ * file a manifest member, an entry-file pattern or a tool's configuration names is a root
214
+ * with each name of its export table and every link of a re-export chain to the
215
+ * declaration it carries; in a library, the published API alone roots what a published
216
+ * file exports. A file a runtime executes without reading its exports, a test file or a
217
+ * worker, is a root by itself.
196
218
  */
197
219
  export function roots(project, held, targetRoot, options) {
198
220
  const files = sourceFilesOf(project, targetRoot);
@@ -227,7 +249,7 @@ export function roots(project, held, targetRoot, options) {
227
249
  (options.publishedAPI || options.publishedMembers?.has(entry.dir) === true),
228
250
  })),
229
251
  ];
230
- const scripted = entries.flatMap(({ entry }) => entry.role === "script" ? files.named(entry.path) : []);
252
+ const scripted = entries.flatMap(({ entry }) => entry.role === "script" && entry.command === undefined ? files.named(entry.path) : []);
231
253
  const readBackTo = [
232
254
  ...scripted,
233
255
  ...points.filter((point) => READ_BACK_RULES.has(point.rule)).map((point) => point.file),
@@ -260,6 +282,10 @@ export function roots(project, held, targetRoot, options) {
260
282
  published.push(...entryFiles(entry));
261
283
  }
262
284
  }
285
+ if (options.publishedAPI) {
286
+ // A library's bundle hands what its entry exports to every program that loads it.
287
+ published.push(...points.filter((point) => point.rule === "bundler-entry").map((point) => point.file));
288
+ }
263
289
  const publishedNames = new Set(published.map((file) => file.fileName));
264
290
  const patterned = [];
265
291
  for (const pattern of options.entryFiles) {
@@ -273,6 +299,9 @@ export function roots(project, held, targetRoot, options) {
273
299
  const unmapped = [];
274
300
  const mapped = patterned.map(({ file }) => file);
275
301
  for (const { entry } of entries) {
302
+ if (entry.command !== undefined) {
303
+ continue;
304
+ }
276
305
  const named = entryFiles(entry);
277
306
  if (named.length === 0 && entry.role !== "script") {
278
307
  unmapped.push(entry);
@@ -295,6 +324,8 @@ export function roots(project, held, targetRoot, options) {
295
324
  for (const point of points) {
296
325
  enter(point.file, point.rule, point.source, point.exports, point.names);
297
326
  }
327
+ const heldScripts = scriptHolds(entries.map(({ entry }) => entry), files, entered, options.host, targetRoot);
328
+ const heldFiles = [...new Set(heldScripts.flatMap((one) => one.files))].flatMap((path) => files.byPath.get(path) ?? []);
298
329
  for (const file of files.byPath.values()) {
299
330
  for (const alias of typeQueryAliases(file)) {
300
331
  const source = alias.type.getText().replace(/\s+/gu, " ");
@@ -309,6 +340,7 @@ export function roots(project, held, targetRoot, options) {
309
340
  const { tables: exportsOf, named, answered, } = exportTables(project, held, [
310
341
  ...entered.filter((entry) => entry.exports).map((entry) => entry.file),
311
342
  ...published,
343
+ ...heldFiles,
312
344
  ]);
313
345
  for (const entry of entered) {
314
346
  const id = fileIds.get(entry.file.fileName);
@@ -339,13 +371,22 @@ export function roots(project, held, targetRoot, options) {
339
371
  const left = sites.get(a.id);
340
372
  const right = sites.get(b.id);
341
373
  const site = left === undefined || right === undefined ? 0 : byPosition(left, right);
342
- return site || compare(a.kind, b.kind) || compare(a.source, b.source);
374
+ return site || bytewise(a.kind, b.kind) || bytewise(a.source, b.source);
343
375
  });
344
376
  return {
345
377
  liveUnderReachability: ordered,
346
378
  unmatched,
347
- unanswered: answered ? api.guessed : held.symbols.map((symbol) => symbol.id),
379
+ unanswered: [
380
+ ...(answered ? api.guessed : held.symbols.map((symbol) => symbol.id)),
381
+ // A command runs a held file and may read any export of it, so the file, which reaches its
382
+ // top level, and its exports are held; a declaration neither reaches is still dead.
383
+ ...heldFiles.flatMap((file) => {
384
+ const id = fileIds.get(file.fileName);
385
+ return [...(id === undefined ? [] : [id]), ...(exportsOf.get(file.fileName) ?? [])];
386
+ }),
387
+ ],
348
388
  unmappedEntries,
389
+ heldScripts,
349
390
  };
350
391
  }
351
392
  /**
@@ -436,12 +477,12 @@ function releaseTagged(held, files) {
436
477
  /** The kinds of container whose export table, not its members' visibility, says what it publishes. */
437
478
  const EXPORTING_CONTAINERS = new Set(["file", "namespace"]);
438
479
  /**
439
- * Every declaration a consumer of the library can name: what the published files export,
440
- * and below each the members a consumer writes, a namespace's exported ones and any other
441
- * declaration's public and protected ones. A declaration a published one's type names is
442
- * published too, and so is an exported declaration a release tag marks. A type name the
443
- * checker leaves unanswered publishes every declaration spelled as it is, as `guessed`. A
444
- * published file is not itself published.
480
+ * Every declaration a consumer of the library can name, a published file itself excluded:
481
+ * what the published files export, with a namespace's exported members and any other
482
+ * declaration's public and protected ones; a declaration a published one's type names; an
483
+ * exported declaration a release tag marks; each declaration a `declare global` block holds
484
+ * in a published file or one holding a published declaration; and, as `guessed`, each
485
+ * declaration spelled as a type name the checker leaves unanswered.
445
486
  */
446
487
  function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
447
488
  const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
@@ -467,46 +508,80 @@ function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
467
508
  reach(id);
468
509
  }
469
510
  }
470
- while (pending.length > 0) {
471
- const round = [];
472
- for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
473
- round.push(next);
474
- const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
475
- for (const member of children.get(next) ?? []) {
476
- const reachable = exporting
477
- ? member.exported
478
- : member.visibility !== "private" && member.visibility !== "private-name";
479
- if (member.kind !== "type-parameter" && reachable) {
511
+ const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
512
+ const blocks = held.symbols.filter((one) => isGlobalBlock(one, held, files));
513
+ const fileOf = (id) => {
514
+ let at = byId.get(id);
515
+ while (at !== undefined && at.kind !== "file") {
516
+ at = byId.get(at.parent);
517
+ }
518
+ return at?.id;
519
+ };
520
+ const publishedFiles = published.flatMap((file) => held.declarations.get(nodeKey(file, file)) ?? []);
521
+ const globalsReached = () => {
522
+ const typed = new Set([...publishedFiles, ...[...reached].flatMap((id) => fileOf(id) ?? [])]);
523
+ const before = pending.length;
524
+ for (const block of blocks.filter((one) => typed.has(one.parent))) {
525
+ reach(block.id);
526
+ for (const member of children.get(block.id) ?? []) {
527
+ if (member.kind !== "type-parameter") {
480
528
  reach(member.id);
481
529
  }
482
530
  }
483
531
  }
484
- const named = round.flatMap((id) => {
485
- const symbol = byId.get(id);
486
- return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
487
- });
488
- for (const symbol of inferredTypeSymbols(project, round.flatMap((id) => {
489
- const symbol = byId.get(id);
490
- return symbol === undefined ? [] : nodes.nodesOf(symbol);
491
- }))) {
492
- for (const target of chains.chainOf(symbol)) {
493
- reach(target.id);
494
- }
495
- }
496
- project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
497
- if (symbol === UNANSWERED) {
498
- for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
499
- guessed.add(id);
500
- reach(id);
532
+ return pending.length > before;
533
+ };
534
+ do {
535
+ while (pending.length > 0) {
536
+ const round = [];
537
+ for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
538
+ round.push(next);
539
+ const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
540
+ for (const member of children.get(next) ?? []) {
541
+ const reachable = exporting
542
+ ? member.exported
543
+ : member.visibility !== "private" && member.visibility !== "private-name";
544
+ if (member.kind !== "type-parameter" && reachable) {
545
+ reach(member.id);
546
+ }
501
547
  }
502
548
  }
503
- else if (symbol !== undefined) {
549
+ const named = round.flatMap((id) => {
550
+ const symbol = byId.get(id);
551
+ return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
552
+ });
553
+ const inferred = inferredTypeSymbols(project, round.flatMap((id) => {
554
+ const symbol = byId.get(id);
555
+ return symbol === undefined ? [] : nodes.nodesOf(symbol);
556
+ }));
557
+ for (const symbol of inferred.types) {
504
558
  for (const target of chains.chainOf(symbol)) {
505
559
  reach(target.id);
506
560
  }
507
561
  }
508
- });
509
- }
562
+ for (const symbol of inferred.members) {
563
+ for (const target of chains.chainOf(symbol)) {
564
+ const member = byId.get(target.id);
565
+ if (member?.visibility === "public" || member?.visibility === "protected") {
566
+ reach(target.id);
567
+ }
568
+ }
569
+ }
570
+ project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
571
+ if (symbol === UNANSWERED) {
572
+ for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
573
+ guessed.add(id);
574
+ reach(id);
575
+ }
576
+ }
577
+ else if (symbol !== undefined) {
578
+ for (const target of chains.chainOf(symbol)) {
579
+ reach(target.id);
580
+ }
581
+ }
582
+ });
583
+ }
584
+ } while (globalsReached());
510
585
  return { ids: [...reached], guessed: [...guessed] };
511
586
  }
512
587
  /** The type names declarations of the inventory write, each file walked once, when first asked. */
@@ -544,11 +619,101 @@ function declarationNodes(held, byPath) {
544
619
  /** How deep the type arguments and constituents of an inferred type are followed. */
545
620
  const INFERRED_DEPTH = 4;
546
621
  /**
547
- * The declarations of the types the compiler infers where a declaration writes none: the
548
- * type of a variable or a property with no annotation, and the result of a function or a
549
- * method with no result annotation, each with the types its type arguments and its union
550
- * or intersection constituents name, to a fixed depth. A question the checker leaves
551
- * unanswered names nothing.
622
+ * The values one function body returns, each through the type assertions around it: the
623
+ * value a caller receives carries the operand's type whatever type the assertion or the
624
+ * signature names. A nested function's returns are its own.
625
+ */
626
+ function returnedValues(body) {
627
+ const found = [];
628
+ const add = (value) => {
629
+ for (let at = value; at !== undefined;) {
630
+ found.push(at);
631
+ at =
632
+ isAssertionExpression(at) || isSatisfiesExpression(at) || isParenthesizedExpression(at)
633
+ ? at.expression
634
+ : undefined;
635
+ }
636
+ };
637
+ if (!isFunctionLikeDeclaration(body.parent) || body.parent.body !== body) {
638
+ return found;
639
+ }
640
+ if (isArrowFunction(body.parent) && body.kind !== SyntaxKind.Block) {
641
+ add(body);
642
+ return found;
643
+ }
644
+ const visit = (node) => {
645
+ if (isFunctionLikeDeclaration(node)) {
646
+ return;
647
+ }
648
+ if (isReturnStatement(node) && node.expression !== undefined) {
649
+ add(node.expression);
650
+ }
651
+ node.forEachChild(visit);
652
+ };
653
+ body.forEachChild(visit);
654
+ return found;
655
+ }
656
+ /** The object literal one returned value is, written there or as the initializer of a variable `body` declares. */
657
+ function returnedLiteral(value, body) {
658
+ if (isObjectLiteralExpression(value)) {
659
+ return value;
660
+ }
661
+ if (!isIdentifier(value)) {
662
+ return undefined;
663
+ }
664
+ let found;
665
+ const visit = (node) => {
666
+ if (isVariableDeclaration(node) &&
667
+ isIdentifier(node.name) &&
668
+ node.name.text === value.text &&
669
+ node.initializer !== undefined) {
670
+ let at = node.initializer;
671
+ while (isAssertionExpression(at) ||
672
+ isSatisfiesExpression(at) ||
673
+ isParenthesizedExpression(at)) {
674
+ at = at.expression;
675
+ }
676
+ found = isObjectLiteralExpression(at) ? at : found;
677
+ }
678
+ if (!isFunctionLikeDeclaration(node)) {
679
+ node.forEachChild(visit);
680
+ }
681
+ };
682
+ body.forEachChild(visit);
683
+ return found;
684
+ }
685
+ /**
686
+ * The values one function body returns, and those each method of an object literal it
687
+ * returns returns, to a fixed depth: a caller holding the literal calls its methods, and
688
+ * receives what they return.
689
+ */
690
+ function returnedThroughLiterals(body, depth = 0) {
691
+ const values = returnedValues(body);
692
+ if (depth >= INFERRED_DEPTH) {
693
+ return values;
694
+ }
695
+ const found = [...values];
696
+ for (const value of values) {
697
+ for (const property of returnedLiteral(value, body)?.properties ?? []) {
698
+ const fn = isMethodDeclaration(property)
699
+ ? property
700
+ : isPropertyAssignment(property) && isFunctionLikeDeclaration(property.initializer)
701
+ ? property.initializer
702
+ : undefined;
703
+ if (fn?.body !== undefined) {
704
+ found.push(...returnedThroughLiterals(fn.body, depth + 1));
705
+ }
706
+ }
707
+ }
708
+ return found;
709
+ }
710
+ /**
711
+ * The declarations of the types the compiler infers where a declaration writes none, and
712
+ * of the values a function returns: the type of a variable or a property with no
713
+ * annotation, the result of a function or a method with no result annotation, and the type
714
+ * of each value its body returns, each with the types its type arguments and its union or
715
+ * intersection constituents name, to a fixed depth; and as `members` the properties of each
716
+ * returned value's type. A question the checker leaves unanswered names nothing.
552
717
  */
553
718
  function inferredTypeSymbols(project, declarations) {
554
719
  const { queries } = project;
@@ -557,12 +722,24 @@ function inferredTypeSymbols(project, declarations) {
557
722
  !isFunctionLikeDeclaration(node.initializer ?? node)
558
723
  ? [node.name]
559
724
  : []);
725
+ const returned = declarations.flatMap((node) => {
726
+ const signature = isFunctionLikeDeclaration(node)
727
+ ? node
728
+ : isVariableDeclaration(node) &&
729
+ node.initializer !== undefined &&
730
+ isFunctionLikeDeclaration(node.initializer)
731
+ ? node.initializer
732
+ : undefined;
733
+ return signature?.body === undefined ? [] : returnedThroughLiterals(signature.body);
734
+ });
560
735
  const types = [];
561
- queries.typesAt(valued).forEach((type) => {
736
+ const returnedTypes = [];
737
+ queries.typesAt([...valued, ...returned]).forEach((type, at) => {
562
738
  if (type !== UNANSWERED && type !== undefined) {
563
- types.push(type);
739
+ (at < valued.length ? types : returnedTypes).push(type);
564
740
  }
565
741
  });
742
+ types.push(...returnedTypes);
566
743
  for (const node of declarations) {
567
744
  const signature = isFunctionLikeDeclaration(node)
568
745
  ? node
@@ -604,7 +781,19 @@ function inferredTypeSymbols(project, declarations) {
604
781
  types.forEach((type) => {
605
782
  visit(type, 0);
606
783
  });
607
- return [...found.values()];
784
+ // A returned value carries each property of its type, one an anonymous type declares or
785
+ // an intersection merges included, which no type name reaches.
786
+ const members = new Map();
787
+ for (const type of returnedTypes) {
788
+ const parts = queries.constituents(type);
789
+ for (const one of [type, ...(parts === UNANSWERED ? [] : parts)]) {
790
+ const properties = queries.propertiesOf(one);
791
+ for (const property of properties === UNANSWERED ? [] : properties) {
792
+ members.set(property.id, property);
793
+ }
794
+ }
795
+ }
796
+ return { types: [...found.values()], members: [...members.values()] };
608
797
  }
609
798
  /**
610
799
  * The names of types one declaration's own syntax writes: in a type reference, a
@@ -659,13 +848,10 @@ function configured(held, patterns, add) {
659
848
  return unmatched;
660
849
  }
661
850
  /**
662
- * Every configured string that named no declaration in ANY project of the run, in
663
- * configuration order and each once.
664
- *
665
- * A finding about a configured root is about the configuration and not about a
666
- * declaration, so there is one per run: a string that names a declaration of one
667
- * project has named something, and the projects it named nothing in are not a
668
- * finding. A run with no project names nothing with any of them.
851
+ * Every configured string that named no declaration in any project of the run, in
852
+ * configuration order and each once. The finding is about the configuration, so a string
853
+ * naming a declaration of one project has named something, and a run with no project
854
+ * names nothing with any of them.
669
855
  */
670
856
  export function unmatchedEverywhere(patterns, perProject) {
671
857
  return [...new Set(patterns)].filter((pattern) => perProject.every((project) => project.unmatched.includes(pattern)));