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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/mapper-connection.js +2 -1
  3. package/dist/bin/node-host.js +24 -5
  4. package/dist/src/analysis.js +181 -341
  5. package/dist/src/augmentations.js +106 -13
  6. package/dist/src/automatic-types.js +10 -29
  7. package/dist/src/build-scripts.js +425 -0
  8. package/dist/src/component-layer.js +4 -2
  9. package/dist/src/components.js +2 -3
  10. package/dist/src/config.js +6 -82
  11. package/dist/src/configuration-files.js +28 -176
  12. package/dist/src/configured-declarations.js +15 -20
  13. package/dist/src/conformance-record.js +1 -1
  14. package/dist/src/consumers.js +3 -30
  15. package/dist/src/contextual-uses.js +56 -9
  16. package/dist/src/contract/config.generated.js +558 -0
  17. package/dist/src/contract/contract.generated.js +1244 -0
  18. package/dist/src/contract/report.generated.js +862 -0
  19. package/dist/src/convention-rows.js +73 -3
  20. package/dist/src/conventions.js +36 -66
  21. package/dist/src/dependencies.js +103 -40
  22. package/dist/src/edges.js +38 -122
  23. package/dist/src/element-access.js +4 -6
  24. package/dist/src/entry-point-gaps.js +2 -8
  25. package/dist/src/entry-points.js +11 -27
  26. package/dist/src/enum-group.js +3 -10
  27. package/dist/src/erased-stores.js +4 -6
  28. package/dist/src/exempt-classes.js +1 -63
  29. package/dist/src/exempt.js +21 -40
  30. package/dist/src/failure.js +36 -0
  31. package/dist/src/file-facts.js +2 -25
  32. package/dist/src/findings/completion.js +139 -72
  33. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  34. package/dist/src/findings/emitters.js +5 -5
  35. package/dist/src/findings/interfaces.js +4 -5
  36. package/dist/src/findings/intra-function.js +13 -10
  37. package/dist/src/findings/non-code-artifacts.js +7 -8
  38. package/dist/src/findings/reads-and-writes.js +16 -13
  39. package/dist/src/findings/self-check.js +10 -12
  40. package/dist/src/findings/unused-declarations.js +41 -32
  41. package/dist/src/findings/visibility-narrowing.js +19 -16
  42. package/dist/src/findings-pass.js +8 -15
  43. package/dist/src/framework-lifecycle.js +7 -7
  44. package/dist/src/fs/document.js +52 -0
  45. package/dist/src/fs/manifest.js +72 -0
  46. package/dist/src/fs/tree.js +88 -0
  47. package/dist/src/generated-file.js +7 -4
  48. package/dist/src/git-ignore.js +39 -26
  49. package/dist/src/graph.js +5 -3
  50. package/dist/src/html-entries.js +6 -33
  51. package/dist/src/in-operands.js +2 -1
  52. package/dist/src/index.js +1 -1
  53. package/dist/src/injection-container.js +1 -1
  54. package/dist/src/intra-function-parts.js +63 -43
  55. package/dist/src/inventory.js +2 -21
  56. package/dist/src/javascript-file.js +33 -0
  57. package/dist/src/json-document.js +198 -67
  58. package/dist/src/keyof-comparisons.js +4 -6
  59. package/dist/src/kinds.js +3 -366
  60. package/dist/src/load/classify.js +270 -0
  61. package/dist/src/load/diagnostics.js +80 -0
  62. package/dist/src/load/discover.js +277 -0
  63. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  64. package/dist/src/load/expected-module.js +136 -0
  65. package/dist/src/load/load.js +27 -0
  66. package/dist/src/load/module-lookup.js +37 -0
  67. package/dist/src/load/plan.js +313 -0
  68. package/dist/src/load/specifiers.js +115 -0
  69. package/dist/src/load/synthesize.js +140 -0
  70. package/dist/src/load/test-globals.js +207 -0
  71. package/dist/src/load/test-joins.js +82 -0
  72. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  73. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  74. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
  75. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  76. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  77. package/dist/src/manifest.js +49 -54
  78. package/dist/src/matrix.js +76 -4
  79. package/dist/src/mdx-entries.js +3 -26
  80. package/dist/src/outside-tests.js +1 -1
  81. package/dist/src/partial.js +2 -1
  82. package/dist/src/paths.js +26 -15
  83. package/dist/src/position.js +2 -1
  84. package/dist/src/print.js +11 -79
  85. package/dist/src/product.js +13 -0
  86. package/dist/src/query.js +1 -0
  87. package/dist/src/ref.js +1 -0
  88. package/dist/src/references.js +48 -30
  89. package/dist/src/report-schema.js +51 -0
  90. package/dist/src/report.js +57 -41
  91. package/dist/src/reporters/annotations.js +15 -8
  92. package/dist/src/reporters/sarif.js +16 -10
  93. package/dist/src/reporters/template.js +4 -19
  94. package/dist/src/resolve.js +245 -729
  95. package/dist/src/roots.js +273 -87
  96. package/dist/src/run.js +66 -77
  97. package/dist/src/schema.js +320 -343
  98. package/dist/src/scope.js +38 -115
  99. package/dist/src/serialization-contract.js +595 -21
  100. package/dist/src/session.js +34 -29
  101. package/dist/src/source-files.js +1 -1
  102. package/dist/src/suppress-file.js +25 -28
  103. package/dist/src/suppress-inline.js +3 -2
  104. package/dist/src/suppress.js +33 -51
  105. package/dist/src/sweep.js +7 -9
  106. package/dist/src/symbol-keys.js +3 -5
  107. package/dist/src/template-field.js +9 -19
  108. package/dist/src/test-support.js +8 -11
  109. package/dist/src/text/compare.js +26 -0
  110. package/dist/src/text/glob.js +194 -0
  111. package/dist/src/text/named-files.js +54 -0
  112. package/dist/src/text/shell.js +159 -0
  113. package/dist/src/text/yaml.js +393 -0
  114. package/dist/src/type-errors.js +11 -232
  115. package/dist/src/unseen-loads.js +100 -0
  116. package/dist/src/unwrap.js +23 -0
  117. package/dist/src/usage.js +10 -0
  118. package/dist/src/verbs/analyze.js +166 -126
  119. package/dist/src/verbs/describe.js +9 -16
  120. package/dist/src/verbs/explain.js +10 -7
  121. package/dist/src/verbs/print-config.js +2 -1
  122. package/dist/src/verbs/print-projects.js +6 -4
  123. package/dist/src/verbs/print-retained.js +4 -2
  124. package/dist/src/verbs/print-roots.js +4 -2
  125. package/dist/src/verbs/verb.js +1 -5
  126. package/dist/src/verbs/version.js +14 -3
  127. package/dist/src/workflow-steps.js +62 -64
  128. package/jsr.json +1 -1
  129. package/package.json +5 -4
  130. package/src/analysis.ts +245 -502
  131. package/src/augmentations.ts +135 -17
  132. package/src/automatic-types.ts +11 -29
  133. package/src/build-scripts.ts +543 -0
  134. package/src/component-layer.ts +4 -2
  135. package/src/components.ts +2 -4
  136. package/src/config.ts +31 -302
  137. package/src/configuration-files.ts +34 -179
  138. package/src/configured-declarations.ts +18 -23
  139. package/src/conformance-record.ts +1 -1
  140. package/src/consumers.ts +6 -57
  141. package/src/contextual-uses.ts +64 -12
  142. package/src/contract/config.generated.ts +818 -0
  143. package/src/contract/contract.generated.ts +1308 -0
  144. package/src/contract/report.generated.ts +1237 -0
  145. package/src/convention-rows.ts +85 -3
  146. package/src/conventions.ts +46 -84
  147. package/src/dependencies.ts +118 -46
  148. package/src/edges.ts +41 -140
  149. package/src/element-access.ts +4 -7
  150. package/src/entry-point-gaps.ts +3 -9
  151. package/src/entry-points.ts +12 -38
  152. package/src/enum-group.ts +2 -11
  153. package/src/erased-stores.ts +3 -7
  154. package/src/exempt-classes.ts +1 -75
  155. package/src/exempt.ts +36 -50
  156. package/src/failure.ts +63 -0
  157. package/src/file-facts.ts +9 -24
  158. package/src/finding.ts +14 -9
  159. package/src/findings/boundary.ts +2 -0
  160. package/src/findings/completion.ts +181 -84
  161. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  162. package/src/findings/emitter.ts +8 -9
  163. package/src/findings/emitters.ts +7 -7
  164. package/src/findings/interfaces.ts +4 -6
  165. package/src/findings/intra-function.ts +13 -11
  166. package/src/findings/non-code-artifacts.ts +9 -11
  167. package/src/findings/reads-and-writes.ts +20 -16
  168. package/src/findings/self-check.ts +11 -17
  169. package/src/findings/unused-declarations.ts +45 -32
  170. package/src/findings/visibility-narrowing.ts +20 -17
  171. package/src/findings-pass.ts +10 -19
  172. package/src/framework-lifecycle.ts +7 -7
  173. package/src/fs/document.ts +70 -0
  174. package/src/fs/manifest.ts +92 -0
  175. package/src/fs/tree.ts +110 -0
  176. package/src/generated-file.ts +7 -4
  177. package/src/git-ignore.ts +50 -26
  178. package/src/graph.ts +5 -3
  179. package/src/host.ts +12 -5
  180. package/src/html-entries.ts +6 -33
  181. package/src/in-operands.ts +2 -1
  182. package/src/index.ts +1 -1
  183. package/src/injection-container.ts +1 -1
  184. package/src/intra-function-parts.ts +73 -44
  185. package/src/inventory.ts +2 -20
  186. package/src/javascript-file.ts +37 -0
  187. package/src/json-document.ts +241 -76
  188. package/src/keyof-comparisons.ts +3 -7
  189. package/src/kinds.ts +5 -413
  190. package/src/load/classify.ts +397 -0
  191. package/src/load/diagnostics.ts +144 -0
  192. package/src/load/discover.ts +455 -0
  193. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  194. package/src/load/expected-module.ts +188 -0
  195. package/src/load/load.ts +73 -0
  196. package/src/load/module-lookup.ts +50 -0
  197. package/src/load/plan.ts +504 -0
  198. package/src/load/specifiers.ts +168 -0
  199. package/src/load/synthesize.ts +211 -0
  200. package/src/load/test-globals.ts +251 -0
  201. package/src/load/test-joins.ts +98 -0
  202. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  203. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  204. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
  205. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  206. package/src/{workspace.ts → load/workspace.ts} +90 -134
  207. package/src/manifest.ts +81 -75
  208. package/src/matrix.ts +122 -4
  209. package/src/mdx-entries.ts +3 -26
  210. package/src/outside-tests.ts +1 -1
  211. package/src/partial.ts +3 -2
  212. package/src/paths.ts +27 -15
  213. package/src/position.ts +2 -1
  214. package/src/print.ts +12 -92
  215. package/src/product.ts +18 -0
  216. package/src/query.ts +11 -0
  217. package/src/ref.ts +1 -0
  218. package/src/references.ts +52 -31
  219. package/src/report-schema.ts +68 -0
  220. package/src/report.ts +101 -224
  221. package/src/reporters/annotations.ts +22 -11
  222. package/src/reporters/json.ts +1 -1
  223. package/src/reporters/reporter.ts +2 -4
  224. package/src/reporters/reporters.ts +1 -1
  225. package/src/reporters/sarif.ts +29 -16
  226. package/src/reporters/template.ts +4 -20
  227. package/src/reporters/text.ts +3 -3
  228. package/src/resolve.ts +283 -1114
  229. package/src/roots.ts +339 -104
  230. package/src/run.ts +75 -91
  231. package/src/schema.ts +391 -388
  232. package/src/scope.ts +46 -138
  233. package/src/serialization-contract.ts +763 -22
  234. package/src/session.ts +70 -54
  235. package/src/source-files.ts +1 -1
  236. package/src/suppress-file.ts +24 -33
  237. package/src/suppress-inline.ts +2 -2
  238. package/src/suppress.ts +43 -58
  239. package/src/sweep.ts +23 -9
  240. package/src/symbol-keys.ts +2 -5
  241. package/src/template-field.ts +15 -25
  242. package/src/test-support.ts +17 -13
  243. package/src/text/compare.ts +27 -0
  244. package/src/text/glob.ts +210 -0
  245. package/src/text/named-files.inputs.json +1522 -0
  246. package/src/text/named-files.ts +68 -0
  247. package/src/text/shell.ts +161 -0
  248. package/src/text/yaml.ts +423 -0
  249. package/src/type-errors.ts +18 -288
  250. package/src/unseen-loads.ts +134 -0
  251. package/src/unwrap.ts +34 -0
  252. package/src/usage.ts +10 -0
  253. package/src/verbs/analyze.ts +224 -162
  254. package/src/verbs/describe.ts +14 -15
  255. package/src/verbs/explain.ts +10 -11
  256. package/src/verbs/print-config.ts +2 -1
  257. package/src/verbs/print-projects.ts +6 -4
  258. package/src/verbs/print-retained.ts +4 -2
  259. package/src/verbs/print-roots.ts +4 -2
  260. package/src/verbs/verb.ts +2 -8
  261. package/src/verbs/version.ts +15 -3
  262. package/src/workflow-steps.ts +74 -64
  263. package/dist/src/base-url.js +0 -18
  264. package/dist/src/discover.js +0 -468
  265. package/dist/src/glob.js +0 -65
  266. package/dist/src/pnpm-workspace.js +0 -120
  267. package/dist/src/setup-failure.js +0 -23
  268. package/dist/src/test-joins.js +0 -101
  269. package/dist/src/version.js +0 -7
  270. package/src/base-url.ts +0 -24
  271. package/src/discover.ts +0 -653
  272. package/src/glob.ts +0 -67
  273. package/src/pnpm-workspace.ts +0 -135
  274. package/src/setup-failure.ts +0 -39
  275. package/src/test-joins.ts +0 -120
  276. package/src/version.ts +0 -7
@@ -7,22 +7,34 @@
7
7
  */
8
8
  import { ModuleKind, ModuleResolutionKind } from "@typescript/native/sync";
9
9
  import { isStringLiteralLikeNode } from "@typescript/native/ast";
10
- import { baseUrlPaths } from "./base-url.js";
11
- import { dirnamePath, resolvePath } from "./paths.js";
12
- /** The most questions one round trip carries. */
13
- const BATCH = 2048;
10
+ import { JAVASCRIPT_FILE } from "../javascript-file.js";
11
+ import { isRecord } from "../json-document.js";
12
+ import { dirnamePath, resolvePath } from "../paths.js";
13
+ import { batched } from "../session.js";
14
+ import { bytewise } from "../text/compare.js";
15
+ import { PRODUCT } from "../product.js";
14
16
  /**
15
- * The options a configuration's program is built with: its own, less the ones that
16
- * lay out an emitted program's inputs and outputs, and with `paths` read against the
17
- * effective `baseUrl` as {@link baseUrlPaths} states. The analysis emits nothing, and a
18
- * member's source is outside the importer's root directory by construction, which
19
- * the compiler refuses under such a layout.
17
+ * The options a configuration's program is built with: the plan's, less the ones that lay
18
+ * out an emitted program's inputs and outputs. The analysis emits nothing, and a member's
19
+ * source is outside the importer's root directory by construction, which the compiler
20
+ * refuses under such a layout.
20
21
  */
21
- function analysisOptions(parsed) {
22
- const { composite, rootDir: _rootDir, outDir: _outDir, declarationDir: _declarationDir, tsBuildInfoFile: _tsBuildInfoFile, ...kept } = parsed.options;
23
- const paths = baseUrlPaths(parsed.options);
24
- const options = paths === undefined ? kept : { ...kept, paths };
25
- return composite === true ? { ...options, declaration: true } : options;
22
+ function analysisOptions(options, readsJavaScript) {
23
+ const { composite, rootDir: _rootDir, outDir: _outDir, declarationDir: _declarationDir, tsBuildInfoFile: _tsBuildInfoFile, ...kept } = options;
24
+ const read = readsJavaScript ? { ...kept, allowJs: true } : kept;
25
+ return composite === true ? { ...read, declaration: true } : read;
26
+ }
27
+ /**
28
+ * Whether one reading answers a member import with a JavaScript file, which its program reads
29
+ * as source only where it allows JavaScript, a program formed for a JavaScript member being the
30
+ * member's source.
31
+ */
32
+ function readsJavaScript(reading) {
33
+ return (reading.options.allowJs !== true &&
34
+ reading.entries.some((entry) => {
35
+ const file = entry.result.resolvedFileName;
36
+ return typeof file === "string" && JAVASCRIPT_FILE.test(file);
37
+ }));
26
38
  }
27
39
  /** The conditions an `exports` lookup under one configuration and one resolution mode reads. */
28
40
  function conditionsOf(parsed, mode) {
@@ -51,33 +63,45 @@ function unstatable(reading) {
51
63
  }
52
64
  return undefined;
53
65
  }
54
- /** The path beside `configFile` its written configuration takes, which no file has. */
55
- export function writtenPath(host, configFile) {
66
+ /** The path beside `configFile` its written configuration takes, which no file and none of `taken` has. */
67
+ export function writtenPath(host, configFile, taken = new Set()) {
56
68
  const stem = configFile.replace(/\.json$/u, "");
57
69
  for (let counter = 0;; counter += 1) {
58
- const path = `${stem}.deadset-ts${counter === 0 ? "" : `-${String(counter)}`}.json`;
59
- if (host.kindOf(path) === "absent") {
70
+ const path = `${stem}.${PRODUCT}${counter === 0 ? "" : `-${String(counter)}`}.json`;
71
+ if (host.kindOf(path) === "absent" && !taken.has(path)) {
60
72
  return path;
61
73
  }
62
74
  }
63
75
  }
64
76
  /**
65
- * The configuration that reads as one configuration's program: it extends it, lays out
66
- * no output for the reason {@link analysisOptions} gives, maps each answered specifier
67
- * to its file, and carries the references the program does. The compiler's default
68
- * `exclude` names the output directories this drops, so where the file list moves it is
69
- * restated; a list that still differs, or answers `paths` cannot state, are a reason.
77
+ * The path the snapshot holds one configuration's written form at: a formed configuration's
78
+ * own path, else a free one beside it that no configuration of `formed` holds, because a formed
79
+ * program in a directory with a `jsconfig.json` takes the name that file's written form would.
80
+ */
81
+ export function writtenFileOf(host, configuration, formed) {
82
+ return configuration.written === undefined
83
+ ? writtenPath(host, configuration.configFile, formed)
84
+ : configuration.configFile;
85
+ }
86
+ /**
87
+ * The configuration that reads as one configuration's program: it extends it, lays out no output
88
+ * ({@link analysisOptions}), maps each answered specifier to its file and keeps its references.
89
+ * Where the file list moves, the default `exclude` the dropped output directories are named in is
90
+ * restated, and one that {@link readsJavaScript} lists its files outright, as `allowJs` widens its
91
+ * `include` to JavaScript; a list that still differs, or answers `paths` cannot state, are a
92
+ * reason. A formed configuration, which no file holds to extend, is its own value with `paths`.
70
93
  */
71
- function configurationOf(engine, host, reading) {
94
+ function configurationOf(engine, host, reading, formed) {
72
95
  const reason = unstatable(reading);
73
96
  if (reason !== undefined) {
74
97
  return { kind: "unread", reason };
75
98
  }
76
- const { options, projectReferences: references = [] } = reading.parsed;
99
+ const { options } = reading;
100
+ const references = reading.parsed.projectReferences ?? [];
77
101
  const raw = options["pathsBasePath"];
78
102
  const base = typeof raw === "string" ? raw : dirnamePath(reading.configFile);
79
103
  const paths = {};
80
- for (const [key, targets] of Object.entries(baseUrlPaths(options) ?? options.paths ?? {})) {
104
+ for (const [key, targets] of Object.entries(options.paths ?? {})) {
81
105
  paths[key] = targets.map((target) => resolvePath(base, target));
82
106
  }
83
107
  for (const entry of reading.entries) {
@@ -86,6 +110,17 @@ function configurationOf(engine, host, reading) {
86
110
  paths[entry.moduleName] = [file];
87
111
  }
88
112
  }
113
+ if (reading.written !== undefined) {
114
+ const held = reading.written["compilerOptions"];
115
+ return {
116
+ kind: "configuration",
117
+ value: {
118
+ ...reading.written,
119
+ compilerOptions: { ...(isRecord(held) ? held : {}), paths },
120
+ },
121
+ };
122
+ }
123
+ const javascript = readsJavaScript(reading);
89
124
  const json = (exclude) => ({
90
125
  extends: reading.configFile,
91
126
  compilerOptions: {
@@ -94,18 +129,20 @@ function configurationOf(engine, host, reading) {
94
129
  declarationDir: null,
95
130
  tsBuildInfoFile: null,
96
131
  ...(options.composite === true ? { composite: false, declaration: true } : {}),
132
+ ...(javascript ? { allowJs: true } : {}),
97
133
  paths,
98
134
  },
99
135
  ...(references.length > 0
100
136
  ? { references: references.map((reference) => ({ path: reference.path })) }
101
137
  : {}),
138
+ ...(javascript ? { files: [...reading.parsed.fileNames], include: [] } : {}),
102
139
  ...(exclude === undefined ? {} : { exclude }),
103
140
  });
104
- const file = writtenPath(host, reading.configFile);
105
- const expected = [...reading.parsed.fileNames].sort().join("\u0000");
141
+ const file = writtenFileOf(host, reading, formed);
142
+ const expected = [...reading.parsed.fileNames].sort(bytewise).join("\u0000");
106
143
  const listed = (value) => {
107
144
  try {
108
- return [...engine.parseConfigJson(value, file).fileNames].sort().join("\u0000");
145
+ return [...engine.parseConfigJson(value, file).fileNames].sort(bytewise).join("\u0000");
109
146
  }
110
147
  catch {
111
148
  return undefined;
@@ -124,11 +161,11 @@ function configurationOf(engine, host, reading) {
124
161
  };
125
162
  }
126
163
  }
127
- return { kind: "configuration", file, text: `${JSON.stringify(value, null, 2)}\n` };
164
+ return { kind: "configuration", value };
128
165
  }
129
166
  /** The program one configuration is built as, under the answers it holds so far. */
130
167
  function programOf(engine, reading) {
131
- const compilerOptions = analysisOptions(reading.parsed);
168
+ const compilerOptions = analysisOptions(reading.options, readsJavaScript(reading));
132
169
  const references = reading.parsed.projectReferences ?? [];
133
170
  const resolver = engine.createModuleResolver(compilerOptions, reading.entries);
134
171
  return {
@@ -144,17 +181,6 @@ function programOf(engine, reading) {
144
181
  },
145
182
  };
146
183
  }
147
- function chunks(items) {
148
- const found = [];
149
- for (let at = 0; at < items.length; at += BATCH) {
150
- found.push(items.slice(at, at + BATCH));
151
- }
152
- return found;
153
- }
154
- /** Asks one question of every item, in round trips of at most {@link BATCH}. */
155
- function batched(engine, items, question) {
156
- return chunks(items).flatMap((run) => engine.batch(run.map(question)));
157
- }
158
184
  /** One round over one program: answers the member imports no earlier round asked about. */
159
185
  function scan(engine, program, reading, resolver) {
160
186
  const names = program.getSourceFileNames().filter((name) => !name.includes("/node_modules/"));
@@ -215,6 +241,13 @@ function scan(engine, program, reading, resolver) {
215
241
  });
216
242
  return added;
217
243
  }
244
+ function configurationText(value) {
245
+ return `${JSON.stringify(value, null, 2)}\n`;
246
+ }
247
+ /** One configuration value as the snapshot holds it, at `file`, which no file on disk holds. */
248
+ export function writtenConfiguration(file, value) {
249
+ return { kind: "configuration", file, text: configurationText(value) };
250
+ }
218
251
  /**
219
252
  * Answers the member imports of every reading `build` describes, round by round, all
220
253
  * the readings still adding answers built in one snapshot per round. A reading `build`
@@ -256,35 +289,25 @@ function readRounds(engine, readings, resolver, build) {
256
289
  * the rounds go on over each reading's configuration, which reads them, for the imports
257
290
  * only they make.
258
291
  */
259
- export function workspacePrograms(engine, host, configFiles, resolver) {
260
- const readings = configFiles.flatMap((configFile) => {
261
- let parsed;
262
- try {
263
- parsed = engine.parseConfigFile(configFile);
264
- }
265
- catch {
266
- // A configuration that does not read is opened as a project, which states why.
267
- return [];
268
- }
269
- return [
270
- {
271
- configFile,
272
- parsed,
273
- entries: [],
274
- seen: new Set(),
275
- bound: new Map(),
276
- unresolved: [],
277
- },
278
- ];
279
- });
292
+ export function workspacePrograms(engine, host, configurations, resolver) {
293
+ const readings = configurations.map((configuration) => ({
294
+ ...configuration,
295
+ entries: [],
296
+ seen: new Set(),
297
+ bound: new Map(),
298
+ unresolved: [],
299
+ }));
300
+ const formed = new Set(configurations.filter((one) => one.written !== undefined).map((one) => one.configFile));
280
301
  readRounds(engine, readings, resolver, (reading) => ({
281
302
  kind: "program",
282
303
  ...programOf(engine, reading),
283
304
  }));
284
305
  if (engine.readsComponents) {
285
306
  readRounds(engine, readings, resolver, (reading) => {
286
- const one = configurationOf(engine, host, reading);
287
- return one.kind === "configuration" ? one : undefined;
307
+ const one = configurationOf(engine, host, reading, formed);
308
+ return one.kind === "configuration"
309
+ ? writtenConfiguration(writtenFileOf(host, reading, formed), one.value)
310
+ : undefined;
288
311
  });
289
312
  }
290
313
  const programs = new Map();
@@ -293,7 +316,7 @@ export function workspacePrograms(engine, host, configFiles, resolver) {
293
316
  if (reading.entries.length === 0) {
294
317
  continue;
295
318
  }
296
- const one = engine.readsComponents ? configurationOf(engine, host, reading) : undefined;
319
+ const one = engine.readsComponents ? configurationOf(engine, host, reading, formed) : undefined;
297
320
  if (one !== undefined && one.kind !== "unread") {
298
321
  programs.set(reading.configFile, one);
299
322
  continue;
@@ -6,38 +6,17 @@
6
6
  * the package neither compiles nor writes is read as installed, and the rest is unresolved.
7
7
  */
8
8
  import { emittedFrom } from "./emit-map.js";
9
- import { gitIgnored } from "./git-ignore.js";
10
- import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
11
- import { satisfies } from "./version-range.js";
9
+ import { packageOfSpecifier } from "../dependencies.js";
10
+ import { gitIgnored } from "../git-ignore.js";
11
+ import { ancestors, dirnamePath, joinPath, relativePath, resolvePath } from "../paths.js";
12
+ import { satisfies } from "../version-range.js";
13
+ import { isRecord } from "../json-document.js";
14
+ import { MANIFEST } from "../fs/manifest.js";
15
+ import { dependencySpecifiers, installedPackage, manifestsAbove, } from "./module-lookup.js";
12
16
  /** The manifest members that name an entry point for the subpath `.` without `exports`. */
13
17
  const LEGACY_MEMBERS = ["types", "typings", "module", "main"];
14
- /** The dependency members a manifest declares other packages under. */
15
- const DEPENDENCY_MEMBERS = [
16
- "dependencies",
17
- "devDependencies",
18
- "peerDependencies",
19
- "optionalDependencies",
20
- ];
21
18
  const SOURCE = /(?<!\.d)\.(?:[mc]?ts|tsx)$/u;
22
19
  const OUTPUT = /(?:\.d\.[mc]?ts|\.[mc]?js|\.jsx)$/u;
23
- function isRecord(value) {
24
- return typeof value === "object" && value !== null && !Array.isArray(value);
25
- }
26
- /** The package name a bare specifier names, or `undefined` for any other specifier. */
27
- export function packageNameOf(specifier) {
28
- if (specifier === "" ||
29
- specifier.startsWith(".") ||
30
- specifier.startsWith("#") ||
31
- isAbsolutePath(specifier) ||
32
- specifier.includes(":")) {
33
- return undefined;
34
- }
35
- const [first, second] = specifier.split("/");
36
- if (specifier.startsWith("@")) {
37
- return second === undefined || second === "" ? undefined : `${first ?? ""}/${second}`;
38
- }
39
- return first;
40
- }
41
20
  function inside(dir, path) {
42
21
  return relativePath(dir, path) !== undefined;
43
22
  }
@@ -140,49 +119,32 @@ function dependsOn(specifier, member, fromDir) {
140
119
  /** The member imports of one workspace, read from the members' sources. */
141
120
  export function workspaceResolver(host, workspace) {
142
121
  const links = new Map();
143
- /** The directory `node_modules/<name>` names from one directory, links followed. */
122
+ /** The directory the package `name` installed for one directory is, links followed. */
144
123
  const linkedFrom = (name, dir) => {
145
124
  const key = `${name}\u0000${dir}`;
146
- if (links.has(key)) {
147
- return links.get(key);
148
- }
149
- const candidate = joinPath(dir, "node_modules", name);
150
- let found;
151
- if (host.kindOf(candidate) === "absent") {
152
- const up = dirnamePath(dir);
153
- found = up === dir ? undefined : linkedFrom(name, up);
154
- }
155
- else {
125
+ if (!links.has(key)) {
126
+ const installed = installedPackage(host, dir, name);
127
+ let found;
156
128
  try {
157
- found = host.realPath(candidate);
129
+ found = installed === undefined ? undefined : host.realPath(installed);
158
130
  }
159
131
  catch {
160
132
  found = undefined;
161
133
  }
134
+ links.set(key, found);
162
135
  }
163
- links.set(key, found);
164
- return found;
136
+ return links.get(key);
165
137
  };
166
138
  const root = workspace.members[0]?.dir;
167
139
  const ignored = root === undefined ? () => false : gitIgnored(host, root);
168
140
  const manifests = new Map();
169
141
  /** The nearest manifest at or above one directory, up to the workspace's root. */
170
142
  const nearestManifest = (dir) => {
171
- if (manifests.has(dir)) {
172
- return manifests.get(dir);
173
- }
174
- let found;
175
- try {
176
- const value = JSON.parse(host.readFile(joinPath(dir, "package.json")));
177
- found = isRecord(value) ? { dir, manifest: value } : undefined;
178
- }
179
- catch {
180
- const up = dirnamePath(dir);
181
- found =
182
- up === dir || root === undefined || !inside(root, up) ? undefined : nearestManifest(up);
143
+ if (!manifests.has(dir)) {
144
+ const [nearest] = manifestsAbove(host, dir, root ?? dir);
145
+ manifests.set(dir, nearest);
183
146
  }
184
- manifests.set(dir, found);
185
- return found;
147
+ return manifests.get(dir);
186
148
  };
187
149
  const linkOf = (candidates, fromDir) => {
188
150
  const name = candidates[0]?.name ?? "";
@@ -191,18 +153,14 @@ export function workspaceResolver(host, workspace) {
191
153
  const member = candidates.find((one) => one.dir === linked);
192
154
  return member === undefined ? { kind: "none" } : { kind: "member", member };
193
155
  }
194
- const scope = workspace.memberOf(joinPath(fromDir, "package.json"));
156
+ const scope = workspace.memberOf(joinPath(fromDir, MANIFEST));
195
157
  if (scope?.name === name) {
196
158
  return { kind: "member", member: scope };
197
159
  }
198
160
  // A package below the root that no workspace lists depends on a member as a member does.
199
161
  const owner = scope ?? nearestManifest(fromDir);
200
162
  const manifest = owner?.manifest;
201
- const declared = DEPENDENCY_MEMBERS.flatMap((field) => {
202
- const held = manifest?.[field];
203
- const specifier = isRecord(held) ? held[name] : undefined;
204
- return typeof specifier === "string" ? [specifier] : [];
205
- });
163
+ const declared = (manifest === undefined ? [] : dependencySpecifiers(manifest, name)).filter((specifier) => typeof specifier === "string");
206
164
  const matched = candidates.filter((member) => declared.some((specifier) => dependsOn(specifier, member, owner?.dir ?? fromDir)));
207
165
  const [one, two] = matched;
208
166
  if (one === undefined) {
@@ -221,7 +179,7 @@ export function workspaceResolver(host, workspace) {
221
179
  * directory however its configuration writes it.
222
180
  */
223
181
  const mirrored = (member, path) => {
224
- for (let dir = dirnamePath(path); inside(member.dir, dir); dir = dirnamePath(dir)) {
182
+ for (const dir of ancestors(dirnamePath(path), member.dir).filter((at) => at !== member.dir)) {
225
183
  const found = emittedFrom(path, { outDir: dir, sourceDirs: [member.dir] }).find((candidate) => SOURCE.test(candidate) && !ignored(candidate) && host.kindOf(candidate) === "file");
226
184
  if (found !== undefined) {
227
185
  return found;
@@ -288,11 +246,11 @@ export function workspaceResolver(host, workspace) {
288
246
  workspace,
289
247
  sourceOf,
290
248
  names: (specifier) => {
291
- const name = packageNameOf(specifier);
249
+ const name = packageOfSpecifier(specifier);
292
250
  return name !== undefined && workspace.named.has(name);
293
251
  },
294
252
  resolve(specifier, fromDir, conditions, answered) {
295
- const name = packageNameOf(specifier);
253
+ const name = packageOfSpecifier(specifier);
296
254
  const candidates = name === undefined ? undefined : workspace.named.get(name);
297
255
  if (name === undefined || candidates === undefined) {
298
256
  return { kind: "default" };
@@ -4,12 +4,13 @@
4
4
  * the files each configuration holds only for their references, and the entry points
5
5
  * the members' manifests name. A target in no workspace reads none of it.
6
6
  */
7
- import { relativePath } from "./paths.js";
8
- import { findWorkspace, referenceOnlyIn } from "./workspace.js";
7
+ import { relativePath } from "../paths.js";
8
+ import { referenceOnlyIn } from "./workspace.js";
9
9
  import { memberEntries, targetManifestIn } from "./workspace-manifests.js";
10
10
  import { workspacePrograms, } from "./workspace-programs.js";
11
11
  import { workspaceResolver } from "./workspace-resolution.js";
12
- function layoutOf(engine, configFile) {
12
+ /** What one member's configuration file says of its layout, or `undefined` where it does not parse. */
13
+ export function layoutOf(engine, configFile) {
13
14
  try {
14
15
  const parsed = engine.parseConfigFile(configFile);
15
16
  return {
@@ -42,16 +43,16 @@ function unbuildableReason(reading, referenceOnly, targetRoot) {
42
43
  * The workspace the target at `targetRoot` sits in, read for the configurations named,
43
44
  * or `undefined` where the target sits in none.
44
45
  */
45
- export function workspaceRun(engine, host, targetRoot, configFiles, manifest) {
46
- const workspace = findWorkspace(host, targetRoot, (configFile) => layoutOf(engine, configFile));
46
+ export function workspaceRun(engine, host, targetRoot, configurations, manifest, workspace) {
47
47
  if (workspace === undefined) {
48
48
  return undefined;
49
49
  }
50
50
  const resolver = workspaceResolver(host, workspace);
51
- const read = workspacePrograms(engine, host, configFiles, resolver);
51
+ const read = workspacePrograms(engine, host, configurations, resolver);
52
52
  const readings = new Map(read.readings.map((reading) => [reading.configFile, reading]));
53
53
  const referenceOnly = (configFile, compiled = []) => referenceOnlyIn(workspace, new Set([...(readings.get(configFile)?.fileNames ?? []), ...compiled]));
54
54
  return {
55
+ workspace,
55
56
  programs: read.programs,
56
57
  componentsUnread: read.readings.flatMap(({ configFile, componentsUnread }) => componentsUnread === undefined ? [] : [{ configFile, reason: componentsUnread }]),
57
58
  unbuildable: (configFile) => {
@@ -66,7 +67,6 @@ export function workspaceRun(engine, host, targetRoot, configFiles, manifest) {
66
67
  : !fromExternalLibrary,
67
68
  manifest: targetManifestIn(resolver, targetRoot, manifest),
68
69
  entries: memberEntries(host, resolver, targetRoot),
69
- memberDirs: workspace.members.map((member) => member.dir),
70
70
  };
71
71
  }
72
72
  /** The errors one configuration answers for: none in a file it holds only for its references. */