@cplieger/deadset-ts 8.0.0-dev.16 → 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 (131) 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 +58 -22
  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/tools.js +5 -9
  8. package/dist/src/component-files.js +3 -2
  9. package/dist/src/conformance-record.js +1 -1
  10. package/dist/src/consumers.js +6 -9
  11. package/dist/src/contextual-uses.js +1 -17
  12. package/dist/src/contract/config.generated.js +4 -1
  13. package/dist/src/contract/report.generated.js +6 -2
  14. package/dist/src/dependencies.js +25 -29
  15. package/dist/src/destructuring.js +1 -19
  16. package/dist/src/element-access.js +179 -164
  17. package/dist/src/entries/build-scripts.js +2 -2
  18. package/dist/src/entries/configuration-files.js +3 -7
  19. package/dist/src/entries/conventions.js +2 -2
  20. package/dist/src/entries/html-entries.js +7 -10
  21. package/dist/src/entries/mdx-entries.js +2 -2
  22. package/dist/src/entries/static-module.js +2 -1
  23. package/dist/src/facts.js +4 -4
  24. package/dist/src/findings/completion.js +40 -4
  25. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  26. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  27. package/dist/src/framework-lifecycle.js +41 -14
  28. package/dist/src/graph.js +12 -4
  29. package/dist/src/import-bindings.js +73 -28
  30. package/dist/src/index/sites.js +557 -0
  31. package/dist/src/indexed-access.js +523 -478
  32. package/dist/src/interface-satisfaction.js +1 -1
  33. package/dist/src/keyof-comparisons.js +124 -103
  34. package/dist/src/load/expected-module.js +2 -2
  35. package/dist/src/load/specifiers.js +2 -4
  36. package/dist/src/load/synthesize.js +5 -0
  37. package/dist/src/load/workspace-resolution.js +2 -2
  38. package/dist/src/matrix.js +6 -4
  39. package/dist/src/module-declarations.js +70 -85
  40. package/dist/src/overrides.js +134 -112
  41. package/dist/src/paths.js +46 -7
  42. package/dist/src/platform-calls.js +2 -2
  43. package/dist/src/platform-symbols.js +13 -19
  44. package/dist/src/references/read-rule.js +1 -0
  45. package/dist/src/references/read-rules.js +48 -0
  46. package/dist/src/references/sink.js +151 -0
  47. package/dist/src/references/sources.js +450 -0
  48. package/dist/src/references.js +110 -1285
  49. package/dist/src/roots/configured-declarations.js +3 -0
  50. package/dist/src/roots/root-set.js +1 -1
  51. package/dist/src/roots/unseen-loads.js +38 -10
  52. package/dist/src/serialization-contract.js +8 -3
  53. package/dist/src/stores.js +4 -2
  54. package/dist/src/sweep.js +25 -13
  55. package/dist/src/symbol-keys.js +89 -83
  56. package/dist/src/tests/file-class.js +76 -0
  57. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  58. package/dist/src/{test-support.js → tests/support.js} +2 -11
  59. package/dist/src/type-parameter-reads.js +178 -139
  60. package/dist/src/unimplemented-callees.js +262 -242
  61. package/dist/src/verbs/analyze.js +3 -3
  62. package/dist/src/verbs/explain.js +50 -28
  63. package/dist/src/verbs/print-retained.js +2 -2
  64. package/jsr.json +1 -1
  65. package/package.json +2 -2
  66. package/src/alias-chain.ts +0 -7
  67. package/src/amd.ts +130 -0
  68. package/src/analysis.ts +86 -23
  69. package/src/augmentations.ts +41 -27
  70. package/src/bindings.ts +460 -0
  71. package/src/calls.ts +25 -7
  72. package/src/commands/tools.ts +5 -11
  73. package/src/component-files.ts +4 -2
  74. package/src/conformance-record.ts +1 -1
  75. package/src/consumers.ts +8 -11
  76. package/src/contextual-uses.ts +2 -22
  77. package/src/contract/config.generated.ts +4 -1
  78. package/src/contract/report.generated.ts +6 -2
  79. package/src/dependencies.ts +27 -28
  80. package/src/destructuring.ts +1 -31
  81. package/src/element-access.ts +188 -182
  82. package/src/entries/build-scripts.ts +2 -2
  83. package/src/entries/configuration-files.ts +11 -8
  84. package/src/entries/conventions.ts +2 -1
  85. package/src/entries/html-entries.ts +15 -14
  86. package/src/entries/mdx-entries.ts +2 -2
  87. package/src/entries/static-module.ts +2 -1
  88. package/src/facts.ts +5 -3
  89. package/src/findings/boundary.ts +2 -0
  90. package/src/findings/completion.ts +43 -4
  91. package/src/findings/emitter.ts +1 -1
  92. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  93. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  94. package/src/framework-lifecycle.ts +42 -14
  95. package/src/graph.ts +24 -6
  96. package/src/import-bindings.ts +98 -29
  97. package/src/index/sites.ts +781 -0
  98. package/src/indexed-access.ts +570 -527
  99. package/src/interface-satisfaction.ts +1 -1
  100. package/src/keyof-comparisons.ts +131 -116
  101. package/src/load/expected-module.ts +8 -2
  102. package/src/load/specifiers.ts +8 -5
  103. package/src/load/synthesize.ts +6 -0
  104. package/src/load/workspace-resolution.ts +9 -2
  105. package/src/matrix.ts +6 -3
  106. package/src/module-declarations.ts +74 -98
  107. package/src/overrides.ts +162 -134
  108. package/src/paths.ts +54 -8
  109. package/src/platform-calls.ts +3 -3
  110. package/src/platform-symbols.ts +11 -27
  111. package/src/references/read-rule.ts +60 -0
  112. package/src/references/read-rules.ts +55 -0
  113. package/src/references/sink.ts +213 -0
  114. package/src/references/sources.ts +632 -0
  115. package/src/references.ts +168 -1733
  116. package/src/report.ts +1 -1
  117. package/src/roots/configured-declarations.ts +3 -0
  118. package/src/roots/root-set.ts +1 -1
  119. package/src/roots/unseen-loads.ts +49 -11
  120. package/src/serialization-contract.ts +8 -3
  121. package/src/stores.ts +13 -2
  122. package/src/sweep.ts +37 -14
  123. package/src/symbol-keys.ts +102 -102
  124. package/src/tests/file-class.ts +118 -0
  125. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  126. package/src/{test-support.ts → tests/support.ts} +9 -20
  127. package/src/type-parameter-reads.ts +187 -158
  128. package/src/unimplemented-callees.ts +297 -280
  129. package/src/verbs/analyze.ts +3 -2
  130. package/src/verbs/explain.ts +74 -31
  131. package/src/verbs/print-retained.ts +2 -2
package/src/analysis.ts CHANGED
@@ -26,8 +26,9 @@ import { readSources, type SourceFacts } from "./entries/sources.ts";
26
26
  import { readWorkflows, type Workflows } from "./entries/workflow-steps.ts";
27
27
  import { readEdgeSides } from "./edges.ts";
28
28
  import { enumGroup } from "./enum-group.ts";
29
+ import { bindingsOf, distinctBindings, type BindingRecord } from "./bindings.ts";
29
30
  import { generatedFile, generatedFilesOf, type GeneratedFact } from "./generated-file.ts";
30
- import { inOperandNames } from "./in-operands.ts";
31
+ import { inOperandNames } from "./findings/in-operands.ts";
31
32
  import { sourceFilesOf } from "./source-files.ts";
32
33
  import {
33
34
  evidenceRecords,
@@ -49,7 +50,7 @@ import type { EmitterInput } from "./findings/emitter.ts";
49
50
  import { unmatchedRootFindings, type SelfCheckFacts } from "./findings/self-check.ts";
50
51
  import { frameworkLifecycle } from "./framework-lifecycle.ts";
51
52
  import type { Host } from "./host.ts";
52
- import { implementations, mergeImplementations } from "./implementations.ts";
53
+ import { implementations, mergeImplementations } from "./findings/implementations.ts";
53
54
  import { injectionContainer } from "./injection-container.ts";
54
55
  import { interfaceSatisfaction, settledSatisfaction } from "./interface-satisfaction.ts";
55
56
  import { intraFunctionFacts, projectParts, type ProjectParts } from "./intra-function-parts.ts";
@@ -78,7 +79,7 @@ import { byPosition, isComponentFile, type Position } from "./position.ts";
78
79
  import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
79
80
  import { skippedUnits, type SkippedUnit } from "./type-errors.ts";
80
81
  import { reflectiveLookup } from "./reflective-lookup.ts";
81
- import { references, testFileRulesOf, type Reference, type TestFileRule } from "./references.ts";
82
+ import { references, type Reference } from "./references.ts";
82
83
  import {
83
84
  rootSet,
84
85
  unmatchedEverywhere,
@@ -88,12 +89,20 @@ import {
88
89
  } from "./roots/root-set.ts";
89
90
  import type { Scope } from "./scope.ts";
90
91
  import { decodedMembers, serializationContract, type Decoded } from "./serialization-contract.ts";
91
- import { outsideTestReferences, testFilesAt, type OutsideTest } from "./outside-tests.ts";
92
+ import { outsideTestReferences, testFilesAt, type OutsideTest } from "./tests/outside.ts";
92
93
  import type { UnseenLoad } from "./roots/unseen-loads.ts";
93
94
  import type { UnresolvedSpecifier } from "./load/specifiers.ts";
95
+ import { formedForNode } from "./load/synthesize.ts";
94
96
  import type { UnreadInput } from "./load/plan.ts";
95
97
  import { GLOBALS_RUNNER, namesTestGlobals } from "./load/test-globals.ts";
96
- import { supportReferences, testSupportFiles, type TestEvidence } from "./test-support.ts";
98
+ import {
99
+ targetTestFile,
100
+ testFilePaths,
101
+ testFileRulesOf,
102
+ withheldBy,
103
+ type TestFileRule,
104
+ } from "./tests/file-class.ts";
105
+ import { testSupportFiles, type TestEvidence } from "./tests/support.ts";
97
106
  import type { Engine, ProjectView } from "./session.ts";
98
107
  import { accessorsOf, storesOf } from "./stores.ts";
99
108
  import { isPartKind, type SuppressionRecord, type Suppressions, type Verdict } from "./suppress.ts";
@@ -179,8 +188,12 @@ interface ProjectRead {
179
188
  readonly skipped: readonly SkippedUnit[];
180
189
  /** The project's generated files, by path below the target root, each with what decided it. */
181
190
  readonly generated: ReadonlyMap<string, GeneratedFact>;
191
+ /** The binding identities its generated files publish (`bindings.6`, `bindings.7`). */
192
+ readonly bindings: readonly BindingRecord[];
182
193
  /** What the project's entry sources enter and name, read once. */
183
194
  readonly sources: SourceFacts;
195
+ /** Whether the run formed the program to resolve its modules as Node does (program.36). */
196
+ readonly formedForNode: boolean;
184
197
  }
185
198
 
186
199
  /** One target project the run analyzes, as its stage is handed it. */
@@ -195,6 +208,8 @@ interface ProjectsRead<Answer> {
195
208
  readonly answers: readonly Answer[];
196
209
  /** Every reference a consumer makes to a declaration of the target, each once. */
197
210
  readonly references: readonly Reference[];
211
+ /** The declarations a consumer's read rules held, each once. */
212
+ readonly consumerHeld: readonly string[];
198
213
  /** The test files below the target root no project the run reads holds, parsed. */
199
214
  readonly outsideTests: readonly OutsideTest[];
200
215
  /** The target root's workflow steps, git hooks and makefile, read once for every project. */
@@ -246,6 +261,7 @@ function readProjects<Answer>(
246
261
  workflows,
247
262
  });
248
263
  const rooted = rootSet(project, held, targetRoot, { ...options, sources, skips });
264
+ const generated = generatedFilesOf(own, sources.generated);
249
265
  return {
250
266
  configuration: planned.id,
251
267
  configFile: project.configFile,
@@ -257,8 +273,18 @@ function readProjects<Answer>(
257
273
  .ownSourceFiles()
258
274
  .filter((file) => referenceOnly(file.fileName))
259
275
  .flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
260
- generated: generatedFilesOf(own, sources.generated),
276
+ generated,
277
+ bindings: bindingsOf(
278
+ own.byPath,
279
+ generated,
280
+ new Map(
281
+ held.symbols
282
+ .filter((one) => one.kind === "file")
283
+ .map((one) => [one.position.path, one.ref]),
284
+ ),
285
+ ),
261
286
  sources,
287
+ formedForNode: formedForNode(planned),
262
288
  };
263
289
  };
264
290
  for (const file of project.ownSourceFiles()) {
@@ -267,11 +293,16 @@ function readProjects<Answer>(
267
293
  return stage(project, { configuration: planned.id, read });
268
294
  });
269
295
  const consumed = new Map<string, Reference>();
296
+ const consumerHeld = new Set<string>();
270
297
  for (const { project, consumer } of classified.consumed) {
271
298
  const side = { id: consumer.id, root: consumer.path };
272
- for (const reference of consumerReferences(project, host, targetRoot, side, config, parse)) {
299
+ const read = consumerReferences(project, host, targetRoot, side, config, parse);
300
+ for (const reference of read.references) {
273
301
  consumed.set(referenceKey(reference), reference);
274
302
  }
303
+ for (const id of read.held) {
304
+ consumerHeld.add(id);
305
+ }
275
306
  }
276
307
  const outsideTests = testFilesAt(
277
308
  host,
@@ -281,7 +312,13 @@ function readProjects<Answer>(
281
312
  ),
282
313
  parse,
283
314
  );
284
- return { answers, references: [...consumed.values()], outsideTests, workflows };
315
+ return {
316
+ answers,
317
+ references: [...consumed.values()],
318
+ consumerHeld: [...consumerHeld],
319
+ outsideTests,
320
+ workflows,
321
+ };
285
322
  });
286
323
  }
287
324
 
@@ -386,6 +423,14 @@ export function runRoots(
386
423
  };
387
424
  }
388
425
 
426
+ /** The production mode a configuration sets: a consumer's test reference is classed as `analysis.consumer_tests` says. */
427
+ export function productionMode(config: PartialConfig): Mode {
428
+ return {
429
+ production: true,
430
+ consumerTestsProduction: config.analysis.consumer_tests === "production",
431
+ };
432
+ }
433
+
389
434
  /** The run's sweep, beside the matrix it judged. */
390
435
  export interface RunSweep {
391
436
  readonly matrix: Matrix;
@@ -442,6 +487,8 @@ interface SweptProject<Extra> {
442
487
  readonly unanswered: readonly string[];
443
488
  /** The data members a generic declared outside the analysis fills, which it may read. */
444
489
  readonly filledOutside: readonly string[];
490
+ /** What only a bundler's retry of an import Node leaves unresolved names (program.38). */
491
+ readonly possiblyLoaded: readonly string[];
445
492
  readonly typeErrorSkips: readonly TypeErrorSkip[];
446
493
  readonly skipped: readonly SkippedUnit[];
447
494
  /** The component files the project holds of its own, by path below the target root. */
@@ -457,6 +504,9 @@ interface SweptProject<Extra> {
457
504
  interface ReadProject<Extra> {
458
505
  readonly projectRead: ProjectRead;
459
506
  readonly references: readonly Reference[];
507
+ /** The declarations its read rules held where the checker did not answer. */
508
+ readonly held: readonly string[];
509
+ readonly possiblyLoaded: readonly string[];
460
510
  readonly testFiles: readonly string[];
461
511
  readonly evidence: readonly Exemption[];
462
512
  readonly componentFiles: readonly string[];
@@ -578,11 +628,13 @@ function readRun<Extra>(
578
628
  );
579
629
  const read = readProjects<ReadProject<Extra>>(engine, host, scope, config, (project, opened) => {
580
630
  const projectRead = opened.read();
631
+ const testFile = targetTestFile(projectRead.sources.testFiles);
581
632
  const resolved = references(project, projectRead.held, targetRoot, {
582
- testFiles: config.ts.test_files,
583
- testFileNames: projectRead.sources.testFiles,
633
+ testFile,
584
634
  directories,
635
+ formedForNode: projectRead.formedForNode,
585
636
  });
637
+ const testFiles = testFilePaths(project, targetRoot, testFile);
586
638
  const evidence = evidenceRecords(
587
639
  {
588
640
  project,
@@ -607,7 +659,9 @@ function readRun<Extra>(
607
659
  return {
608
660
  projectRead,
609
661
  references: resolved.references,
610
- testFiles: resolved.testFilePaths,
662
+ held: resolved.held,
663
+ possiblyLoaded: resolved.possiblyLoaded,
664
+ testFiles,
611
665
  evidence,
612
666
  componentFiles: components.map(pathOf),
613
667
  imports: new Map(
@@ -638,14 +692,14 @@ function readRun<Extra>(
638
692
  decoded: decodedMembers(
639
693
  project,
640
694
  projectRead.held,
641
- productionFiles(project, targetRoot, resolved.testFilePaths),
695
+ productionFiles(project, targetRoot, testFiles),
642
696
  consumers,
643
697
  ),
644
698
  extra: extra(
645
699
  project,
646
700
  projectRead,
647
701
  resolved.references,
648
- resolved.testFilePaths,
702
+ testFiles,
649
703
  resolved.unseenLoads.map((position) => ({
650
704
  configuration: projectRead.configuration,
651
705
  position,
@@ -705,7 +759,7 @@ function readRun<Extra>(
705
759
  testFiles: new Set([...one.testFiles, ...support]),
706
760
  });
707
761
  const exempt = detected.records;
708
- const made = supportReferences([...one.references, ...(satisfied?.references ?? [])], support);
762
+ const made = [...one.references, ...(satisfied?.references ?? [])];
709
763
  const held = new Set(projectRead.held.symbols.map((symbol) => symbol.position.path));
710
764
  const supportFiles = [...support].filter((path) => held.has(path)).sort(bytewise);
711
765
  const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort(bytewise);
@@ -732,13 +786,13 @@ function readRun<Extra>(
732
786
  unanswered: [
733
787
  ...projectRead.held.unanswered,
734
788
  ...projectRead.rooted.unanswered,
735
- ...made
736
- .filter((reference) => reference.resolution === "by-name")
737
- .map((reference) => reference.to),
789
+ ...made.filter((reference) => reference.guessed === true).map((reference) => reference.to),
790
+ ...one.held,
738
791
  ...exempt.filter(isUnansweredRecord).map((record) => record.id),
739
792
  ...one.decoded.unanswered,
740
793
  ],
741
794
  filledOutside: one.decoded.filled,
795
+ possiblyLoaded: one.possiblyLoaded,
742
796
  typeErrorSkips: projectRead.typeErrorSkips,
743
797
  unmappedEntries: projectRead.rooted.unmappedEntries,
744
798
  heldScripts: projectRead.rooted.heldScripts,
@@ -766,7 +820,8 @@ function readRun<Extra>(
766
820
  unansweredHeld: [
767
821
  ...new Set([
768
822
  ...answers.flatMap((one) => one.unanswered),
769
- ...consumed.filter((one) => one.resolution === "by-name").map((one) => one.to),
823
+ ...consumed.filter((one) => one.guessed === true).map((one) => one.to),
824
+ ...read.result.consumerHeld,
770
825
  ]),
771
826
  ],
772
827
  projects,
@@ -855,8 +910,8 @@ function countedWrites(
855
910
  mode: Mode,
856
911
  ): ReadonlySet<string> {
857
912
  return new Set(
858
- supportReferences(references, support).flatMap((reference) =>
859
- reference.use === "write" && !(mode.production && reference.test) ? [reference.to] : [],
913
+ references.flatMap((reference) =>
914
+ reference.use === "write" && !withheldBy(mode, reference, support) ? [reference.to] : [],
860
915
  ),
861
916
  );
862
917
  }
@@ -869,8 +924,9 @@ function writtenOnly<Extra>(read: ReadRun<Extra>, mode: Mode): ReadonlySet<strin
869
924
  const written = new Set<string>();
870
925
  const readSome = new Set<string>();
871
926
  for (const { configured } of read.projects) {
927
+ const support = new Set(configured.supportFiles ?? []);
872
928
  for (const reference of configured.references) {
873
- if (mode.production && reference.test) {
929
+ if (withheldBy(mode, reference, support)) {
874
930
  continue;
875
931
  }
876
932
  if (reference.use === "write" && reference.consumer === undefined) {
@@ -902,7 +958,7 @@ function sweptOf<Extra>(
902
958
  // over what it reads, the test of dead code among them, cannot judge it.
903
959
  for (const { configured } of read.projects) {
904
960
  for (const reference of configured.references) {
905
- if (reference.resolution === "by-name") {
961
+ if (reference.guessed === true) {
906
962
  held.add(reference.from);
907
963
  }
908
964
  }
@@ -911,7 +967,7 @@ function sweptOf<Extra>(
911
967
  const answered = matrixOf(
912
968
  read.projects.map(({ configured }) => ({
913
969
  ...configured,
914
- references: configured.references.filter((one) => one.resolution !== "by-name"),
970
+ references: configured.references.filter((one) => one.guessed !== true),
915
971
  })),
916
972
  );
917
973
  const bare: SweepInput = {
@@ -972,6 +1028,7 @@ interface EmitterExtra {
972
1028
  readonly implementations: ReturnType<typeof implementations>;
973
1029
  readonly heldByInclusion: readonly string[];
974
1030
  readonly generated: ReadonlyMap<string, GeneratedFact>;
1031
+ readonly bindings: readonly BindingRecord[];
975
1032
  readonly inOperands: readonly string[];
976
1033
  readonly rooted: Roots;
977
1034
  readonly directives: readonly InlineDirective[];
@@ -1024,6 +1081,7 @@ export function readEmitterRun(
1024
1081
  implementations: implementations(project, read.held),
1025
1082
  heldByInclusion: heldByInclusion(project, targetRoot),
1026
1083
  generated: read.generated,
1084
+ bindings: read.bindings,
1027
1085
  inOperands: inOperandNames(project, targetRoot, testFiles),
1028
1086
  rooted: read.rooted,
1029
1087
  directives: inlineDirectives(project, read.held, targetRoot).filter(
@@ -1112,6 +1170,7 @@ export function emitterInputOver(
1112
1170
  stores: storesOf(
1113
1171
  projects.map((one) => ({
1114
1172
  references: one.configured.references,
1173
+ support: new Set(one.configured.supportFiles ?? []),
1115
1174
  accessors: one.extra.accessors,
1116
1175
  })),
1117
1176
  swept.exempt,
@@ -1143,6 +1202,7 @@ export function emitterInputOver(
1143
1202
  encapsulated: readManifest(host, targetRoot).declaresExports,
1144
1203
  edges: readEdgeSides(host, targetRoot),
1145
1204
  filledOutside: new Set(projects.flatMap((one) => one.filledOutside)),
1205
+ possiblyLoaded: new Set(projects.flatMap((one) => one.possiblyLoaded)),
1146
1206
  },
1147
1207
  selfCheck,
1148
1208
  intraFunction: intraFunctionFacts(
@@ -1232,6 +1292,7 @@ function readAnalysis(
1232
1292
  ),
1233
1293
  unseenLoads: read.projects.flatMap((one) => one.extra.unseenLoads),
1234
1294
  generated: new Map(read.projects.flatMap((one) => [...one.extra.generated])),
1295
+ bindings: distinctBindings(read.projects.flatMap((one) => one.extra.bindings)),
1235
1296
  },
1236
1297
  configured: read.projects.map((one) => one.configured),
1237
1298
  symbols,
@@ -1340,6 +1401,8 @@ interface RunFacts {
1340
1401
  readonly unseenLoads: readonly UnseenLoad[];
1341
1402
  /** Every generated file of the run, by its path below the target root, each once. */
1342
1403
  readonly generated: ReadonlyMap<string, GeneratedFact>;
1404
+ /** Every binding record the run's generated files publish, each once. */
1405
+ readonly bindings: readonly BindingRecord[];
1343
1406
  }
1344
1407
 
1345
1408
  /** One configuration whose component files a run cannot read. */
@@ -78,7 +78,7 @@ function holdersOf(
78
78
  * file or a `declare` holds that a `declare global` block, or the top level of a file in
79
79
  * `scripts`, declares, directly or inside a namespace there.
80
80
  */
81
- export function ambientGlobalInterfaces(
81
+ function ambientGlobalInterfaces(
82
82
  held: Inventory,
83
83
  interfaces: readonly string[],
84
84
  scripts: ReadonlySet<string>,
@@ -89,19 +89,31 @@ export function ambientGlobalInterfaces(
89
89
  : interfaces.filter((id) => held.ambient.has(id) && holders(id).global);
90
90
  }
91
91
 
92
+ /** The ambient global interfaces of a project's own files that merge into a declaration outside them. */
93
+ interface GlobalMerges {
94
+ /** The files that are no module, by identifier. */
95
+ readonly scripts: ReadonlySet<string>;
96
+ /** Each such interface, by identifier. */
97
+ readonly interfaces: ReadonlySet<string>;
98
+ /**
99
+ * Each such interface with every interface, namespace and block between it and the global
100
+ * scope: the outside declaration names it wherever a value is typed by it. Where every
101
+ * outside declaration is a package's, not the compiler's library, and declares no member,
102
+ * each member it declares as well: such an interface is a slot the package reads by type,
103
+ * as `keyof` does, in declarations the analysis does not walk.
104
+ */
105
+ readonly kept: readonly string[];
106
+ }
107
+
92
108
  /**
93
- * Each ambient global interface of the project's own files that merges into an interface
94
- * declared outside them, with every interface, namespace and block between it and the
95
- * global scope: the outside declaration names it wherever a value is typed by it. Where
96
- * every outside declaration is a package's, not the compiler's library, and declares no
97
- * member, each member it declares as well: such an interface is a slot the package reads
98
- * by type, as `keyof` does, in declarations the analysis does not walk.
109
+ * Which ambient global interfaces of the project's own files, `files`, merge into an interface
110
+ * declared outside them, and what each merge keeps.
99
111
  */
100
112
  export function mergedGlobalInterfaces<Brand>(
101
113
  project: ProjectView<Brand>,
102
114
  held: Inventory,
103
115
  files: SourceFiles,
104
- ): readonly string[] {
116
+ ): GlobalMerges {
105
117
  const scripts = new Set<string>();
106
118
  const names = new Map<string, Node>();
107
119
  for (const file of files.byName.values()) {
@@ -124,7 +136,7 @@ export function mergedGlobalInterfaces<Brand>(
124
136
  const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
125
137
  const asked = [...names].filter(([id]) => global.has(id));
126
138
  if (asked.length === 0) {
127
- return [];
139
+ return { scripts, interfaces: new Set(), kept: [] };
128
140
  }
129
141
  const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
130
142
  const holders = holdersOf(held, scripts);
@@ -135,24 +147,26 @@ export function mergedGlobalInterfaces<Brand>(
135
147
  const node = project.declarationAt(handle)?.node;
136
148
  return node !== undefined && isInterfaceDeclaration(node) && node.members.length === 0;
137
149
  };
138
- return [
139
- ...new Set(
140
- asked.flatMap(([id], at) => {
141
- const symbol = answers[at];
142
- if (typeof symbol !== "object") {
143
- return [];
144
- }
145
- const outside = symbol.declarations.filter((handle) => !files.byName.has(handle.path));
146
- if (outside.length === 0) {
147
- return [];
148
- }
149
- const members = outside.every(empty)
150
- ? held.symbols.filter((one) => one.parent === id).map((one) => one.id)
151
- : [];
152
- return [id, ...(holders?.(id).ids ?? []), ...members];
153
- }),
154
- ),
155
- ];
150
+ const interfaces = new Set<string>();
151
+ const kept = new Set<string>();
152
+ asked.forEach(([id], at) => {
153
+ const symbol = answers[at];
154
+ const outside =
155
+ typeof symbol === "object"
156
+ ? symbol.declarations.filter((handle) => !files.byName.has(handle.path))
157
+ : [];
158
+ if (outside.length === 0) {
159
+ return;
160
+ }
161
+ interfaces.add(id);
162
+ const members = outside.every(empty)
163
+ ? held.symbols.filter((one) => one.parent === id).map((one) => one.id)
164
+ : [];
165
+ for (const one of [id, ...(holders?.(id).ids ?? []), ...members]) {
166
+ kept.add(one);
167
+ }
168
+ });
169
+ return { scripts, interfaces, kept: [...kept] };
156
170
  }
157
171
 
158
172
  /**