@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
@@ -7,18 +7,34 @@
7
7
  */
8
8
  import { ModuleKind, ModuleResolutionKind } from "@typescript/native/sync";
9
9
  import { isStringLiteralLikeNode } from "@typescript/native/ast";
10
- import { dirnamePath, resolvePath } from "./paths.js";
11
- /** The most questions one round trip carries. */
12
- 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";
13
16
  /**
14
- * The options a configuration's program is built with: its own, less the ones that
15
- * lay out an emitted program's inputs and outputs. The analysis emits nothing, and a
16
- * member's source is outside the importer's root directory by construction, which
17
- * 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.
18
21
  */
19
- function analysisOptions(parsed) {
20
- const { composite, rootDir: _rootDir, outDir: _outDir, declarationDir: _declarationDir, tsBuildInfoFile: _tsBuildInfoFile, ...kept } = parsed.options;
21
- return composite === true ? { ...kept, declaration: true } : kept;
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
+ }));
22
38
  }
23
39
  /** The conditions an `exports` lookup under one configuration and one resolution mode reads. */
24
40
  function conditionsOf(parsed, mode) {
@@ -47,29 +63,41 @@ function unstatable(reading) {
47
63
  }
48
64
  return undefined;
49
65
  }
50
- /** The path beside `configFile` its written configuration takes, which no file has. */
51
- 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()) {
52
68
  const stem = configFile.replace(/\.json$/u, "");
53
69
  for (let counter = 0;; counter += 1) {
54
- const path = `${stem}.deadset-ts${counter === 0 ? "" : `-${String(counter)}`}.json`;
55
- if (host.kindOf(path) === "absent") {
70
+ const path = `${stem}.${PRODUCT}${counter === 0 ? "" : `-${String(counter)}`}.json`;
71
+ if (host.kindOf(path) === "absent" && !taken.has(path)) {
56
72
  return path;
57
73
  }
58
74
  }
59
75
  }
60
76
  /**
61
- * The configuration that reads as one configuration's program: it extends it, lays out
62
- * no output for the reason {@link analysisOptions} gives, maps each answered specifier
63
- * to its file, and carries the references the program does. The compiler's default
64
- * `exclude` names the output directories this drops, so where the file list moves it is
65
- * 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`.
66
93
  */
67
- function configurationOf(engine, host, reading) {
94
+ function configurationOf(engine, host, reading, formed) {
68
95
  const reason = unstatable(reading);
69
96
  if (reason !== undefined) {
70
97
  return { kind: "unread", reason };
71
98
  }
72
- const { options, projectReferences: references = [] } = reading.parsed;
99
+ const { options } = reading;
100
+ const references = reading.parsed.projectReferences ?? [];
73
101
  const raw = options["pathsBasePath"];
74
102
  const base = typeof raw === "string" ? raw : dirnamePath(reading.configFile);
75
103
  const paths = {};
@@ -82,6 +110,17 @@ function configurationOf(engine, host, reading) {
82
110
  paths[entry.moduleName] = [file];
83
111
  }
84
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);
85
124
  const json = (exclude) => ({
86
125
  extends: reading.configFile,
87
126
  compilerOptions: {
@@ -90,18 +129,20 @@ function configurationOf(engine, host, reading) {
90
129
  declarationDir: null,
91
130
  tsBuildInfoFile: null,
92
131
  ...(options.composite === true ? { composite: false, declaration: true } : {}),
132
+ ...(javascript ? { allowJs: true } : {}),
93
133
  paths,
94
134
  },
95
135
  ...(references.length > 0
96
136
  ? { references: references.map((reference) => ({ path: reference.path })) }
97
137
  : {}),
138
+ ...(javascript ? { files: [...reading.parsed.fileNames], include: [] } : {}),
98
139
  ...(exclude === undefined ? {} : { exclude }),
99
140
  });
100
- const file = writtenPath(host, reading.configFile);
101
- 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");
102
143
  const listed = (value) => {
103
144
  try {
104
- return [...engine.parseConfigJson(value, file).fileNames].sort().join("\u0000");
145
+ return [...engine.parseConfigJson(value, file).fileNames].sort(bytewise).join("\u0000");
105
146
  }
106
147
  catch {
107
148
  return undefined;
@@ -120,11 +161,11 @@ function configurationOf(engine, host, reading) {
120
161
  };
121
162
  }
122
163
  }
123
- return { kind: "configuration", file, text: `${JSON.stringify(value, null, 2)}\n` };
164
+ return { kind: "configuration", value };
124
165
  }
125
166
  /** The program one configuration is built as, under the answers it holds so far. */
126
167
  function programOf(engine, reading) {
127
- const compilerOptions = analysisOptions(reading.parsed);
168
+ const compilerOptions = analysisOptions(reading.options, readsJavaScript(reading));
128
169
  const references = reading.parsed.projectReferences ?? [];
129
170
  const resolver = engine.createModuleResolver(compilerOptions, reading.entries);
130
171
  return {
@@ -140,17 +181,6 @@ function programOf(engine, reading) {
140
181
  },
141
182
  };
142
183
  }
143
- function chunks(items) {
144
- const found = [];
145
- for (let at = 0; at < items.length; at += BATCH) {
146
- found.push(items.slice(at, at + BATCH));
147
- }
148
- return found;
149
- }
150
- /** Asks one question of every item, in round trips of at most {@link BATCH}. */
151
- function batched(engine, items, question) {
152
- return chunks(items).flatMap((run) => engine.batch(run.map(question)));
153
- }
154
184
  /** One round over one program: answers the member imports no earlier round asked about. */
155
185
  function scan(engine, program, reading, resolver) {
156
186
  const names = program.getSourceFileNames().filter((name) => !name.includes("/node_modules/"));
@@ -211,6 +241,13 @@ function scan(engine, program, reading, resolver) {
211
241
  });
212
242
  return added;
213
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
+ }
214
251
  /**
215
252
  * Answers the member imports of every reading `build` describes, round by round, all
216
253
  * the readings still adding answers built in one snapshot per round. A reading `build`
@@ -252,35 +289,25 @@ function readRounds(engine, readings, resolver, build) {
252
289
  * the rounds go on over each reading's configuration, which reads them, for the imports
253
290
  * only they make.
254
291
  */
255
- export function workspacePrograms(engine, host, configFiles, resolver) {
256
- const readings = configFiles.flatMap((configFile) => {
257
- let parsed;
258
- try {
259
- parsed = engine.parseConfigFile(configFile);
260
- }
261
- catch {
262
- // A configuration that does not read is opened as a project, which states why.
263
- return [];
264
- }
265
- return [
266
- {
267
- configFile,
268
- parsed,
269
- entries: [],
270
- seen: new Set(),
271
- bound: new Map(),
272
- unresolved: [],
273
- },
274
- ];
275
- });
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));
276
301
  readRounds(engine, readings, resolver, (reading) => ({
277
302
  kind: "program",
278
303
  ...programOf(engine, reading),
279
304
  }));
280
305
  if (engine.readsComponents) {
281
306
  readRounds(engine, readings, resolver, (reading) => {
282
- const one = configurationOf(engine, host, reading);
283
- 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;
284
311
  });
285
312
  }
286
313
  const programs = new Map();
@@ -289,7 +316,7 @@ export function workspacePrograms(engine, host, configFiles, resolver) {
289
316
  if (reading.entries.length === 0) {
290
317
  continue;
291
318
  }
292
- const one = engine.readsComponents ? configurationOf(engine, host, reading) : undefined;
319
+ const one = engine.readsComponents ? configurationOf(engine, host, reading, formed) : undefined;
293
320
  if (one !== undefined && one.kind !== "unread") {
294
321
  programs.set(reading.configFile, one);
295
322
  continue;
@@ -1,43 +1,22 @@
1
1
  /**
2
- * Which file an import of a workspace package's name is read from. The package is the
3
- * one `node_modules` links the importer to or, with no link, the one the importer's
4
- * manifest depends on. A source file the compiler reaches stands; any other file is read
5
- * back to the source it is compiled from or to a manifest target that is source, a file
6
- * the package's configurations neither compile nor write is read as installed, and an
7
- * import nothing answers for is unresolved.
2
+ * Which file an import of a workspace package's name is read from: the package
3
+ * `node_modules` links the importer to or, with no link, the one its manifest depends on.
4
+ * A source file the compiler reaches stands; any other file reads back to its source or to
5
+ * a manifest target that is source, an ignored file to the source its path mirrors, a file
6
+ * the package neither compiles nor writes is read as installed, and the rest is unresolved.
8
7
  */
9
8
  import { emittedFrom } from "./emit-map.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,28 +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);
135
+ }
136
+ return links.get(key);
137
+ };
138
+ const root = workspace.members[0]?.dir;
139
+ const ignored = root === undefined ? () => false : gitIgnored(host, root);
140
+ const manifests = new Map();
141
+ /** The nearest manifest at or above one directory, up to the workspace's root. */
142
+ const nearestManifest = (dir) => {
143
+ if (!manifests.has(dir)) {
144
+ const [nearest] = manifestsAbove(host, dir, root ?? dir);
145
+ manifests.set(dir, nearest);
162
146
  }
163
- links.set(key, found);
164
- return found;
147
+ return manifests.get(dir);
165
148
  };
166
149
  const linkOf = (candidates, fromDir) => {
167
150
  const name = candidates[0]?.name ?? "";
@@ -170,17 +153,15 @@ export function workspaceResolver(host, workspace) {
170
153
  const member = candidates.find((one) => one.dir === linked);
171
154
  return member === undefined ? { kind: "none" } : { kind: "member", member };
172
155
  }
173
- const scope = workspace.memberOf(joinPath(fromDir, "package.json"));
156
+ const scope = workspace.memberOf(joinPath(fromDir, MANIFEST));
174
157
  if (scope?.name === name) {
175
158
  return { kind: "member", member: scope };
176
159
  }
177
- const manifest = scope?.manifest;
178
- const declared = DEPENDENCY_MEMBERS.flatMap((field) => {
179
- const held = manifest?.[field];
180
- const specifier = isRecord(held) ? held[name] : undefined;
181
- return typeof specifier === "string" ? [specifier] : [];
182
- });
183
- const matched = candidates.filter((member) => declared.some((specifier) => dependsOn(specifier, member, scope?.dir ?? fromDir)));
160
+ // A package below the root that no workspace lists depends on a member as a member does.
161
+ const owner = scope ?? nearestManifest(fromDir);
162
+ const manifest = owner?.manifest;
163
+ const declared = (manifest === undefined ? [] : dependencySpecifiers(manifest, name)).filter((specifier) => typeof specifier === "string");
164
+ const matched = candidates.filter((member) => declared.some((specifier) => dependsOn(specifier, member, owner?.dir ?? fromDir)));
184
165
  const [one, two] = matched;
185
166
  if (one === undefined) {
186
167
  return { kind: "none" };
@@ -191,6 +172,21 @@ export function workspaceResolver(host, workspace) {
191
172
  };
192
173
  /** The files a member's configurations compile, which is what makes a file there source. */
193
174
  const compiled = (member, path) => member.configurations.some((configuration) => configuration.fileNames.has(path));
175
+ /**
176
+ * The member's source an ignored output path mirrors: the path below one of its
177
+ * directories, the deepest first, read below the member's own directory as an output
178
+ * directory's tree mirrors its root directory's, which a build leaves in an ignored
179
+ * directory however its configuration writes it.
180
+ */
181
+ const mirrored = (member, path) => {
182
+ for (const dir of ancestors(dirnamePath(path), member.dir).filter((at) => at !== member.dir)) {
183
+ const found = emittedFrom(path, { outDir: dir, sourceDirs: [member.dir] }).find((candidate) => SOURCE.test(candidate) && !ignored(candidate) && host.kindOf(candidate) === "file");
184
+ if (found !== undefined) {
185
+ return found;
186
+ }
187
+ }
188
+ return undefined;
189
+ };
194
190
  /**
195
191
  * The member's source one of its paths is, or is compiled from: a TypeScript source
196
192
  * file, the source an output path maps back to, or a file one of the member's
@@ -212,6 +208,9 @@ export function workspaceResolver(host, workspace) {
212
208
  if (found !== undefined) {
213
209
  return found;
214
210
  }
211
+ if (ignored(path)) {
212
+ return mirrored(member, path);
213
+ }
215
214
  return compiled(member, path) && host.kindOf(path) === "file" ? path : undefined;
216
215
  };
217
216
  /** The targets a member's manifest names for one subpath, the selected one first. */
@@ -247,11 +246,11 @@ export function workspaceResolver(host, workspace) {
247
246
  workspace,
248
247
  sourceOf,
249
248
  names: (specifier) => {
250
- const name = packageNameOf(specifier);
249
+ const name = packageOfSpecifier(specifier);
251
250
  return name !== undefined && workspace.named.has(name);
252
251
  },
253
252
  resolve(specifier, fromDir, conditions, answered) {
254
- const name = packageNameOf(specifier);
253
+ const name = packageOfSpecifier(specifier);
255
254
  const candidates = name === undefined ? undefined : workspace.named.get(name);
256
255
  if (name === undefined || candidates === undefined) {
257
256
  return { kind: "default" };
@@ -270,9 +269,11 @@ export function workspaceResolver(host, workspace) {
270
269
  }
271
270
  const { member } = linked;
272
271
  // A member no compiler configuration builds is read as the package manager
273
- // installed it, wherever the compiler's resolution found it.
272
+ // installed it, wherever the compiler's resolution found it, unless what it found is
273
+ // a file the tree ignores, which no install wrote.
274
274
  if (answered !== undefined &&
275
- (member.configurations.length === 0 || !inside(member.dir, answered.resolvedFileName))) {
275
+ ((member.configurations.length === 0 && !ignored(answered.resolvedFileName)) ||
276
+ !inside(member.dir, answered.resolvedFileName))) {
276
277
  return { kind: "default" };
277
278
  }
278
279
  if (answered !== undefined) {
@@ -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. */