@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
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The test files a derived matrix holds although no configuration's file list names them.
3
+ * The test runner compiles such a file with the options of the nearest compiler
4
+ * configuration at or above it, so the file joins that configuration's program as a file
5
+ * its list names, unless a `package.json` below that configuration's directory makes the
6
+ * file another package's, which its own test runner compiles.
7
+ */
8
+
9
+ import { sourcePaths } from "../file-facts.ts";
10
+ import type { Host } from "../host.ts";
11
+ import { ancestors, dirnamePath, joinPath } from "../paths.ts";
12
+ import { bytewise } from "../text/compare.ts";
13
+ import { holdsManifest } from "../fs/manifest.ts";
14
+ import type { Configuration } from "./discover.ts";
15
+ import type { ProgramPlan } from "./plan.ts";
16
+
17
+ const PREFERRED = "/tsconfig.json";
18
+
19
+ /** The configurations of one directory in the order a file joins them: `tsconfig.json` first. */
20
+ function byPreference(a: string, b: string): number {
21
+ const first = a.endsWith(PREFERRED) ? 0 : 1;
22
+ const second = b.endsWith(PREFERRED) ? 0 : 1;
23
+ return first - second || bytewise(a, b);
24
+ }
25
+
26
+ /**
27
+ * Per configuration of `configurations`, the absolute paths of the test files that join it:
28
+ * each file below `root` a pattern of `patterns` names, that no configuration's file list
29
+ * holds, joins the configuration nearest at or above its directory when no `package.json`
30
+ * sits between them.
31
+ */
32
+ export function joinedTestFiles(
33
+ host: Host,
34
+ root: string,
35
+ configurations: readonly Configuration[],
36
+ patterns: readonly RegExp[],
37
+ ): ReadonlyMap<string, readonly string[]> {
38
+ const listed = new Set<string>();
39
+ const byDir = new Map<string, string[]>();
40
+ for (const { configFile, parsed } of configurations) {
41
+ parsed.fileNames.forEach((name) => listed.add(name));
42
+ const dir = dirnamePath(configFile);
43
+ byDir.set(dir, [...(byDir.get(dir) ?? []), configFile].sort(byPreference));
44
+ }
45
+ const joins = new Map<string, string[]>();
46
+ for (const path of sourcePaths(host, root)) {
47
+ const file = joinPath(root, path);
48
+ if (listed.has(file) || !patterns.some((pattern) => pattern.test(path))) {
49
+ continue;
50
+ }
51
+ for (const dir of ancestors(dirnamePath(file), root)) {
52
+ const [nearest] = byDir.get(dir) ?? [];
53
+ if (nearest !== undefined) {
54
+ joins.set(nearest, [...(joins.get(nearest) ?? []), file]);
55
+ break;
56
+ }
57
+ if (holdsManifest(host, dir)) {
58
+ break;
59
+ }
60
+ }
61
+ }
62
+ return joins;
63
+ }
64
+
65
+ /**
66
+ * The plans with each joined test file added to the configuration it joins: to a described
67
+ * program's root files, or to the file list of a configuration only the snapshot holds, which
68
+ * lists every file the configuration lists and the joined ones.
69
+ */
70
+ export function joinedTests(
71
+ plans: readonly ProgramPlan[],
72
+ joins: ReadonlyMap<string, readonly string[]>,
73
+ ): readonly ProgramPlan[] {
74
+ return plans.map((plan): ProgramPlan => {
75
+ const files = joins.get(plan.configFile);
76
+ const { build } = plan;
77
+ if (files === undefined) {
78
+ return plan;
79
+ }
80
+ if (build.kind === "program") {
81
+ return {
82
+ ...plan,
83
+ build: {
84
+ kind: "program",
85
+ params: { ...build.params, rootFiles: [...(build.params.rootFiles ?? []), ...files] },
86
+ },
87
+ };
88
+ }
89
+ const written = build.kind === "configuration" ? build.value : { extends: plan.configFile };
90
+ return {
91
+ ...plan,
92
+ build: {
93
+ kind: "configuration",
94
+ value: { ...written, files: [...plan.parsed.fileNames, ...files], include: [] },
95
+ },
96
+ };
97
+ });
98
+ }
@@ -7,11 +7,12 @@
7
7
  */
8
8
 
9
9
  import { emittedFrom } from "./emit-map.ts";
10
- import type { Host } from "./host.ts";
11
- import { readManifest, type Manifest, type ManifestEntry } from "./manifest.ts";
12
- import { joinPath, relativePath } from "./paths.ts";
10
+ import type { Host } from "../host.ts";
11
+ import { readManifest, type Manifest, type ManifestEntry } from "../manifest.ts";
12
+ import { joinPath, relativePath } from "../paths.ts";
13
13
  import type { WorkspaceMember } from "./workspace.ts";
14
14
  import type { WorkspaceResolver } from "./workspace-resolution.ts";
15
+ import { MANIFEST } from "../fs/manifest.ts";
15
16
 
16
17
  /** The one character a subpath pattern of a manifest's `exports` holds. */
17
18
  const SUBPATH_WILDCARD = "*";
@@ -84,8 +85,7 @@ export function memberEntries(
84
85
  }
85
86
  const manifest = readManifest(host, member.dir);
86
87
  const where =
87
- relativePath(targetRoot, joinPath(member.dir, "package.json")) ??
88
- joinPath(member.dir, "package.json");
88
+ relativePath(targetRoot, joinPath(member.dir, MANIFEST)) ?? joinPath(member.dir, MANIFEST);
89
89
  const kept = manifest.entries.flatMap((entry): ManifestEntry[] => {
90
90
  const exported = entry.member.startsWith("exports");
91
91
  if (member.private && exported) {
@@ -16,10 +16,16 @@ import type {
16
16
  } from "@typescript/native/sync";
17
17
  import { isStringLiteralLikeNode } from "@typescript/native/ast";
18
18
  import type { StringLiteralLikeNode } from "@typescript/native/ast";
19
- import type { Host } from "./host.ts";
20
- import { dirnamePath, resolvePath } from "./paths.ts";
21
- import type { Engine } from "./session.ts";
19
+ import type { Host } from "../host.ts";
20
+ import { JAVASCRIPT_FILE } from "../javascript-file.ts";
21
+ import { isRecord } from "../json-document.ts";
22
+ import { dirnamePath, resolvePath } from "../paths.ts";
23
+ import { batched, type Engine, type ProgramBuild } from "../session.ts";
22
24
  import type { WorkspaceResolver } from "./workspace-resolution.ts";
25
+ import { bytewise } from "../text/compare.ts";
26
+ import { PRODUCT } from "../product.ts";
27
+ import type { Parsed } from "./discover.ts";
28
+ import type { Build, CompilerOptions } from "./plan.ts";
23
29
 
24
30
  /** One member import no member source answers for. */
25
31
  interface UnresolvedImport {
@@ -42,31 +48,35 @@ export interface MemberReading {
42
48
  readonly componentsUnread: string | undefined;
43
49
  }
44
50
 
45
- /** How a snapshot reads one configuration that imports a member. */
46
- export type WorkspaceProgram =
47
- | { readonly kind: "program"; readonly params: CreateSnapshotProgramParams }
48
- /** A configuration only the snapshot holds, at a path no file has. */
49
- | { readonly kind: "configuration"; readonly file: string; readonly text: string };
51
+ /** One configuration as a written configuration or a described program. */
52
+ type Written = Exclude<Build, { readonly kind: "project" }>;
50
53
 
51
54
  /** The programs one run builds instead of opening projects, with what reading them did. */
52
55
  interface WorkspacePrograms {
53
56
  /** Per configuration that imports a member, how the snapshot reads it. */
54
- readonly programs: ReadonlyMap<string, WorkspaceProgram>;
57
+ readonly programs: ReadonlyMap<string, Written>;
55
58
  readonly readings: readonly MemberReading[];
56
59
  }
57
60
 
58
- type Parsed = ReturnType<Engine["parseConfigFile"]>;
59
-
60
- /** The most questions one round trip carries. */
61
- const BATCH = 2048;
61
+ /** One configuration the rounds read: its parse and the options the plan builds it with. */
62
+ export interface PlannedConfiguration {
63
+ readonly configFile: string;
64
+ readonly parsed: Parsed;
65
+ readonly options: CompilerOptions;
66
+ /** The configuration of a program the run forms, which only the snapshot holds, at `configFile`. */
67
+ readonly written?: Readonly<Record<string, unknown>> | undefined;
68
+ }
62
69
 
63
70
  /**
64
- * The options a configuration's program is built with: its own, less the ones that
65
- * lay out an emitted program's inputs and outputs. The analysis emits nothing, and a
66
- * member's source is outside the importer's root directory by construction, which
67
- * the compiler refuses under such a layout.
71
+ * The options a configuration's program is built with: the plan's, less the ones that lay
72
+ * out an emitted program's inputs and outputs. The analysis emits nothing, and a member's
73
+ * source is outside the importer's root directory by construction, which the compiler
74
+ * refuses under such a layout.
68
75
  */
69
- function analysisOptions(parsed: Parsed): CreateSnapshotProgramParams["compilerOptions"] {
76
+ function analysisOptions(
77
+ options: CompilerOptions,
78
+ readsJavaScript: boolean,
79
+ ): CreateSnapshotProgramParams["compilerOptions"] {
70
80
  const {
71
81
  composite,
72
82
  rootDir: _rootDir,
@@ -74,8 +84,24 @@ function analysisOptions(parsed: Parsed): CreateSnapshotProgramParams["compilerO
74
84
  declarationDir: _declarationDir,
75
85
  tsBuildInfoFile: _tsBuildInfoFile,
76
86
  ...kept
77
- } = parsed.options;
78
- return composite === true ? { ...kept, declaration: true } : kept;
87
+ } = options;
88
+ const read = readsJavaScript ? { ...kept, allowJs: true } : kept;
89
+ return composite === true ? { ...read, declaration: true } : read;
90
+ }
91
+
92
+ /**
93
+ * Whether one reading answers a member import with a JavaScript file, which its program reads
94
+ * as source only where it allows JavaScript, a program formed for a JavaScript member being the
95
+ * member's source.
96
+ */
97
+ function readsJavaScript(reading: Reading): boolean {
98
+ return (
99
+ reading.options.allowJs !== true &&
100
+ reading.entries.some((entry) => {
101
+ const file = entry.result.resolvedFileName;
102
+ return typeof file === "string" && JAVASCRIPT_FILE.test(file);
103
+ })
104
+ );
79
105
  }
80
106
 
81
107
  /** The conditions an `exports` lookup under one configuration and one resolution mode reads. */
@@ -97,9 +123,7 @@ function resolutionMode(kind: ModuleKind): ResolutionMode | undefined {
97
123
  }
98
124
 
99
125
  /** One configuration's state across the rounds. */
100
- interface Reading {
101
- readonly configFile: string;
102
- readonly parsed: Parsed;
126
+ interface Reading extends PlannedConfiguration {
103
127
  readonly entries: ModuleResolutionEntry[];
104
128
  readonly seen: Set<string>;
105
129
  /** Per member specifier, every file an import of it is read as. */
@@ -121,34 +145,56 @@ function unstatable(reading: Reading): string | undefined {
121
145
  return undefined;
122
146
  }
123
147
 
124
- /** The path beside `configFile` its written configuration takes, which no file has. */
125
- export function writtenPath(host: Host, configFile: string): string {
148
+ /** The path beside `configFile` its written configuration takes, which no file and none of `taken` has. */
149
+ export function writtenPath(
150
+ host: Host,
151
+ configFile: string,
152
+ taken: ReadonlySet<string> = new Set(),
153
+ ): string {
126
154
  const stem = configFile.replace(/\.json$/u, "");
127
155
  for (let counter = 0; ; counter += 1) {
128
- const path = `${stem}.deadset-ts${counter === 0 ? "" : `-${String(counter)}`}.json`;
129
- if (host.kindOf(path) === "absent") {
156
+ const path = `${stem}.${PRODUCT}${counter === 0 ? "" : `-${String(counter)}`}.json`;
157
+ if (host.kindOf(path) === "absent" && !taken.has(path)) {
130
158
  return path;
131
159
  }
132
160
  }
133
161
  }
134
162
 
135
163
  /**
136
- * The configuration that reads as one configuration's program: it extends it, lays out
137
- * no output for the reason {@link analysisOptions} gives, maps each answered specifier
138
- * to its file, and carries the references the program does. The compiler's default
139
- * `exclude` names the output directories this drops, so where the file list moves it is
140
- * restated; a list that still differs, or answers `paths` cannot state, are a reason.
164
+ * The path the snapshot holds one configuration's written form at: a formed configuration's
165
+ * own path, else a free one beside it that no configuration of `formed` holds, because a formed
166
+ * program in a directory with a `jsconfig.json` takes the name that file's written form would.
167
+ */
168
+ export function writtenFileOf(
169
+ host: Host,
170
+ configuration: Pick<PlannedConfiguration, "configFile" | "written">,
171
+ formed: ReadonlySet<string>,
172
+ ): string {
173
+ return configuration.written === undefined
174
+ ? writtenPath(host, configuration.configFile, formed)
175
+ : configuration.configFile;
176
+ }
177
+
178
+ /**
179
+ * The configuration that reads as one configuration's program: it extends it, lays out no output
180
+ * ({@link analysisOptions}), maps each answered specifier to its file and keeps its references.
181
+ * Where the file list moves, the default `exclude` the dropped output directories are named in is
182
+ * restated, and one that {@link readsJavaScript} lists its files outright, as `allowJs` widens its
183
+ * `include` to JavaScript; a list that still differs, or answers `paths` cannot state, are a
184
+ * reason. A formed configuration, which no file holds to extend, is its own value with `paths`.
141
185
  */
142
186
  function configurationOf(
143
187
  engine: Engine,
144
188
  host: Host,
145
189
  reading: Reading,
146
- ): WorkspaceProgram | { readonly kind: "unread"; readonly reason: string } {
190
+ formed: ReadonlySet<string>,
191
+ ): Written | { readonly kind: "unread"; readonly reason: string } {
147
192
  const reason = unstatable(reading);
148
193
  if (reason !== undefined) {
149
194
  return { kind: "unread", reason };
150
195
  }
151
- const { options, projectReferences: references = [] } = reading.parsed;
196
+ const { options } = reading;
197
+ const references = reading.parsed.projectReferences ?? [];
152
198
  const raw: unknown = (options as Record<string, unknown>)["pathsBasePath"];
153
199
  const base = typeof raw === "string" ? raw : dirnamePath(reading.configFile);
154
200
  const paths: Record<string, string[]> = {};
@@ -161,6 +207,17 @@ function configurationOf(
161
207
  paths[entry.moduleName] = [file];
162
208
  }
163
209
  }
210
+ if (reading.written !== undefined) {
211
+ const held = reading.written["compilerOptions"];
212
+ return {
213
+ kind: "configuration",
214
+ value: {
215
+ ...reading.written,
216
+ compilerOptions: { ...(isRecord(held) ? held : {}), paths },
217
+ },
218
+ };
219
+ }
220
+ const javascript = readsJavaScript(reading);
164
221
  const json = (exclude: readonly string[] | undefined): Record<string, unknown> => ({
165
222
  extends: reading.configFile,
166
223
  compilerOptions: {
@@ -169,18 +226,20 @@ function configurationOf(
169
226
  declarationDir: null,
170
227
  tsBuildInfoFile: null,
171
228
  ...(options.composite === true ? { composite: false, declaration: true } : {}),
229
+ ...(javascript ? { allowJs: true } : {}),
172
230
  paths,
173
231
  },
174
232
  ...(references.length > 0
175
233
  ? { references: references.map((reference) => ({ path: reference.path })) }
176
234
  : {}),
235
+ ...(javascript ? { files: [...reading.parsed.fileNames], include: [] } : {}),
177
236
  ...(exclude === undefined ? {} : { exclude }),
178
237
  });
179
- const file = writtenPath(host, reading.configFile);
180
- const expected = [...reading.parsed.fileNames].sort().join("\u0000");
238
+ const file = writtenFileOf(host, reading, formed);
239
+ const expected = [...reading.parsed.fileNames].sort(bytewise).join("\u0000");
181
240
  const listed = (value: Record<string, unknown>): string | undefined => {
182
241
  try {
183
- return [...engine.parseConfigJson(value, file).fileNames].sort().join("\u0000");
242
+ return [...engine.parseConfigJson(value, file).fileNames].sort(bytewise).join("\u0000");
184
243
  } catch {
185
244
  return undefined;
186
245
  }
@@ -198,7 +257,7 @@ function configurationOf(
198
257
  };
199
258
  }
200
259
  }
201
- return { kind: "configuration", file, text: `${JSON.stringify(value, null, 2)}\n` };
260
+ return { kind: "configuration", value };
202
261
  }
203
262
 
204
263
  /** The program one configuration is built as, under the answers it holds so far. */
@@ -206,7 +265,7 @@ function programOf(
206
265
  engine: Engine,
207
266
  reading: Reading,
208
267
  ): { readonly params: CreateSnapshotProgramParams; readonly resolver: ModuleResolver } {
209
- const compilerOptions = analysisOptions(reading.parsed);
268
+ const compilerOptions = analysisOptions(reading.options, readsJavaScript(reading));
210
269
  const references = reading.parsed.projectReferences ?? [];
211
270
  const resolver = engine.createModuleResolver(compilerOptions, reading.entries);
212
271
  return {
@@ -223,23 +282,6 @@ function programOf(
223
282
  };
224
283
  }
225
284
 
226
- function chunks<T>(items: readonly T[]): readonly (readonly T[])[] {
227
- const found: T[][] = [];
228
- for (let at = 0; at < items.length; at += BATCH) {
229
- found.push(items.slice(at, at + BATCH));
230
- }
231
- return found;
232
- }
233
-
234
- /** Asks one question of every item, in round trips of at most {@link BATCH}. */
235
- function batched<T, A>(
236
- engine: Engine,
237
- items: readonly T[],
238
- question: (item: T) => Parameters<Engine["batch"]>[0][number],
239
- ): A[] {
240
- return chunks(items).flatMap((run) => engine.batch(run.map(question)) as A[]);
241
- }
242
-
243
285
  /** One round over one program: answers the member imports no earlier round asked about. */
244
286
  function scan(
245
287
  engine: Engine,
@@ -332,6 +374,18 @@ type Built =
332
374
  }
333
375
  | { readonly kind: "configuration"; readonly file: string; readonly text: string };
334
376
 
377
+ function configurationText(value: Readonly<Record<string, unknown>>): string {
378
+ return `${JSON.stringify(value, null, 2)}\n`;
379
+ }
380
+
381
+ /** One configuration value as the snapshot holds it, at `file`, which no file on disk holds. */
382
+ export function writtenConfiguration(
383
+ file: string,
384
+ value: Readonly<Record<string, unknown>>,
385
+ ): Extract<ProgramBuild, { readonly kind: "configuration" }> {
386
+ return { kind: "configuration", file, text: configurationText(value) };
387
+ }
388
+
335
389
  /**
336
390
  * Answers the member imports of every reading `build` describes, round by round, all
337
391
  * the readings still adding answers built in one snapshot per round. A reading `build`
@@ -387,45 +441,38 @@ function readRounds(
387
441
  export function workspacePrograms(
388
442
  engine: Engine,
389
443
  host: Host,
390
- configFiles: readonly string[],
444
+ configurations: readonly PlannedConfiguration[],
391
445
  resolver: WorkspaceResolver,
392
446
  ): WorkspacePrograms {
393
- const readings: Reading[] = configFiles.flatMap((configFile) => {
394
- let parsed: Parsed;
395
- try {
396
- parsed = engine.parseConfigFile(configFile);
397
- } catch {
398
- // A configuration that does not read is opened as a project, which states why.
399
- return [];
400
- }
401
- return [
402
- {
403
- configFile,
404
- parsed,
405
- entries: [],
406
- seen: new Set<string>(),
407
- bound: new Map<string, Set<string>>(),
408
- unresolved: [],
409
- },
410
- ];
411
- });
447
+ const readings: Reading[] = configurations.map((configuration) => ({
448
+ ...configuration,
449
+ entries: [],
450
+ seen: new Set<string>(),
451
+ bound: new Map<string, Set<string>>(),
452
+ unresolved: [],
453
+ }));
454
+ const formed = new Set(
455
+ configurations.filter((one) => one.written !== undefined).map((one) => one.configFile),
456
+ );
412
457
  readRounds(engine, readings, resolver, (reading) => ({
413
458
  kind: "program",
414
459
  ...programOf(engine, reading),
415
460
  }));
416
461
  if (engine.readsComponents) {
417
462
  readRounds(engine, readings, resolver, (reading) => {
418
- const one = configurationOf(engine, host, reading);
419
- return one.kind === "configuration" ? one : undefined;
463
+ const one = configurationOf(engine, host, reading, formed);
464
+ return one.kind === "configuration"
465
+ ? writtenConfiguration(writtenFileOf(host, reading, formed), one.value)
466
+ : undefined;
420
467
  });
421
468
  }
422
- const programs = new Map<string, WorkspaceProgram>();
469
+ const programs = new Map<string, Written>();
423
470
  const unread = new Map<string, string>();
424
471
  for (const reading of readings) {
425
472
  if (reading.entries.length === 0) {
426
473
  continue;
427
474
  }
428
- const one = engine.readsComponents ? configurationOf(engine, host, reading) : undefined;
475
+ const one = engine.readsComponents ? configurationOf(engine, host, reading, formed) : undefined;
429
476
  if (one !== undefined && one.kind !== "unread") {
430
477
  programs.set(reading.configFile, one);
431
478
  continue;