@cplieger/deadset-ts 8.0.0-dev.15 → 8.0.0-dev.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/src/alias-chain.js +0 -5
  2. package/dist/src/amd.js +81 -0
  3. package/dist/src/analysis.js +94 -54
  4. package/dist/src/augmentations.js +23 -24
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +25 -8
  7. package/dist/src/commands/read.js +6 -7
  8. package/dist/src/commands/tools.js +5 -9
  9. package/dist/src/component-files.js +3 -2
  10. package/dist/src/conformance-record.js +1 -1
  11. package/dist/src/consumers.js +9 -10
  12. package/dist/src/contextual-uses.js +1 -17
  13. package/dist/src/contract/config.generated.js +5 -2
  14. package/dist/src/contract/contract.generated.js +119 -2
  15. package/dist/src/contract/report.generated.js +6 -2
  16. package/dist/src/dependencies.js +98 -71
  17. package/dist/src/destructuring.js +1 -19
  18. package/dist/src/element-access.js +179 -164
  19. package/dist/src/entries/build-scripts.js +222 -0
  20. package/dist/src/{configuration-files.js → entries/configuration-files.js} +22 -64
  21. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  22. package/dist/src/entries/conventions.js +739 -0
  23. package/dist/src/{html-entries.js → entries/html-entries.js} +9 -12
  24. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +3 -3
  25. package/dist/src/entries/run-directories.js +77 -0
  26. package/dist/src/entries/sources.js +144 -0
  27. package/dist/src/entries/static-module.js +670 -0
  28. package/dist/src/entries/test-runners.js +684 -0
  29. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  30. package/dist/src/extensions.js +37 -0
  31. package/dist/src/facts.js +38 -0
  32. package/dist/src/findings/completion.js +40 -4
  33. package/dist/src/findings/emitters.js +5 -8
  34. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  35. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  36. package/dist/src/findings/self-check.js +36 -20
  37. package/dist/src/findings-pass.js +5 -6
  38. package/dist/src/framework-lifecycle.js +42 -15
  39. package/dist/src/fs/manifest.js +15 -20
  40. package/dist/src/generated-file.js +172 -29
  41. package/dist/src/graph.js +14 -8
  42. package/dist/src/import-bindings.js +73 -28
  43. package/dist/src/index/sites.js +557 -0
  44. package/dist/src/index.js +1 -1
  45. package/dist/src/indexed-access.js +523 -478
  46. package/dist/src/injection-container.js +1 -1
  47. package/dist/src/interface-satisfaction.js +1 -1
  48. package/dist/src/inventory/commonjs.js +62 -17
  49. package/dist/src/inventory/enumerate.js +2 -1
  50. package/dist/src/javascript-file.js +26 -11
  51. package/dist/src/keyof-comparisons.js +124 -103
  52. package/dist/src/load/classify.js +28 -9
  53. package/dist/src/load/diagnostics.js +8 -1
  54. package/dist/src/load/discover.js +15 -7
  55. package/dist/src/load/expected-module.js +2 -2
  56. package/dist/src/load/plan.js +2 -2
  57. package/dist/src/load/specifiers.js +2 -4
  58. package/dist/src/load/synthesize.js +10 -5
  59. package/dist/src/load/workspace-manifests.js +1 -1
  60. package/dist/src/load/workspace-resolution.js +2 -2
  61. package/dist/src/matrix.js +12 -17
  62. package/dist/src/module-declarations.js +70 -85
  63. package/dist/src/overrides.js +134 -112
  64. package/dist/src/paths.js +56 -1
  65. package/dist/src/platform-calls.js +78 -0
  66. package/dist/src/platform-symbols.js +134 -0
  67. package/dist/src/references/read-rule.js +1 -0
  68. package/dist/src/references/read-rules.js +48 -0
  69. package/dist/src/references/sink.js +151 -0
  70. package/dist/src/references/sources.js +450 -0
  71. package/dist/src/references.js +111 -1116
  72. package/dist/src/resolve.js +1 -1
  73. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +7 -4
  74. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  75. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  76. package/dist/src/roots/published-reach.js +412 -0
  77. package/dist/src/roots/root-set.js +489 -0
  78. package/dist/src/roots/unseen-loads.js +236 -0
  79. package/dist/src/serialization-contract.js +9 -4
  80. package/dist/src/source-files.js +5 -9
  81. package/dist/src/stores.js +4 -2
  82. package/dist/src/suppress.js +30 -32
  83. package/dist/src/sweep.js +25 -13
  84. package/dist/src/symbol-keys.js +89 -83
  85. package/dist/src/tests/file-class.js +76 -0
  86. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  87. package/dist/src/{test-support.js → tests/support.js} +5 -12
  88. package/dist/src/type-parameter-reads.js +178 -139
  89. package/dist/src/unimplemented-callees.js +262 -242
  90. package/dist/src/verbs/analyze.js +15 -3
  91. package/dist/src/verbs/describe.js +5 -3
  92. package/dist/src/verbs/explain.js +50 -28
  93. package/dist/src/verbs/print-retained.js +2 -2
  94. package/jsr.json +1 -1
  95. package/package.json +3 -3
  96. package/src/alias-chain.ts +0 -7
  97. package/src/amd.ts +130 -0
  98. package/src/analysis.ts +156 -70
  99. package/src/augmentations.ts +41 -27
  100. package/src/automatic-types.ts +1 -1
  101. package/src/bindings.ts +460 -0
  102. package/src/calls.ts +25 -7
  103. package/src/commands/read.ts +6 -8
  104. package/src/commands/tools.ts +5 -11
  105. package/src/component-files.ts +4 -2
  106. package/src/conformance-record.ts +1 -1
  107. package/src/consumers.ts +12 -11
  108. package/src/contextual-uses.ts +2 -22
  109. package/src/contract/config.generated.ts +5 -2
  110. package/src/contract/contract.generated.ts +138 -2
  111. package/src/contract/report.generated.ts +6 -2
  112. package/src/dependencies.ts +117 -71
  113. package/src/destructuring.ts +1 -31
  114. package/src/element-access.ts +188 -182
  115. package/src/entries/build-scripts.ts +292 -0
  116. package/src/{configuration-files.ts → entries/configuration-files.ts} +33 -82
  117. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  118. package/src/{conventions.ts → entries/conventions.ts} +349 -523
  119. package/src/{html-entries.ts → entries/html-entries.ts} +19 -18
  120. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +5 -5
  121. package/src/entries/run-directories.ts +108 -0
  122. package/src/entries/sources.ts +266 -0
  123. package/src/entries/static-module.ts +858 -0
  124. package/src/entries/test-runners.ts +868 -0
  125. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  126. package/src/exempt.ts +3 -2
  127. package/src/extensions.ts +45 -0
  128. package/src/facts.ts +46 -0
  129. package/src/file-facts.ts +1 -1
  130. package/src/findings/boundary.ts +2 -0
  131. package/src/findings/completion.ts +43 -4
  132. package/src/findings/emitter.ts +1 -1
  133. package/src/findings/emitters.ts +14 -10
  134. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  135. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  136. package/src/findings/self-check.ts +41 -22
  137. package/src/findings/visibility-narrowing.ts +1 -1
  138. package/src/findings-pass.ts +5 -12
  139. package/src/framework-lifecycle.ts +43 -15
  140. package/src/fs/manifest.ts +25 -21
  141. package/src/generated-file.ts +240 -36
  142. package/src/graph.ts +27 -11
  143. package/src/import-bindings.ts +98 -29
  144. package/src/index/sites.ts +781 -0
  145. package/src/index.ts +1 -1
  146. package/src/indexed-access.ts +570 -527
  147. package/src/injection-container.ts +1 -1
  148. package/src/interface-satisfaction.ts +1 -1
  149. package/src/inventory/commonjs.ts +102 -17
  150. package/src/inventory/enumerate.ts +2 -1
  151. package/src/javascript-file.ts +29 -12
  152. package/src/keyof-comparisons.ts +131 -116
  153. package/src/load/classify.ts +49 -13
  154. package/src/load/diagnostics.ts +11 -1
  155. package/src/load/discover.ts +27 -11
  156. package/src/load/expected-module.ts +8 -2
  157. package/src/load/plan.ts +11 -2
  158. package/src/load/specifiers.ts +8 -5
  159. package/src/load/synthesize.ts +12 -6
  160. package/src/load/test-globals.ts +1 -1
  161. package/src/load/workspace-manifests.ts +1 -1
  162. package/src/load/workspace-resolution.ts +9 -2
  163. package/src/load/workspace-run.ts +1 -1
  164. package/src/matrix.ts +13 -17
  165. package/src/module-declarations.ts +74 -98
  166. package/src/overrides.ts +162 -134
  167. package/src/paths.ts +65 -1
  168. package/src/platform-calls.ts +119 -0
  169. package/src/platform-symbols.ts +189 -0
  170. package/src/references/read-rule.ts +60 -0
  171. package/src/references/read-rules.ts +55 -0
  172. package/src/references/sink.ts +213 -0
  173. package/src/references/sources.ts +632 -0
  174. package/src/references.ts +174 -1492
  175. package/src/report.ts +2 -2
  176. package/src/resolve.ts +1 -1
  177. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +10 -7
  178. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  179. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  180. package/src/roots/published-reach.ts +481 -0
  181. package/src/{roots.ts → roots/root-set.ts} +203 -630
  182. package/src/roots/unseen-loads.ts +345 -0
  183. package/src/serialization-contract.ts +9 -4
  184. package/src/source-files.ts +5 -10
  185. package/src/stores.ts +13 -2
  186. package/src/suppress.ts +48 -33
  187. package/src/sweep.ts +37 -14
  188. package/src/symbol-keys.ts +102 -102
  189. package/src/tests/file-class.ts +118 -0
  190. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  191. package/src/{test-support.ts → tests/support.ts} +15 -21
  192. package/src/type-parameter-reads.ts +187 -158
  193. package/src/unimplemented-callees.ts +297 -280
  194. package/src/verbs/analyze.ts +19 -5
  195. package/src/verbs/describe.ts +5 -2
  196. package/src/verbs/explain.ts +75 -32
  197. package/src/verbs/print-retained.ts +2 -2
  198. package/dist/src/build-scripts.js +0 -418
  199. package/dist/src/conventions.js +0 -907
  200. package/dist/src/entry-points.js +0 -444
  201. package/dist/src/own-body.js +0 -28
  202. package/dist/src/roots.js +0 -865
  203. package/dist/src/unseen-loads.js +0 -189
  204. package/src/build-scripts.ts +0 -536
  205. package/src/entry-points.ts +0 -584
  206. package/src/own-body.ts +0 -42
  207. package/src/unseen-loads.ts +0 -282
@@ -1,5 +1,6 @@
1
1
  import { CONTRACT_VERSION, EXIT_CLEAN, type Format } from "../contract/contract.generated.ts";
2
2
  import {
3
+ productionMode,
3
4
  runAnalysis,
4
5
  runBaselineWrite,
5
6
  unansweredCounts,
@@ -9,10 +10,11 @@ import {
9
10
  import { kindOf } from "../config.ts";
10
11
  import { CONFORMANCE, DECLARED_GAPS } from "../conformance.ts";
11
12
  import { analyzerProvenance, verdictOf } from "../findings-pass.ts";
13
+ import { factsDocument } from "../facts.ts";
12
14
  import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "../paths.ts";
13
15
  import type { Host } from "../host.ts";
14
- import type { ManifestEntry } from "../manifest.ts";
15
- import type { HeldScripts } from "../roots.ts";
16
+ import type { ManifestEntry } from "../roots/manifest.ts";
17
+ import type { HeldScripts } from "../roots/root-set.ts";
16
18
  import { moduleIdentity, type NotBuilt } from "../load/discover.ts";
17
19
  import { partialNotes } from "../partial.ts";
18
20
  import {
@@ -29,7 +31,7 @@ import { RENDERINGS } from "../reporters/reporters.ts";
29
31
  import { parseTemplate, TemplateError, type Template } from "../reporters/template.ts";
30
32
  import { resolve } from "../resolve.ts";
31
33
  import { otherSchemeRows, rowReason, writeBaseline, type Recorded } from "../suppress-file.ts";
32
- import type { UnseenLoad } from "../unseen-loads.ts";
34
+ import type { UnseenLoad } from "../roots/unseen-loads.ts";
33
35
  import type { UnreadInput } from "../load/plan.ts";
34
36
  import type { UnresolvedSpecifier } from "../load/specifiers.ts";
35
37
 
@@ -42,7 +44,7 @@ import { ownVersion } from "./version.ts";
42
44
 
43
45
  /** The options `analyze` takes beside the ones every verb takes. */
44
46
  export const ANALYZE_OPTIONS: VerbOptions = {
45
- plain: ["report", "baseline-write", "exit-code", "template"],
47
+ plain: ["report", "facts", "baseline-write", "exit-code", "template"],
46
48
  repeatable: ["format"],
47
49
  };
48
50
 
@@ -258,6 +260,7 @@ function templateOf(
258
260
  /** Every document one invocation asked for: the report, each rendering beside it, a baseline. */
259
261
  interface Documents {
260
262
  readonly report: string;
263
+ readonly facts: string | undefined;
261
264
  readonly formats: readonly Format[];
262
265
  readonly baseline: string | undefined;
263
266
  }
@@ -286,8 +289,13 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
286
289
  const { config, provenance } = resolve(inputs());
287
290
  const invoked = host.workingDirectory();
288
291
  const baseline = option("baseline-write");
292
+ const facts = option("facts");
293
+ if (facts === "") {
294
+ throw new UsageError("--facts names the path the facts document is written to, and named none");
295
+ }
289
296
  const documents: Documents = {
290
297
  report: resolvePath(invoked, reportPath),
298
+ facts: facts === undefined ? undefined : resolvePath(invoked, facts),
291
299
  formats: formatsOf(repeated("format"), config.reporters.formats),
292
300
  baseline: baseline === undefined ? undefined : resolvePath(invoked, baseline),
293
301
  };
@@ -299,7 +307,7 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
299
307
 
300
308
  let recorded: readonly Recorded[];
301
309
  const root = runRelative(invoked, targetRoot);
302
- const mode = { production: true };
310
+ const mode = productionMode(config);
303
311
  let analysis: RunAnalysis;
304
312
  if (documents.baseline === undefined) {
305
313
  analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
@@ -378,6 +386,12 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
378
386
  );
379
387
  }
380
388
  writeDocument(host, documents.report, "name a path the run can write", () => json(report));
389
+ const factsPath = documents.facts;
390
+ if (factsPath !== undefined) {
391
+ writeDocument(host, factsPath, "name a path the run can write", () => [
392
+ factsDocument(version, run.generated, run.bindings),
393
+ ]);
394
+ }
381
395
  const options = {
382
396
  readSource: (path: string) => host.readFile(joinPath(targetRoot, path)),
383
397
  template,
@@ -2,6 +2,7 @@ import {
2
2
  CONTRACT_VERSION,
3
3
  EXIT_CLEAN,
4
4
  EXIT_USAGE,
5
+ FACTS_VERSION,
5
6
  SCHEMA_VERSIONS,
6
7
  } from "../contract/contract.generated.ts";
7
8
  import { CONFORMANCE, type Conformance } from "../conformance.ts";
@@ -11,8 +12,9 @@ import { ownVersion } from "./version.ts";
11
12
 
12
13
  /**
13
14
  * The document `describe` writes: the analyzer, the Contract version it implements, the
14
- * report schema versions it reads, the languages it claims, and its recorded result over
15
- * the conformance corpus, a member the document omits where no run is recorded.
15
+ * report schema versions it reads, the languages it claims, the facts version it writes,
16
+ * and its recorded result over the conformance corpus, a member the document omits where
17
+ * no run is recorded.
16
18
  */
17
19
  export function describeDocument(version: string, recorded: Conformance | undefined): string {
18
20
  const document = {
@@ -21,6 +23,7 @@ export function describeDocument(version: string, recorded: Conformance | undefi
21
23
  contract_version: CONTRACT_VERSION,
22
24
  schema_versions_accepted: SCHEMA_VERSIONS,
23
25
  languages: [LANGUAGE],
26
+ facts_version_written: FACTS_VERSION,
24
27
  ...(recorded === undefined
25
28
  ? {}
26
29
  : {
@@ -1,5 +1,5 @@
1
1
  import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.ts";
2
- import { runAnalysis, type RunAnalysis } from "../analysis.ts";
2
+ import { productionMode, runAnalysis, type RunAnalysis } from "../analysis.ts";
3
3
  import { listing } from "../components.ts";
4
4
  import type { Cascade } from "../config.ts";
5
5
  import type { CompletedFinding } from "../finding.ts";
@@ -10,8 +10,10 @@ import { partialNotes } from "../partial.ts";
10
10
  import { resolvePath } from "../paths.ts";
11
11
  import { positionKey } from "../position.ts";
12
12
  import type { Reference } from "../references.ts";
13
+ import type { Mode } from "../sweep.ts";
14
+ import { madeByTestCode, withheldBy } from "../tests/file-class.ts";
13
15
  import { resolve } from "../resolve.ts";
14
- import type { Root } from "../roots.ts";
16
+ import type { Root } from "../roots/root-set.ts";
15
17
  import { type Verb, type VerbOptions } from "./verb.ts";
16
18
  import { UsageError } from "../usage.ts";
17
19
  import { bytewise } from "../text/compare.ts";
@@ -141,15 +143,20 @@ type Hop =
141
143
  | { readonly k: "reference"; readonly reference: Reference }
142
144
  | { readonly k: "export"; readonly reference: Reference; readonly to: string };
143
145
 
144
- /** Where a path starts: a root of the run, or a reference a loaded consumer makes. */
146
+ /**
147
+ * Where a path starts: a root of the run, or a reference a loaded consumer or a test file no
148
+ * project holds makes.
149
+ */
145
150
  type Origin =
146
151
  | { readonly k: "root"; readonly root: Root }
147
- | { readonly k: "consumer"; readonly consumer: string };
152
+ | { readonly k: "consumer"; readonly consumer: string }
153
+ | { readonly k: "unheld"; readonly file: string };
148
154
 
149
155
  /**
150
- * The shortest path from a root or a loaded consumer to one declaration, walked breadth first
151
- * from every root at once in the order the run names them, a consumer's reference being the
152
- * first hop of a path of its own, and following the references the sweep follows: a
156
+ * The shortest path from a root, a loaded consumer or a test file no project holds to one
157
+ * declaration, walked breadth first from every root at once in the order the run names them,
158
+ * a reference of a consumer or of such a file being the first hop of a path of its own, as
159
+ * the sweep seeds them, and following the references the sweep follows: a
153
160
  * reference that reads a module reaches what the module exports, and an evaluation or a root
154
161
  * reaches the module alone. Under `production` no reference a test file made is followed,
155
162
  * which is the set the report's sweep counts.
@@ -157,18 +164,26 @@ type Origin =
157
164
  function shortestPath(
158
165
  analysis: RunAnalysis,
159
166
  to: string,
160
- production: boolean,
167
+ mode: Mode,
168
+ support: ReadonlySet<string>,
161
169
  ): { readonly origin: Origin; readonly path: readonly Hop[] } | undefined {
162
170
  const graph = analysis.swept.matrix.union;
163
171
  const out = new Map<string, Reference[]>();
164
- const consumed: (Reference & { readonly consumer: string })[] = [];
172
+ const consumed: { readonly reference: Reference; readonly origin: Origin }[] = [];
165
173
  for (const reference of referencesOf(analysis.configured)) {
166
- if (production && reference.test) {
174
+ if (withheldBy(mode, reference, support)) {
167
175
  continue;
168
176
  }
169
177
  const { consumer } = reference;
170
178
  if (consumer !== undefined) {
171
- consumed.push({ ...reference, consumer });
179
+ consumed.push({ reference, origin: { k: "consumer", consumer } });
180
+ continue;
181
+ }
182
+ if (reference.unheld === true) {
183
+ consumed.push({
184
+ reference,
185
+ origin: { k: "unheld", file: reference.from.replace(/#$/u, "") },
186
+ });
172
187
  continue;
173
188
  }
174
189
  const held = out.get(reference.from);
@@ -210,13 +225,12 @@ function shortestPath(
210
225
  for (const root of rootsOf(analysis.configured)) {
211
226
  enter({ at: root.id, hop: undefined, previous: -1, origin: { k: "root", root } });
212
227
  }
213
- // A consumer's reference is one hop long, so it joins the walk where the roots' first
214
- // hops do.
228
+ // A consumed reference is one hop long, so it joins the walk where the roots' first hops do.
215
229
  const firstHops = steps.length;
216
230
  for (let head = 0; ; head += 1) {
217
231
  if (head === firstHops) {
218
- for (const reference of consumed) {
219
- follow(reference, -1, { k: "consumer", consumer: reference.consumer });
232
+ for (const { reference, origin } of consumed) {
233
+ follow(reference, -1, origin);
220
234
  }
221
235
  }
222
236
  const step = steps[head];
@@ -237,17 +251,35 @@ function shortestPath(
237
251
  return undefined;
238
252
  }
239
253
 
254
+ /** Where a path starts, as a line names it. */
255
+ function originText(origin: Origin): string {
256
+ switch (origin.k) {
257
+ case "root":
258
+ return `root ${origin.root.kind} ${origin.root.id}`;
259
+ case "consumer":
260
+ return `consumer ${origin.consumer}`;
261
+ case "unheld":
262
+ return `test file no project holds ${origin.file}`;
263
+ }
264
+ }
265
+
240
266
  /** What made a reference: a declaration of the target, or a loaded consumer. */
241
267
  function madeBy(reference: Reference): string {
242
268
  return reference.consumer === undefined ? reference.from : `consumer ${reference.consumer}`;
243
269
  }
244
270
 
245
- function referenceLine(reference: Reference, tail: string): string {
246
- return ` reference: ${positionKey(reference.position)}\t${reference.use}\t${tail}${reference.test ? "\tfrom a test file" : ""}\n`;
271
+ /** One reference of an answer, marked where test code made it, a test-support file's included. */
272
+ function referenceLine(reference: Reference, tail: string, support: ReadonlySet<string>): string {
273
+ return ` reference: ${positionKey(reference.position)}\t${reference.use}\t${tail}${madeByTestCode(reference, support) ? "\tfrom a test file" : ""}\n`;
247
274
  }
248
275
 
249
276
  /** Why a declaration no finding names and no exemption held back is live, or how it fell. */
250
- function liveness(analysis: RunAnalysis, subject: InventorySymbol, cascade: Cascade): string {
277
+ function liveness(
278
+ analysis: RunAnalysis,
279
+ subject: InventorySymbol,
280
+ cascade: Cascade,
281
+ mode: Mode,
282
+ ): string {
251
283
  const { sweep } = analysis.swept;
252
284
  const candidate = sweep.candidates.find((one) => one.id === subject.id);
253
285
  if (candidate === undefined && !judged(subject.kind)) {
@@ -258,23 +290,30 @@ function liveness(analysis: RunAnalysis, subject: InventorySymbol, cascade: Casc
258
290
  for (const root of rootsOf(analysis.configured).filter((one) => one.id === subject.id)) {
259
291
  text += ` root: ${root.kind}${root.source === "" ? "" : `\tnamed by ${root.source}`}\n`;
260
292
  }
293
+ const support = new Set(analysis.configured.flatMap((one) => one.supportFiles ?? []));
261
294
  const found =
262
- shortestPath(analysis, subject.id, true) ?? shortestPath(analysis, subject.id, false);
295
+ shortestPath(analysis, subject.id, mode, support) ??
296
+ shortestPath(analysis, subject.id, { production: false }, support);
263
297
  if (found !== undefined && found.path.length > 0) {
264
298
  const { origin } = found;
265
- text += ` reached from: ${origin.k === "root" ? `root ${origin.root.kind} ${origin.root.id}` : `consumer ${origin.consumer}`}\n`;
299
+ text += ` reached from: ${originText(origin)}\n`;
266
300
  for (const hop of found.path) {
267
- text += referenceLine(hop.reference, `${madeBy(hop.reference)} -> ${hop.reference.to}`);
301
+ text += referenceLine(
302
+ hop.reference,
303
+ `${madeBy(hop.reference)} -> ${hop.reference.to}`,
304
+ support,
305
+ );
268
306
  if (hop.k === "export") {
269
307
  text += ` export: ${hop.reference.to} -> ${hop.to}\n`;
270
308
  }
271
309
  }
272
310
  } else if (found === undefined) {
273
311
  const references = referencesOf(analysis.configured).filter((one) => one.to === subject.id);
274
- text += " unreachable: no path of references reaches it from a root or a loaded consumer\n";
312
+ text +=
313
+ " unreachable: no path of references reaches it from a root, a loaded consumer or a test file no project holds\n";
275
314
  text += ` references: ${String(references.length)}\n`;
276
315
  for (const reference of references) {
277
- text += referenceLine(reference, `from ${madeBy(reference)}`);
316
+ text += referenceLine(reference, `from ${madeBy(reference)}`, support);
278
317
  }
279
318
  }
280
319
  if (candidate !== undefined) {
@@ -332,7 +371,12 @@ function namedElsewhere(findings: readonly CompletedFinding[], subject: Inventor
332
371
  }
333
372
 
334
373
  /** The one explanation that applies to a declaration, built whole before it is written. */
335
- function explanation(analysis: RunAnalysis, subject: InventorySymbol, cascade: Cascade): string {
374
+ function explanation(
375
+ analysis: RunAnalysis,
376
+ subject: InventorySymbol,
377
+ cascade: Cascade,
378
+ mode: Mode,
379
+ ): string {
336
380
  const graph = analysis.swept.matrix.union;
337
381
  const at = graph.at(subject.id);
338
382
  let text = `symbol: ${subject.ref}\n`;
@@ -352,7 +396,7 @@ function explanation(analysis: RunAnalysis, subject: InventorySymbol, cascade: C
352
396
  text +=
353
397
  "answer: held\n held by: a question the checker did not answer, which could have kept it live, so no finding names it\n";
354
398
  } else {
355
- text += liveness(analysis, subject, cascade);
399
+ text += liveness(analysis, subject, cascade, mode);
356
400
  }
357
401
  return text + namedElsewhere(analysis.result.findings, subject);
358
402
  }
@@ -361,17 +405,16 @@ function explanation(analysis: RunAnalysis, subject: InventorySymbol, cascade: C
361
405
  * Explains one symbol from the analysis the report is built from: why a reported symbol is
362
406
  * reported, which exemption classes held a retained one back, that an unanswered question held
363
407
  * a held one, and for any other declaration the relations that hold it live and the shortest
364
- * path from a root or a loaded consumer, or the references it has where no path reaches it. A
365
- * request naming no one declaration exits with the usage code and names what partially
366
- * matches it. Where the analysis was partial, the error stream says so.
408
+ * path from a root, a loaded consumer or a test file no project holds, or the references it
409
+ * has where no path reaches it. A request naming no one declaration exits with the usage
410
+ * code and names what partially matches it. Where the analysis was partial, the error stream says so.
367
411
  */
368
412
  export const explainVerb: Verb = ({ out, err, host, inputs, scope, openClient, option }) => {
369
413
  const named = namedSymbol(option);
370
414
  const { config, provenance } = resolve(inputs());
371
415
  const scoped = scope();
372
- const analysis = runAnalysis(openClient(config), host, scoped, config, provenance, {
373
- production: true,
374
- });
416
+ const mode = productionMode(config);
417
+ const analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
375
418
  const { subject, candidates } = subjectOf(analysis.swept.matrix.union.symbols, named);
376
419
  const notes = partialNotes({
377
420
  notBuilt: analysis.run.notBuilt,
@@ -387,7 +430,7 @@ export const explainVerb: Verb = ({ out, err, host, inputs, scope, openClient, o
387
430
  err.write(" no symbol of the target partially matches it either\n");
388
431
  }
389
432
  } else {
390
- out.write(explanation(analysis, subject, config.reporters.cascade));
433
+ out.write(explanation(analysis, subject, config.reporters.cascade, mode));
391
434
  }
392
435
  for (const note of notes) {
393
436
  diagnose(err, note);
@@ -1,5 +1,5 @@
1
1
  import { EXIT_CLEAN } from "../contract/contract.generated.ts";
2
- import { runSweep } from "../analysis.ts";
2
+ import { productionMode, runSweep } from "../analysis.ts";
3
3
  import { retainedLines } from "../exempt.ts";
4
4
  import { partialNotes } from "../partial.ts";
5
5
  import { resolvePath } from "../paths.ts";
@@ -19,7 +19,7 @@ export const printRetainedVerb: Verb = ({ out, err, host, inputs, scope, openCli
19
19
  const scoped = scope();
20
20
  const swept = runSweep(openClient(config), host, scoped, config, {
21
21
  marked: [],
22
- mode: { production: true },
22
+ mode: productionMode(config),
23
23
  });
24
24
  for (const line of retainedLines(swept.matrix.union.symbols, swept.retained)) {
25
25
  out.write(`${line}\n`);