@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,140 @@
1
+ /**
2
+ * The programs a run forms for the JavaScript files no compiler configuration lists: one per
3
+ * package, over the files git would track that are no build output, each written as a
4
+ * configuration only the snapshot holds, with the module system of the runtime its package
5
+ * is written for.
6
+ */
7
+ import { CONVENTION_ROWS } from "../convention-rows.js";
8
+ import { holdsManifest, MANIFEST, readManifestAt } from "../fs/manifest.js";
9
+ import { filesBelow } from "../fs/tree.js";
10
+ import { JAVASCRIPT_FILE, minified, minifiedByName } from "../javascript-file.js";
11
+ import { isRecord } from "../json-document.js";
12
+ import { ancestors, dirnamePath, joinPath, relativePath } from "../paths.js";
13
+ import { bytewise } from "../text/compare.js";
14
+ import { writtenPath } from "./workspace-programs.js";
15
+ /** The name a package's program is written beside, made free by {@link writtenPath}. */
16
+ const GROUP_CONFIGURATION = "jsconfig.json";
17
+ /**
18
+ * The options of a formed program before its runtime is chosen: unchecked JavaScript, no
19
+ * emit, no installed package's JavaScript, which is not the target's code and grows the
20
+ * program with the depth of `node_modules`, and every type package visible.
21
+ */
22
+ const FORMED_OPTIONS = {
23
+ allowJs: true,
24
+ checkJs: false,
25
+ noEmit: true,
26
+ skipLibCheck: true,
27
+ maxNodeModuleJsDepth: 0,
28
+ target: "esnext",
29
+ jsx: "preserve",
30
+ resolveJsonModule: true,
31
+ };
32
+ /** Whether one file is minified build output; one that cannot be read is left to the compiler. */
33
+ function minifiedFile(host, file) {
34
+ if (minifiedByName(file)) {
35
+ return true;
36
+ }
37
+ try {
38
+ return minified(file, host.readFile(file));
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ /**
45
+ * The JavaScript files below `root` a program may be formed over: none in a skipped
46
+ * directory, none git ignores, none minified, none `context` covers and none below one of
47
+ * its output directories. A convention row's generated directory leaves out more, once the
48
+ * rows are read: {@link formedGroups}.
49
+ */
50
+ export function candidateFiles(host, root, { covered, outputs, ignored }) {
51
+ return filesBelow(host, root, (name) => JAVASCRIPT_FILE.test(name))
52
+ .map((path) => joinPath(root, path))
53
+ .filter((file) => !covered.has(file) &&
54
+ !outputs.some((dir) => relativePath(dir, file) !== undefined) &&
55
+ !ignored(file) &&
56
+ !minifiedFile(host, file));
57
+ }
58
+ /**
59
+ * The candidates below `root` grouped by package, in directory order, less every file below a
60
+ * directory an applied row of `conventions` names as generated: each file belongs to the
61
+ * nearest directory at or above it, up to `root`, that holds a `package.json`, a manifest that
62
+ * does not parse included, and to `root` where none does.
63
+ */
64
+ export function formedGroups(host, root, candidates, conventions) {
65
+ const generated = conventions.generated.map(({ dir, below }) => joinPath(dir, below));
66
+ const groups = new Map();
67
+ for (const file of candidates) {
68
+ if (generated.some((dir) => relativePath(dir, file) !== undefined)) {
69
+ continue;
70
+ }
71
+ const dir = packageOf(host, root, file);
72
+ groups.set(dir, [...(groups.get(dir) ?? []), file]);
73
+ }
74
+ return [...groups]
75
+ .sort(([a], [b]) => bytewise(a, b))
76
+ .map(([dir, files]) => ({
77
+ dir,
78
+ manifest: readManifestAt(host, dir),
79
+ files: files.sort(bytewise),
80
+ }));
81
+ }
82
+ /** The directory whose `package.json` groups one file: the nearest at or above it, up to `root`. */
83
+ export function packageOf(host, root, file) {
84
+ return ancestors(dirnamePath(file), root).find((at) => holdsManifest(host, at)) ?? root;
85
+ }
86
+ const RUNTIME_OPTIONS = {
87
+ bundler: { module: "preserve", moduleResolution: "bundler" },
88
+ node: { module: "nodenext", moduleResolution: "nodenext" },
89
+ };
90
+ /** The manifest members that say a package is written for Node. */
91
+ const NODE_MEMBERS = ["main", "exports", "bin", "type"];
92
+ /** Whether one manifest says its package runs under Node, `engines.node` included; its scripts are not read. */
93
+ function writtenForNode(manifest) {
94
+ const { engines } = manifest;
95
+ return (NODE_MEMBERS.some((member) => manifest[member] !== undefined) ||
96
+ (isRecord(engines) && engines["node"] !== undefined));
97
+ }
98
+ /**
99
+ * The manifests, by path as `conventions` names them, an applied row of a tool that bundles
100
+ * applies through.
101
+ */
102
+ export function bundlingManifests(conventions) {
103
+ return new Set(conventions.applied
104
+ .filter((one) => CONVENTION_ROWS.some((row) => row.name === one.name && row.bundles === true))
105
+ .map((one) => one.manifest));
106
+ }
107
+ /**
108
+ * Each group's program, given the module system of the runtime its package is written for: a
109
+ * bundler's where a row of a tool that bundles applies through the group's manifest or the
110
+ * root's, else Node's where its manifest says the package runs under Node, under which a file's
111
+ * extension and its package's `type` decide its module format, else a bundler's, which reads a
112
+ * file with no module syntax as a classic script, as a browser loads it.
113
+ */
114
+ export function formedPrograms(engine, host, groups, { root, origin, bundling, idOf }) {
115
+ return groups.map((group) => {
116
+ const manifest = group.manifest.kind === "absent" ? undefined : joinPath(group.dir, MANIFEST);
117
+ const below = manifest === undefined ? undefined : relativePath(root, manifest);
118
+ const runtime = !bundling.has(MANIFEST) &&
119
+ (below === undefined || !bundling.has(below)) &&
120
+ group.manifest.kind === "read" &&
121
+ writtenForNode(group.manifest.manifest)
122
+ ? "node"
123
+ : "bundler";
124
+ const configFile = writtenPath(host, joinPath(group.dir, GROUP_CONFIGURATION));
125
+ const written = {
126
+ compilerOptions: { ...FORMED_OPTIONS, ...RUNTIME_OPTIONS[runtime] },
127
+ files: group.files,
128
+ };
129
+ return {
130
+ group,
131
+ configuration: {
132
+ id: idOf(manifest),
133
+ configFile,
134
+ origin,
135
+ parsed: engine.parseConfigJson(written, configFile),
136
+ written,
137
+ },
138
+ };
139
+ });
140
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * The globals a test runner injects into every file it runs, which no import names: a
3
+ * Vitest configuration whose `test.globals` is `true` gives each test and each file it
4
+ * loads `describe`, `it`, `expect` and `vi` (https://vitest.dev/config/globals). A program
5
+ * analyzed without the runner's declarations of them reads every use as an error, so a
6
+ * configuration under such a runner is read with them; and a file that calls one is
7
+ * written for the runner, as one that imports it is.
8
+ */
9
+ import { isAsExpression, isBinaryExpression, isCallExpression, isExportAssignment, isExpressionStatement, isIdentifier, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, SyntaxKind, } from "@typescript/native/ast";
10
+ import { isRecord } from "../json-document.js";
11
+ import { ancestors, dirnamePath, joinPath } from "../paths.js";
12
+ import { UNANSWERED } from "../query.js";
13
+ import { filesIn } from "../fs/tree.js";
14
+ import { holdsManifest, INSTALLED_PACKAGES } from "../fs/manifest.js";
15
+ import { isTargetConfiguration } from "./discover.js";
16
+ /** The test runner whose configuration injects the globals, and the `types` entry declaring them. */
17
+ export const GLOBALS_RUNNER = "vitest";
18
+ const GLOBALS_TYPES = "vitest/globals";
19
+ /** The configuration files the runner reads its settings from, by name. */
20
+ const RUNNER_CONFIG = /^(?:vitest|vite)\.config\.(?:js|mjs|cjs|ts|mts|cts)$/u;
21
+ /** The globals `vitest/globals` declares that a test or a helper calls or reads a member of. */
22
+ const GLOBAL_NAMES = new Set([
23
+ "afterAll",
24
+ "afterEach",
25
+ "assert",
26
+ "assertType",
27
+ "beforeAll",
28
+ "beforeEach",
29
+ "bench",
30
+ "describe",
31
+ "expect",
32
+ "expectTypeOf",
33
+ "it",
34
+ "onTestFailed",
35
+ "onTestFinished",
36
+ "suite",
37
+ "test",
38
+ "vi",
39
+ "vitest",
40
+ ]);
41
+ /** The expression one wrapper that changes no value holds, and a call's first argument. */
42
+ function configObject(node) {
43
+ let at = node;
44
+ while (at !== undefined) {
45
+ if (isParenthesizedExpression(at) || isAsExpression(at) || isSatisfiesExpression(at)) {
46
+ at = at.expression;
47
+ }
48
+ else if (isCallExpression(at)) {
49
+ at = at.arguments[0];
50
+ }
51
+ else {
52
+ return at;
53
+ }
54
+ }
55
+ return undefined;
56
+ }
57
+ /** The value one object literal writes for one member as `name: value`. */
58
+ function member(object, name) {
59
+ if (object === undefined || !isObjectLiteralExpression(object)) {
60
+ return undefined;
61
+ }
62
+ for (const property of object.properties) {
63
+ if (isPropertyAssignment(property) &&
64
+ isIdentifier(property.name) &&
65
+ property.name.text === name) {
66
+ return configObject(property.initializer);
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+ /** Whether one runner configuration's default export sets `test.globals` to `true`. */
72
+ function setsGlobals(file) {
73
+ for (const statement of file.statements) {
74
+ let exported;
75
+ if (isExportAssignment(statement)) {
76
+ exported = statement.expression;
77
+ }
78
+ else if (isExpressionStatement(statement) && isBinaryExpression(statement.expression)) {
79
+ const { left, operatorToken, right } = statement.expression;
80
+ if (operatorToken.kind === SyntaxKind.EqualsToken &&
81
+ isPropertyAccessExpression(left) &&
82
+ isIdentifier(left.expression) &&
83
+ left.expression.text === "module" &&
84
+ isIdentifier(left.name) &&
85
+ left.name.text === "exports") {
86
+ exported = right;
87
+ }
88
+ }
89
+ const object = exported === undefined ? undefined : configObject(exported);
90
+ const globals = member(member(object, "test"), "globals");
91
+ if (globals?.kind === SyntaxKind.TrueKeyword) {
92
+ return true;
93
+ }
94
+ }
95
+ return false;
96
+ }
97
+ /** Whether the runner's configuration in one manifest's directory injects the globals. */
98
+ function injectsGlobals(host, parse, dir) {
99
+ return filesIn(host, dir, (name) => RUNNER_CONFIG.test(name)).some((name) => {
100
+ const path = joinPath(dir, name);
101
+ try {
102
+ return setsGlobals(parse(path, host.readFile(path)));
103
+ }
104
+ catch {
105
+ return false;
106
+ }
107
+ });
108
+ }
109
+ /** Whether a file declaring the globals is installed where the configuration resolves packages from. */
110
+ function globalsInstalled(host, configFile) {
111
+ return ancestors(dirnamePath(configFile)).some((dir) => host.kindOf(joinPath(dir, INSTALLED_PACKAGES, GLOBALS_RUNNER, "globals.d.ts")) === "file");
112
+ }
113
+ /** The directory of the manifest nearest one configuration file, at or above its own. */
114
+ function manifestDir(host, configFile) {
115
+ return ancestors(dirnamePath(configFile)).find((dir) => holdsManifest(host, dir));
116
+ }
117
+ function typesOf(value) {
118
+ return Array.isArray(value) && value.every((one) => typeof one === "string") ? value : undefined;
119
+ }
120
+ /**
121
+ * The plans with the globals' declarations added to the `types` of each target configuration
122
+ * whose nearest manifest's runner configuration injects them and that can resolve them.
123
+ */
124
+ export function testGlobals(host, parse, plans) {
125
+ const injects = new Map();
126
+ return plans.map((plan) => {
127
+ const dir = manifestDir(host, plan.configFile);
128
+ if (!isTargetConfiguration(plan) ||
129
+ dir === undefined ||
130
+ !globalsInstalled(host, plan.configFile)) {
131
+ return plan;
132
+ }
133
+ if (!injects.has(dir)) {
134
+ injects.set(dir, injectsGlobals(host, parse, dir));
135
+ }
136
+ const base = typesOf(plan.options.types) ?? [];
137
+ const { build } = plan;
138
+ if (injects.get(dir) !== true) {
139
+ return plan;
140
+ }
141
+ if (build.kind === "program") {
142
+ const listed = typesOf(build.params.compilerOptions.types) ?? base;
143
+ return listed.includes(GLOBALS_TYPES)
144
+ ? plan
145
+ : {
146
+ ...plan,
147
+ build: {
148
+ kind: "program",
149
+ params: {
150
+ ...build.params,
151
+ compilerOptions: {
152
+ ...build.params.compilerOptions,
153
+ types: [...listed, GLOBALS_TYPES],
154
+ },
155
+ },
156
+ },
157
+ };
158
+ }
159
+ const written = build.kind === "configuration" ? build.value : { extends: plan.configFile };
160
+ const held = written["compilerOptions"];
161
+ const options = isRecord(held) ? held : {};
162
+ const listed = typesOf(options["types"]) ?? base;
163
+ return listed.includes(GLOBALS_TYPES)
164
+ ? plan
165
+ : {
166
+ ...plan,
167
+ build: {
168
+ kind: "configuration",
169
+ value: {
170
+ ...written,
171
+ compilerOptions: { ...options, types: [...listed, GLOBALS_TYPES] },
172
+ },
173
+ },
174
+ };
175
+ });
176
+ }
177
+ /**
178
+ * Whether one file names a global the runner injects, which is how a file written for the
179
+ * runner's globals reads: a call of one of the names or a read of a member of it that
180
+ * resolves to a declaration outside the project's own files, the runner's declaration
181
+ * file. A name the file binds, imported, declared or a parameter, resolves to its own.
182
+ */
183
+ export function namesTestGlobals(project, file) {
184
+ const named = [];
185
+ const visit = (node) => {
186
+ const callee = isCallExpression(node)
187
+ ? node.expression
188
+ : isPropertyAccessExpression(node)
189
+ ? node.expression
190
+ : undefined;
191
+ if (callee !== undefined && isIdentifier(callee) && GLOBAL_NAMES.has(callee.text)) {
192
+ named.push(callee);
193
+ }
194
+ node.forEachChild(visit);
195
+ };
196
+ file.forEachChild(visit);
197
+ if (named.length === 0) {
198
+ return false;
199
+ }
200
+ const own = project.ownPaths();
201
+ return project
202
+ .symbolsAt(named.map((node) => project.handle(node)))
203
+ .some((symbol) => symbol === UNANSWERED ||
204
+ (symbol !== undefined &&
205
+ symbol.declarations.length > 0 &&
206
+ symbol.declarations.every((handle) => !own.has(handle.path))));
207
+ }
@@ -0,0 +1,82 @@
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
+ import { sourcePaths } from "../file-facts.js";
9
+ import { ancestors, dirnamePath, joinPath } from "../paths.js";
10
+ import { bytewise } from "../text/compare.js";
11
+ import { holdsManifest } from "../fs/manifest.js";
12
+ const PREFERRED = "/tsconfig.json";
13
+ /** The configurations of one directory in the order a file joins them: `tsconfig.json` first. */
14
+ function byPreference(a, b) {
15
+ const first = a.endsWith(PREFERRED) ? 0 : 1;
16
+ const second = b.endsWith(PREFERRED) ? 0 : 1;
17
+ return first - second || bytewise(a, b);
18
+ }
19
+ /**
20
+ * Per configuration of `configurations`, the absolute paths of the test files that join it:
21
+ * each file below `root` a pattern of `patterns` names, that no configuration's file list
22
+ * holds, joins the configuration nearest at or above its directory when no `package.json`
23
+ * sits between them.
24
+ */
25
+ export function joinedTestFiles(host, root, configurations, patterns) {
26
+ const listed = new Set();
27
+ const byDir = new Map();
28
+ for (const { configFile, parsed } of configurations) {
29
+ parsed.fileNames.forEach((name) => listed.add(name));
30
+ const dir = dirnamePath(configFile);
31
+ byDir.set(dir, [...(byDir.get(dir) ?? []), configFile].sort(byPreference));
32
+ }
33
+ const joins = new Map();
34
+ for (const path of sourcePaths(host, root)) {
35
+ const file = joinPath(root, path);
36
+ if (listed.has(file) || !patterns.some((pattern) => pattern.test(path))) {
37
+ continue;
38
+ }
39
+ for (const dir of ancestors(dirnamePath(file), root)) {
40
+ const [nearest] = byDir.get(dir) ?? [];
41
+ if (nearest !== undefined) {
42
+ joins.set(nearest, [...(joins.get(nearest) ?? []), file]);
43
+ break;
44
+ }
45
+ if (holdsManifest(host, dir)) {
46
+ break;
47
+ }
48
+ }
49
+ }
50
+ return joins;
51
+ }
52
+ /**
53
+ * The plans with each joined test file added to the configuration it joins: to a described
54
+ * program's root files, or to the file list of a configuration only the snapshot holds, which
55
+ * lists every file the configuration lists and the joined ones.
56
+ */
57
+ export function joinedTests(plans, joins) {
58
+ return plans.map((plan) => {
59
+ const files = joins.get(plan.configFile);
60
+ const { build } = plan;
61
+ if (files === undefined) {
62
+ return plan;
63
+ }
64
+ if (build.kind === "program") {
65
+ return {
66
+ ...plan,
67
+ build: {
68
+ kind: "program",
69
+ params: { ...build.params, rootFiles: [...(build.params.rootFiles ?? []), ...files] },
70
+ },
71
+ };
72
+ }
73
+ const written = build.kind === "configuration" ? build.value : { extends: plan.configFile };
74
+ return {
75
+ ...plan,
76
+ build: {
77
+ kind: "configuration",
78
+ value: { ...written, files: [...plan.parsed.fileNames, ...files], include: [] },
79
+ },
80
+ };
81
+ });
82
+ }
@@ -6,8 +6,9 @@
6
6
  * workspace imports, so only its commands are entry points.
7
7
  */
8
8
  import { emittedFrom } from "./emit-map.js";
9
- import { readManifest } from "./manifest.js";
10
- import { joinPath, relativePath } from "./paths.js";
9
+ import { readManifest } from "../manifest.js";
10
+ import { joinPath, relativePath } from "../paths.js";
11
+ import { MANIFEST } from "../fs/manifest.js";
11
12
  /** The one character a subpath pattern of a manifest's `exports` holds. */
12
13
  const SUBPATH_WILDCARD = "*";
13
14
  /** The paths one written target stands for in its member's sources, itself included. */
@@ -53,8 +54,7 @@ export function memberEntries(host, resolver, targetRoot) {
53
54
  return [];
54
55
  }
55
56
  const manifest = readManifest(host, member.dir);
56
- const where = relativePath(targetRoot, joinPath(member.dir, "package.json")) ??
57
- joinPath(member.dir, "package.json");
57
+ const where = relativePath(targetRoot, joinPath(member.dir, MANIFEST)) ?? joinPath(member.dir, MANIFEST);
58
58
  const kept = manifest.entries.flatMap((entry) => {
59
59
  const exported = entry.member.startsWith("exports");
60
60
  if (member.private && exported) {