@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
package/dist/src/roots.js CHANGED
@@ -1,23 +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";
10
+ import { filesNamedBy, outsideChain, relativeSpecifiers, } from "./configuration-files.js";
12
11
  import { conventionEntries } from "./conventions.js";
13
12
  import { entryPoints } from "./entry-points.js";
14
- import { globExpression } from "./glob.js";
15
13
  import { nodeKey } from "./inventory.js";
16
- import { relativePath, resolvePath } from "./paths.js";
14
+ import { ancestors, dirnamePath, relativePath, resolvePath } from "./paths.js";
17
15
  import { byPosition } from "./position.js";
18
16
  import { UNANSWERED } from "./query.js";
19
17
  import { sourceFilesOf } from "./source-files.js";
20
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";
21
24
  /** Whether one declaration is written below the directory of a published workspace member. */
22
25
  function inPublishedMember(held, id, root, members) {
23
26
  const path = held.symbols.find((symbol) => symbol.id === id)?.position.path;
@@ -43,11 +46,17 @@ const READ_BACK_RULES = new Set([
43
46
  const OUTPUT_EXTENSION = /\.(?:d\.ts|d\.mts|d\.cts|js|mjs|cjs|jsx)$/u;
44
47
  /** The extension a source file's name drops. */
45
48
  const SOURCE_EXTENSION = /\.(?:d\.)?[cm]?[jt]sx?$/u;
49
+ /** Whether no `package.json` sits between one file and the manifest's directory, which holds it. */
50
+ function ofPackage(host, dir, file) {
51
+ const between = ancestors(dirnamePath(file), dir);
52
+ return between.length > 0 && !between.slice(0, -1).some((at) => holdsManifest(host, at));
53
+ }
46
54
  /**
47
55
  * The rooted files one manifest target reads back to by name: its path below the
48
56
  * manifest's directory, past its first directory where it names one, without its output
49
- * extension, is the name, and a rooted file is one whose path below the same directory,
50
- * without its source extension, ends with that name at a directory boundary.
57
+ * extension, is the name, and a rooted file of the manifest's own package is one whose
58
+ * path below the same directory, without its source extension, ends with that name at a
59
+ * directory boundary.
51
60
  */
52
61
  function readBackByName(host, entry, rooted) {
53
62
  const dir = resolvePath(host.workingDirectory(), entry.dir);
@@ -63,12 +72,41 @@ function readBackByName(host, entry, rooted) {
63
72
  const found = new Map();
64
73
  for (const file of rooted) {
65
74
  const path = relativePath(dir, file.fileName)?.replace(SOURCE_EXTENSION, "");
66
- if (path !== undefined && (path === name || path.endsWith(`/${name}`))) {
75
+ if (path !== undefined &&
76
+ (path === name || path.endsWith(`/${name}`)) &&
77
+ ofPackage(host, dir, file.fileName)) {
67
78
  found.set(file.fileName, file);
68
79
  }
69
80
  }
70
81
  return [...found.values()];
71
82
  }
83
+ /**
84
+ * The program files a manifest target no program holds reaches: the target is a file
85
+ * read as a configuration file outside the program is, and each file of the chain it
86
+ * starts loads the program files its relative specifiers name, read through the emit
87
+ * mapping, as a command's shim loads the compiled entry it runs.
88
+ */
89
+ function throughOutside(options, targetRoot, files, entry) {
90
+ const cwd = options.host.workingDirectory();
91
+ const path = resolvePath(cwd, entry.path);
92
+ if (options.parse === undefined ||
93
+ options.runFiles?.has(path) === true ||
94
+ options.host.kindOf(path) !== "file") {
95
+ return [];
96
+ }
97
+ const chain = outsideChain(options.host, targetRoot, files, options.parse, [
98
+ { path, manifestDir: resolvePath(cwd, entry.dir) },
99
+ ]);
100
+ const found = new Map();
101
+ for (const { file } of chain) {
102
+ for (const specifier of relativeSpecifiers(file)) {
103
+ for (const named of filesNamedBy(files, dirnamePath(file.fileName), specifier)) {
104
+ found.set(named.fileName, named);
105
+ }
106
+ }
107
+ }
108
+ return [...found.values()];
109
+ }
72
110
  /** Whether a configured string carries either special character. */
73
111
  function isPattern(text) {
74
112
  return text.includes("*") || text.includes("?");
@@ -121,13 +159,6 @@ export function matchRef(pattern, ref) {
121
159
  }
122
160
  return matchGlob(Array.from(pattern), Array.from(ref));
123
161
  }
124
- /** Two strings ordered bytewise, which is the order every set of a run is read in. */
125
- function compare(a, b) {
126
- if (a === b) {
127
- return 0;
128
- }
129
- return a < b ? -1 : 1;
130
- }
131
162
  /** The members of one declaration, indexed by the container that holds them. */
132
163
  function childrenOf(held) {
133
164
  const byParent = new Map();
@@ -143,15 +174,47 @@ function childrenOf(held) {
143
174
  return byParent;
144
175
  }
145
176
  /**
146
- * The roots one project holds, and every configured string that named nothing in it.
147
- *
148
- * A file a manifest member, an entry-file pattern or a tool's configuration names
149
- * is a root with everything its module exports: each name of the module's export
150
- * table, and where a name is a re-export, every link of the chain to the declaration
151
- * it carries, so a re-exported declaration is a root of the file that publishes it.
152
- * In a library, what a published file exports is rooted by the published API alone.
153
- * A file a runtime executes without reading its exports, a test file or a worker, is
154
- * 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.
155
218
  */
156
219
  export function roots(project, held, targetRoot, options) {
157
220
  const files = sourceFilesOf(project, targetRoot);
@@ -168,8 +231,8 @@ export function roots(project, held, targetRoot, options) {
168
231
  found.set([id, kind, source].join("\u0000"), { id, kind, source });
169
232
  };
170
233
  const entered = [];
171
- const enter = (file, kind, source, exports) => {
172
- entered.push({ file, kind, source, exports });
234
+ const enter = (file, kind, source, exports, names) => {
235
+ entered.push({ file, kind, source, exports, ...(names === undefined ? {} : { names }) });
173
236
  };
174
237
  const points = entryPoints(project, files, options.testFiles, options.host, targetRoot, options.parse, options.conventions?.selected);
175
238
  const published = [];
@@ -186,45 +249,83 @@ export function roots(project, held, targetRoot, options) {
186
249
  (options.publishedAPI || options.publishedMembers?.has(entry.dir) === true),
187
250
  })),
188
251
  ];
189
- 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) : []);
190
253
  const readBackTo = [
191
254
  ...scripted,
192
255
  ...points.filter((point) => READ_BACK_RULES.has(point.rule)).map((point) => point.file),
193
256
  ];
257
+ const entryFilesOf = new Map();
194
258
  const entryFiles = (entry) => {
259
+ const known = entryFilesOf.get(entry);
260
+ if (known !== undefined) {
261
+ return known;
262
+ }
263
+ const found = readEntryFiles(entry);
264
+ entryFilesOf.set(entry, found);
265
+ return found;
266
+ };
267
+ const readEntryFiles = (entry) => {
195
268
  const named = files.named(entry.path);
196
- return named.length > 0 || entry.role === "script"
197
- ? named
198
- : readBackByName(options.host, entry, readBackTo);
269
+ if (named.length > 0 || entry.role === "script") {
270
+ return named;
271
+ }
272
+ const cwd = options.host.workingDirectory();
273
+ const shipped = files.namedAsOutput(resolvePath(cwd, entry.dir), resolvePath(cwd, entry.path));
274
+ if (shipped.length > 0) {
275
+ return shipped;
276
+ }
277
+ const byName = readBackByName(options.host, entry, readBackTo);
278
+ return byName.length > 0 ? byName : throughOutside(options, targetRoot, files, entry);
199
279
  };
200
280
  for (const { entry, publishes } of entries) {
201
281
  if (publishes) {
202
282
  published.push(...entryFiles(entry));
203
283
  }
204
284
  }
205
- const publishedNames = new Set(published.map((file) => file.fileName));
206
- for (const { entry } of entries) {
207
- for (const file of entryFiles(entry)) {
208
- const kind = ENTRY_KINDS[entry.role];
209
- enter(file, kind, entry.member, !publishedNames.has(file.fileName));
210
- }
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));
211
288
  }
289
+ const publishedNames = new Set(published.map((file) => file.fileName));
290
+ const patterned = [];
212
291
  for (const pattern of options.entryFiles) {
213
292
  const expression = globExpression(pattern);
214
293
  for (const [path, file] of files.byPath) {
215
294
  if (expression.test(path)) {
216
- enter(file, "entry-file", pattern, true);
295
+ patterned.push({ file, pattern });
217
296
  }
218
297
  }
219
298
  }
299
+ const unmapped = [];
300
+ const mapped = patterned.map(({ file }) => file);
301
+ for (const { entry } of entries) {
302
+ if (entry.command !== undefined) {
303
+ continue;
304
+ }
305
+ const named = entryFiles(entry);
306
+ if (named.length === 0 && entry.role !== "script") {
307
+ unmapped.push(entry);
308
+ }
309
+ mapped.push(...named);
310
+ for (const file of named) {
311
+ const kind = ENTRY_KINDS[entry.role];
312
+ enter(file, kind, entry.member, !publishedNames.has(file.fileName));
313
+ }
314
+ }
315
+ const unmappedEntries = unmapped.filter((entry) => readBackByName(options.host, entry, mapped).length === 0);
316
+ for (const { file, pattern } of patterned) {
317
+ enter(file, "entry-file", pattern, true);
318
+ }
220
319
  for (const { file, row } of options.conventions === undefined
221
320
  ? []
222
321
  : conventionEntries(options.conventions, files)) {
223
322
  enter(file, "convention", row, true);
224
323
  }
225
324
  for (const point of points) {
226
- enter(point.file, point.rule, point.source, point.exports);
325
+ enter(point.file, point.rule, point.source, point.exports, point.names);
227
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) ?? []);
228
329
  for (const file of files.byPath.values()) {
229
330
  for (const alias of typeQueryAliases(file)) {
230
331
  const source = alias.type.getText().replace(/\s+/gu, " ");
@@ -236,9 +337,10 @@ export function roots(project, held, targetRoot, options) {
236
337
  }
237
338
  }
238
339
  }
239
- const { tables: exportsOf, answered } = exportTables(project, held, [
340
+ const { tables: exportsOf, named, answered, } = exportTables(project, held, [
240
341
  ...entered.filter((entry) => entry.exports).map((entry) => entry.file),
241
342
  ...published,
343
+ ...heldFiles,
242
344
  ]);
243
345
  for (const entry of entered) {
244
346
  const id = fileIds.get(entry.file.fileName);
@@ -247,8 +349,12 @@ export function roots(project, held, targetRoot, options) {
247
349
  }
248
350
  add(id, entry.kind, entry.source);
249
351
  if (entry.exports) {
250
- for (const exported of exportsOf.get(entry.file.fileName) ?? []) {
251
- add(exported, entry.kind, entry.source);
352
+ const byName = named.get(entry.file.fileName);
353
+ const exported = entry.names === undefined || byName === undefined
354
+ ? (exportsOf.get(entry.file.fileName) ?? [])
355
+ : entry.names.flatMap((name) => byName.get(name) ?? []);
356
+ for (const one of exported) {
357
+ add(one, entry.kind, entry.source);
252
358
  }
253
359
  }
254
360
  }
@@ -265,12 +371,22 @@ export function roots(project, held, targetRoot, options) {
265
371
  const left = sites.get(a.id);
266
372
  const right = sites.get(b.id);
267
373
  const site = left === undefined || right === undefined ? 0 : byPosition(left, right);
268
- 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);
269
375
  });
270
376
  return {
271
377
  liveUnderReachability: ordered,
272
378
  unmatched,
273
- 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
+ ],
388
+ unmappedEntries,
389
+ heldScripts,
274
390
  };
275
391
  }
276
392
  /**
@@ -284,9 +400,10 @@ export function roots(project, held, targetRoot, options) {
284
400
  function exportTables(project, held, wanted) {
285
401
  const unique = [...new Map(wanted.map((file) => [file.fileName, file])).values()];
286
402
  const tables = new Map();
403
+ const named = new Map();
287
404
  let answered = true;
288
405
  if (unique.length === 0) {
289
- return { tables, answered };
406
+ return { tables, named, answered };
290
407
  }
291
408
  const modules = project.symbolsAt(unique.map((file) => project.handle(file)));
292
409
  const chains = aliasChains(project, held);
@@ -307,15 +424,18 @@ function exportTables(project, held, wanted) {
307
424
  return;
308
425
  }
309
426
  const ids = new Set();
427
+ const byName = new Map();
310
428
  for (const exported of table) {
311
429
  for (const target of chains.chainOf(exported)) {
312
430
  answered &&= !target.guessed;
313
431
  ids.add(target.id);
432
+ byName.set(exported.name, [...(byName.get(exported.name) ?? []), target.id]);
314
433
  }
315
434
  }
316
435
  tables.set(file.fileName, [...ids]);
436
+ named.set(file.fileName, byName);
317
437
  });
318
- return { tables, answered };
438
+ return { tables, named, answered };
319
439
  }
320
440
  /** A release tag of a documentation comment: `@` and its name, between white space or the ends. */
321
441
  const RELEASE_TAG = /(?:^|\s)@(?:public|beta|alpha)(?=\s|$)/u;
@@ -357,12 +477,12 @@ function releaseTagged(held, files) {
357
477
  /** The kinds of container whose export table, not its members' visibility, says what it publishes. */
358
478
  const EXPORTING_CONTAINERS = new Set(["file", "namespace"]);
359
479
  /**
360
- * Every declaration a consumer of the library can name: what the published files export,
361
- * and below each the members a consumer writes, a namespace's exported ones and any other
362
- * declaration's public and protected ones. A declaration a published one's type names is
363
- * published too, and so is an exported declaration a release tag marks. A type name the
364
- * checker leaves unanswered publishes every declaration spelled as it is, as `guessed`. A
365
- * 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.
366
486
  */
367
487
  function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
368
488
  const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
@@ -370,11 +490,13 @@ function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
370
490
  const nodes = declarationNodes(held, byPath);
371
491
  const chains = aliasChains(project, held);
372
492
  const reached = new Set();
493
+ const entered = new Set();
373
494
  const guessed = new Set();
374
495
  const pending = [];
375
496
  const reach = (id) => {
376
- if (!reached.has(id) && byId.get(id)?.kind !== "file") {
377
- reached.add(id);
497
+ const file = byId.get(id)?.kind === "file";
498
+ if (!(file ? entered : reached).has(id)) {
499
+ (file ? entered : reached).add(id);
378
500
  pending.push(id);
379
501
  }
380
502
  };
@@ -383,55 +505,83 @@ function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
383
505
  }
384
506
  for (const file of published) {
385
507
  for (const id of exportsOf.get(file.fileName) ?? []) {
386
- if (byId.get(id)?.kind === "file") {
387
- pending.push(id);
388
- }
389
- else {
390
- reach(id);
391
- }
508
+ reach(id);
392
509
  }
393
510
  }
394
- while (pending.length > 0) {
395
- const round = [];
396
- for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
397
- round.push(next);
398
- const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
399
- for (const member of children.get(next) ?? []) {
400
- const reachable = exporting
401
- ? member.exported
402
- : member.visibility !== "private" && member.visibility !== "private-name";
403
- 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") {
404
528
  reach(member.id);
405
529
  }
406
530
  }
407
531
  }
408
- const named = round.flatMap((id) => {
409
- const symbol = byId.get(id);
410
- return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
411
- });
412
- for (const symbol of inferredTypeSymbols(project, round.flatMap((id) => {
413
- const symbol = byId.get(id);
414
- const node = symbol === undefined ? undefined : nodes.nodeOf(symbol);
415
- return node === undefined ? [] : [node];
416
- }))) {
417
- for (const target of chains.chainOf(symbol)) {
418
- reach(target.id);
419
- }
420
- }
421
- project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
422
- if (symbol === UNANSWERED) {
423
- for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
424
- guessed.add(id);
425
- 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
+ }
426
547
  }
427
548
  }
428
- 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) {
429
558
  for (const target of chains.chainOf(symbol)) {
430
559
  reach(target.id);
431
560
  }
432
561
  }
433
- });
434
- }
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());
435
585
  return { ids: [...reached], guessed: [...guessed] };
436
586
  }
437
587
  /** The type names declarations of the inventory write, each file walked once, when first asked. */
@@ -449,9 +599,7 @@ function declarationNodes(held, byPath) {
449
599
  const id = held.declarations.get(nodeKey(file, node));
450
600
  if (id !== undefined) {
451
601
  found.nodes.add(node);
452
- if (!found.byId.has(id)) {
453
- found.byId.set(id, node);
454
- }
602
+ found.byId.set(id, [...(found.byId.get(id) ?? []), node]);
455
603
  }
456
604
  node.forEachChild(visit);
457
605
  };
@@ -463,20 +611,109 @@ function declarationNodes(held, byPath) {
463
611
  return {
464
612
  typeNamesOf: (symbol) => {
465
613
  const { byId, nodes } = nodesIn(symbol.position.path);
466
- const declaration = byId.get(symbol.id);
467
- return declaration === undefined ? [] : typeNamesOf(declaration, nodes);
614
+ return (byId.get(symbol.id) ?? []).flatMap((declaration) => typeNamesOf(declaration, nodes));
468
615
  },
469
- nodeOf: (symbol) => nodesIn(symbol.position.path).byId.get(symbol.id),
616
+ nodesOf: (symbol) => nodesIn(symbol.position.path).byId.get(symbol.id) ?? [],
470
617
  };
471
618
  }
472
619
  /** How deep the type arguments and constituents of an inferred type are followed. */
473
620
  const INFERRED_DEPTH = 4;
474
621
  /**
475
- * The declarations of the types the compiler infers where a declaration writes none: the
476
- * type of a variable or a property with no annotation, and the result of a function or a
477
- * method with no result annotation, each with the types its type arguments and its union
478
- * or intersection constituents name, to a fixed depth. A question the checker leaves
479
- * 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.
480
717
  */
481
718
  function inferredTypeSymbols(project, declarations) {
482
719
  const { queries } = project;
@@ -485,12 +722,24 @@ function inferredTypeSymbols(project, declarations) {
485
722
  !isFunctionLikeDeclaration(node.initializer ?? node)
486
723
  ? [node.name]
487
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
+ });
488
735
  const types = [];
489
- queries.typesAt(valued).forEach((type) => {
736
+ const returnedTypes = [];
737
+ queries.typesAt([...valued, ...returned]).forEach((type, at) => {
490
738
  if (type !== UNANSWERED && type !== undefined) {
491
- types.push(type);
739
+ (at < valued.length ? types : returnedTypes).push(type);
492
740
  }
493
741
  });
742
+ types.push(...returnedTypes);
494
743
  for (const node of declarations) {
495
744
  const signature = isFunctionLikeDeclaration(node)
496
745
  ? node
@@ -532,7 +781,19 @@ function inferredTypeSymbols(project, declarations) {
532
781
  types.forEach((type) => {
533
782
  visit(type, 0);
534
783
  });
535
- 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()] };
536
797
  }
537
798
  /**
538
799
  * The names of types one declaration's own syntax writes: in a type reference, a
@@ -587,13 +848,10 @@ function configured(held, patterns, add) {
587
848
  return unmatched;
588
849
  }
589
850
  /**
590
- * Every configured string that named no declaration in ANY project of the run, in
591
- * configuration order and each once.
592
- *
593
- * A finding about a configured root is about the configuration and not about a
594
- * declaration, so there is one per run: a string that names a declaration of one
595
- * project has named something, and the projects it named nothing in are not a
596
- * 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.
597
855
  */
598
856
  export function unmatchedEverywhere(patterns, perProject) {
599
857
  return [...new Set(patterns)].filter((pattern) => perProject.every((project) => project.unmatched.includes(pattern)));