@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
@@ -6,9 +6,13 @@
6
6
  * every string either holds as it likes, so each one that spells a declared dependency
7
7
  * uses it and each relative one that names a source file of the program roots the file.
8
8
  */
9
- import { isExportDeclaration, isImportDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/native/ast";
10
- import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
11
- const MANIFEST = "package.json";
9
+ import { isCallExpression, isExportDeclaration, isIdentifier, isImportDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
10
+ import { ancestors, dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
11
+ import { bytewise } from "./text/compare.js";
12
+ import { isRecord } from "./json-document.js";
13
+ import { filesIn } from "./fs/tree.js";
14
+ import { DEPENDENCY_SECTIONS, holdsManifest, inInstalledPackages, manifestAt, readJSON, } from "./fs/manifest.js";
15
+ import { shellWords } from "./text/shell.js";
12
16
  /** A configuration module's name, each form with the spelling a root reports it by. */
13
17
  const MODULE_FORMS = [
14
18
  { name: /^[\w-]+\.config\.(?:js|mjs|cjs|ts|mts|cts)$/u, form: "<stem>.config.<ext>" },
@@ -20,13 +24,6 @@ const MODULE_FORMS = [
20
24
  ];
21
25
  /** A JSON configuration file's name. */
22
26
  const DOCUMENT_NAME = /^(?:[\w-]+(?:\.[\w-]+)?\.config\.json|\.[\w-]+rc(?:\.json)?)$/u;
23
- /** The manifest members a dependency is declared in. */
24
- const DEPENDENCY_MEMBERS = [
25
- "dependencies",
26
- "devDependencies",
27
- "peerDependencies",
28
- "optionalDependencies",
29
- ];
30
27
  /** The prefixes a string names a file with, relative to the file that writes it. */
31
28
  const RELATIVE_PREFIXES = ["./", "../"];
32
29
  /** The JavaScript extensions a relative specifier from a configuration file stands for TypeScript under. */
@@ -50,18 +47,14 @@ export const RESOLVED_EXTENSIONS = [
50
47
  ".mjs",
51
48
  ".cjs",
52
49
  ];
53
- /** The directory a package manager installs packages into, whose files are no project's own. */
54
- const MODULES_DIR = "node_modules";
55
50
  /** The file's name, the last segment of its path. */
56
51
  function basename(path) {
57
52
  return path.slice(path.lastIndexOf("/") + 1);
58
53
  }
59
54
  /** Whether one path is the project's own: below the target root and in no `node_modules`. */
60
55
  function isProjectPath(root, path) {
61
- return relativePath(root, path)?.split("/").includes(MODULES_DIR) === false;
62
- }
63
- function isRecord(value) {
64
- return typeof value === "object" && value !== null && !Array.isArray(value);
56
+ const below = relativePath(root, path);
57
+ return below !== undefined && !inInstalledPackages(below);
65
58
  }
66
59
  /** The form one file name is as a configuration module, or undefined where it is none. */
67
60
  export function configurationModuleForm(name) {
@@ -75,122 +68,13 @@ export function isConfigurationDocumentName(name) {
75
68
  const CONFIG_FLAGS = new Set(["--config", "-c"]);
76
69
  /** The extensions of a module a configuration flag may name. */
77
70
  const MODULE_EXTENSIONS = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"];
78
- /** The characters that end a word outside quotes and separate nothing: blanks. */
79
- // Stryker disable next-line Regex: its \v to \V mutant is no valid escape under the u flag, so the whole module fails to parse
80
- const BLANK = /[ \t\r\f\v]/u;
81
- /** The characters that end a word outside quotes and begin an operator: newline included. */
82
- const OPERATOR = /[\n;&|()<>]/u;
83
- /** The characters a backslash escapes inside double quotes; before any other it stands for itself. */
84
- const DOUBLE_QUOTE_ESCAPES = new Set(["$", "`", '"', "\\"]);
85
- /**
86
- * The words of one command line, as POSIX token recognition reads quotes, backslashes,
87
- * line continuations, comments and operators. A word an expansion may change (an unquoted
88
- * `$`, backquote, glob character or leading `~`, or a `$` or backquote in double quotes)
89
- * is computed, and so is the rest of a line whose quote is never closed. A computed word
90
- * and each operator are undefined, so a flag's file is never read across them.
91
- */
92
- function shellWords(command) {
93
- const words = [];
94
- let word;
95
- let computed = false;
96
- const end = () => {
97
- if (word !== undefined) {
98
- words.push(computed ? undefined : word);
99
- }
100
- word = undefined;
101
- computed = false;
102
- };
103
- for (let at = 0; at < command.length; at += 1) {
104
- const char = command.charAt(at);
105
- if (char === "\\" && command.charAt(at + 1) === "\n") {
106
- at += 1;
107
- }
108
- else if (BLANK.test(char)) {
109
- end();
110
- }
111
- else if (OPERATOR.test(char)) {
112
- end();
113
- words.push(undefined);
114
- }
115
- else if (char === "#" && word === undefined) {
116
- const newline = command.indexOf("\n", at);
117
- at = newline < 0 ? command.length : newline - 1;
118
- }
119
- else if (char === "\\") {
120
- at += 1;
121
- word = (word ?? "") + command.charAt(at);
122
- }
123
- else if (char === "'" || char === '"') {
124
- const close = closingQuote(command, at);
125
- if (close < 0) {
126
- word ??= "";
127
- computed = true;
128
- break;
129
- }
130
- const quoted = char === "'"
131
- ? { text: command.slice(at + 1, close), computed: false }
132
- : doubleQuoted(command.slice(at + 1, close));
133
- word = (word ?? "") + quoted.text;
134
- computed ||= quoted.computed;
135
- at = close;
136
- }
137
- else {
138
- computed ||= /[$`*?[]/u.test(char) || (char === "~" && word === undefined);
139
- word = (word ?? "") + char;
140
- }
141
- }
142
- end();
143
- return words;
144
- }
145
- /** The text between one pair of double quotes, unescaped, and whether an expansion may change it. */
146
- function doubleQuoted(inner) {
147
- let text = "";
148
- let computed = false;
149
- for (let at = 0; at < inner.length; at += 1) {
150
- const char = inner.charAt(at);
151
- const next = inner.charAt(at + 1);
152
- if (char === "\\" && next === "\n") {
153
- at += 1;
154
- }
155
- else if (char === "\\" && DOUBLE_QUOTE_ESCAPES.has(next)) {
156
- text += next;
157
- at += 1;
158
- }
159
- else {
160
- computed ||= char === "$" || char === "`";
161
- text += char;
162
- }
163
- }
164
- return { text, computed };
165
- }
166
- /** The index of the quote that closes the one at `open`, or -1 where none does. */
167
- function closingQuote(command, open) {
168
- const quote = command.charAt(open);
169
- for (let at = open + 1; at < command.length; at += 1) {
170
- const char = command.charAt(at);
171
- if (char === quote) {
172
- return at;
173
- }
174
- if (quote === '"' && char === "\\") {
175
- at += 1;
176
- }
177
- }
178
- return -1;
179
- }
180
71
  /**
181
72
  * The files the scripts of the manifest in one directory name after a configuration flag,
182
73
  * `--config <file>`, `-c <file>` or `--config=<file>`, each a JSON file or a module below
183
74
  * the target root and in no `node_modules`.
184
75
  */
185
76
  function scriptConfigurations(host, root, dir) {
186
- let scripts;
187
- try {
188
- const manifest = JSON.parse(host.readFile(joinPath(dir, MANIFEST)));
189
- scripts = isRecord(manifest) ? manifest["scripts"] : undefined;
190
- }
191
- catch {
192
- return [];
193
- }
77
+ const scripts = manifestAt(host, dir)?.["scripts"];
194
78
  if (!isRecord(scripts)) {
195
79
  return [];
196
80
  }
@@ -217,15 +101,6 @@ function scriptConfigurations(host, root, dir) {
217
101
  }
218
102
  return found;
219
103
  }
220
- /** The decoded value of one JSON file, or undefined where it cannot be read or parsed. */
221
- function readDocument(host, path) {
222
- try {
223
- return JSON.parse(host.readFile(path));
224
- }
225
- catch {
226
- return undefined;
227
- }
228
- }
229
104
  /** Every string value one decoded JSON value holds at any depth. */
230
105
  export function documentStrings(value) {
231
106
  if (typeof value === "string") {
@@ -258,10 +133,10 @@ export function moduleStrings(file) {
258
133
  export function configurationFiles(host, targetRoot, files, parse) {
259
134
  const root = resolvePath(host.workingDirectory(), targetRoot);
260
135
  const manifests = new Map();
261
- const holdsManifest = (dir) => {
136
+ const manifestHeld = (dir) => {
262
137
  let held = manifests.get(dir);
263
138
  if (held === undefined) {
264
- held = host.kindOf(joinPath(dir, MANIFEST)) === "file";
139
+ held = holdsManifest(host, dir);
265
140
  manifests.set(dir, held);
266
141
  }
267
142
  return held;
@@ -271,30 +146,23 @@ export function configurationFiles(host, targetRoot, files, parse) {
271
146
  for (const file of files.byPath.values()) {
272
147
  const form = configurationModuleForm(basename(file.fileName));
273
148
  const fileDir = dirnamePath(file.fileName);
274
- if (form !== undefined && holdsManifest(fileDir)) {
149
+ if (form !== undefined && manifestHeld(fileDir)) {
275
150
  modules.push({ file, form, manifestDir: fileDir });
276
151
  }
277
- for (let dir = dirnamePath(file.fileName); relativePath(root, dir) !== undefined && !dirs.has(dir); dir = dirnamePath(dir)) {
152
+ for (const dir of ancestors(dirnamePath(file.fileName), root)) {
153
+ if (dirs.has(dir)) {
154
+ break;
155
+ }
278
156
  dirs.add(dir);
279
157
  }
280
158
  }
281
159
  const pending = [];
282
160
  const named = [];
283
- for (const dir of [...dirs].sort()) {
284
- if (!holdsManifest(dir)) {
161
+ for (const dir of [...dirs].sort(bytewise)) {
162
+ if (!manifestHeld(dir)) {
285
163
  continue;
286
164
  }
287
- let names;
288
- try {
289
- names = host
290
- .readDirectory(dir)
291
- .filter((entry) => !entry.directory && isConfigurationDocumentName(entry.name))
292
- .map((entry) => entry.name)
293
- .sort();
294
- }
295
- catch {
296
- names = [];
297
- }
165
+ const names = filesIn(host, dir, isConfigurationDocumentName);
298
166
  pending.push(...names.map((name) => ({ path: joinPath(dir, name), manifestDir: dir })));
299
167
  for (const path of scriptConfigurations(host, root, dir)) {
300
168
  const held = files.byName.get(path);
@@ -318,7 +186,7 @@ export function configurationFiles(host, targetRoot, files, parse) {
318
186
  continue;
319
187
  }
320
188
  if (document === undefined) {
321
- const value = readDocument(host, next.path);
189
+ const value = readJSON(host, next.path);
322
190
  if (value === undefined) {
323
191
  unreadable.add(next.path);
324
192
  continue;
@@ -343,21 +211,59 @@ export function configurationFiles(host, targetRoot, files, parse) {
343
211
  documents: [...documents.values()],
344
212
  outside: parse === undefined
345
213
  ? []
346
- : outsideModules(host, root, [...dirs], files, holdsManifest, parse, named),
214
+ : outsideModules(host, root, [...dirs], files, manifestHeld, parse, named),
347
215
  };
348
216
  }
349
- /** The relative specifiers one file's imports and re-exports name. */
350
- function relativeSpecifiers(file) {
351
- return file.statements.flatMap((statement) => {
352
- const specifier = isImportDeclaration(statement) || isExportDeclaration(statement)
217
+ /** The literal module specifier one `require(...)` or `import(...)` call names, if any. */
218
+ function calledSpecifier(node) {
219
+ if (!isCallExpression(node) || node.arguments.length !== 1) {
220
+ return undefined;
221
+ }
222
+ const callee = node.expression;
223
+ const loads = callee.kind === SyntaxKind.ImportKeyword || (isIdentifier(callee) && callee.text === "require");
224
+ return loads ? node.arguments[0] : undefined;
225
+ }
226
+ /**
227
+ * The relative specifiers one file's imports and re-exports name, and those its
228
+ * `require(...)` and `import(...)` calls name by a literal.
229
+ */
230
+ export function relativeSpecifiers(file) {
231
+ const known = specifiersOf.get(file);
232
+ if (known !== undefined) {
233
+ return known;
234
+ }
235
+ const found = [];
236
+ const named = (specifier) => {
237
+ if (specifier !== undefined &&
238
+ (isStringLiteral(specifier) || isNoSubstitutionTemplateLiteral(specifier)) &&
239
+ RELATIVE_PREFIXES.some((prefix) => specifier.text.startsWith(prefix))) {
240
+ found.push(specifier.text);
241
+ }
242
+ };
243
+ const visit = (node) => {
244
+ named(calledSpecifier(node));
245
+ node.forEachChild(visit);
246
+ };
247
+ for (const statement of file.statements) {
248
+ named(isImportDeclaration(statement) || isExportDeclaration(statement)
353
249
  ? statement.moduleSpecifier
354
- : undefined;
355
- return specifier !== undefined &&
356
- isStringLiteral(specifier) &&
357
- RELATIVE_PREFIXES.some((prefix) => specifier.text.startsWith(prefix))
358
- ? [specifier.text]
359
- : [];
360
- });
250
+ : undefined);
251
+ visit(statement);
252
+ }
253
+ specifiersOf.set(file, found);
254
+ return found;
255
+ }
256
+ /** What {@link relativeSpecifiers} found in each file, which every project of a run reads alike. */
257
+ const specifiersOf = new WeakMap();
258
+ /**
259
+ * The files no program holds that a chain from `seeds` reads: each seed, and each file
260
+ * below the target root one imports, re-exports or loads by a relative specifier that no
261
+ * program holds either, until no further file joins, each read against the manifest
262
+ * directory its seed names. A file that cannot be read is skipped.
263
+ */
264
+ export function outsideChain(host, targetRoot, files, parse, seeds) {
265
+ const root = resolvePath(host.workingDirectory(), targetRoot);
266
+ return outsideModules(host, root, [], files, () => false, parse, seeds);
361
267
  }
362
268
  /**
363
269
  * The paths one relative specifier from a configuration file may reach, in the order they
@@ -391,23 +297,13 @@ export function resolveRelativeSpecifier(host, dir, specifier) {
391
297
  * does not hold, and the project files below `root` they import by a relative specifier,
392
298
  * until no further file joins. A file that cannot be read is skipped.
393
299
  */
394
- function outsideModules(host, root, dirs, files, holdsManifest, parse, named) {
300
+ function outsideModules(host, root, dirs, files, manifestHeld, parse, named) {
395
301
  const pending = [...named];
396
- for (const dir of [...dirs].sort()) {
397
- if (!holdsManifest(dir)) {
398
- continue;
399
- }
400
- let names;
401
- try {
402
- names = host
403
- .readDirectory(dir)
404
- .filter((entry) => !entry.directory && configurationModuleForm(entry.name) !== undefined)
405
- .map((entry) => entry.name)
406
- .sort();
407
- }
408
- catch {
302
+ for (const dir of [...dirs].sort(bytewise)) {
303
+ if (!manifestHeld(dir)) {
409
304
  continue;
410
305
  }
306
+ const names = filesIn(host, dir, (name) => configurationModuleForm(name) !== undefined);
411
307
  pending.push(...names.map((name) => ({ path: joinPath(dir, name), manifestDir: dir })));
412
308
  }
413
309
  const found = new Map();
@@ -443,18 +339,12 @@ function outsideModules(host, root, dirs, files, holdsManifest, parse, named) {
443
339
  }
444
340
  /** Every dependency the manifest in one directory declares, or none where it holds none to read. */
445
341
  export function declaredIn(host, dir) {
446
- let value;
447
- try {
448
- value = JSON.parse(host.readFile(joinPath(dir, MANIFEST)));
449
- }
450
- catch {
451
- return [];
452
- }
453
- if (!isRecord(value)) {
342
+ const value = manifestAt(host, dir);
343
+ if (value === undefined) {
454
344
  return [];
455
345
  }
456
346
  const names = new Set();
457
- for (const member of DEPENDENCY_MEMBERS) {
347
+ for (const member of DEPENDENCY_SECTIONS) {
458
348
  const section = value[member];
459
349
  if (isRecord(section)) {
460
350
  Object.keys(section).forEach((name) => names.add(name));
@@ -179,19 +179,19 @@ export function resolveEntries(project, held, entries) {
179
179
  for (const symbol of held.symbols) {
180
180
  byRef.set(symbol.ref, [...(byRef.get(symbol.ref) ?? []), symbol.id]);
181
181
  }
182
- const modules = modulesNamed(project, new Set(entries.flatMap((entry) => (entry.shape === "module" ? [entry.module] : []))));
182
+ const modules = modulesNamed(project, new Set(entries.flatMap((entry) => ("module" in entry ? [entry.module] : []))));
183
183
  return entries.map((entry) => {
184
- if (entry.shape === "symbol") {
184
+ if ("symbol" in entry) {
185
185
  return { entry, handles: new Set(), ids: new Set(byRef.get(entry.symbol) ?? []) };
186
186
  }
187
- const { components, static: isStatic } = componentsOf(entry.shape === "module" ? entry.name : entry.global);
187
+ const { components, static: isStatic } = componentsOf("module" in entry ? entry.name : entry.global);
188
188
  const [first = "", ...rest] = components;
189
189
  let start;
190
190
  if (isStatic && rest.length === 0) {
191
191
  // Only a member is static, and a path of one component names no member.
192
192
  start = undefined;
193
193
  }
194
- else if (entry.shape === "module") {
194
+ else if ("module" in entry) {
195
195
  const module = modules.get(entry.module);
196
196
  start = module === undefined ? undefined : memberOf(project, module, first, false);
197
197
  }
@@ -246,14 +246,10 @@ export function entriesAt(project, chains, nodes, resolved) {
246
246
  * program the display name the inventory gives it.
247
247
  */
248
248
  export function spelledEntry(entry, held) {
249
- switch (entry.shape) {
250
- case "symbol":
251
- return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
252
- case "module":
253
- return entry.name;
254
- case "global":
255
- return entry.global;
249
+ if ("symbol" in entry) {
250
+ return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
256
251
  }
252
+ return "module" in entry ? entry.name : entry.global;
257
253
  }
258
254
  /** The key under which a lifecycle contract's declarations are configured. */
259
255
  const LIFECYCLE_KEY = "ts.lifecycle_contracts";
@@ -264,11 +260,14 @@ const LIFECYCLE_KEY = "ts.lifecycle_contracts";
264
260
  */
265
261
  export function configuredDeclarations(config) {
266
262
  return [
267
- ...config.ts.injectionRegistrations.map((entry) => ({
263
+ ...config.ts.injection_registrations.map((entry) => ({
268
264
  key: "ts.injection_registrations",
269
265
  entry,
270
266
  })),
271
- ...config.ts.lifecycleContracts.flatMap((contract) => [...contract.components, ...contract.bases].map((entry) => ({ key: LIFECYCLE_KEY, entry }))),
267
+ ...config.ts.lifecycle_contracts.flatMap((contract) => [...(contract.components ?? []), ...(contract.bases ?? [])].map((entry) => ({
268
+ key: LIFECYCLE_KEY,
269
+ entry,
270
+ }))),
272
271
  ...config.ts.serializers.map((entry) => ({ key: "ts.serializers", entry })),
273
272
  ];
274
273
  }
@@ -294,12 +293,8 @@ export function declarationsNamed(project, held, configured) {
294
293
  * sign and its name, and a global entry's path after a number sign.
295
294
  */
296
295
  export function configuredRef(entry) {
297
- switch (entry.shape) {
298
- case "symbol":
299
- return entry.symbol;
300
- case "module":
301
- return `${entry.module}#${entry.name}`;
302
- case "global":
303
- return `#${entry.global}`;
296
+ if ("symbol" in entry) {
297
+ return entry.symbol;
304
298
  }
299
+ return "module" in entry ? `${entry.module}#${entry.name}` : `#${entry.global}`;
305
300
  }
@@ -2,9 +2,9 @@
2
2
  // Regenerate it with `UPDATE_GOLDEN=1 npx vitest --run src/conformance.corpus.test.ts`.
3
3
  export const RECORD = {
4
4
  conformance: {
5
- corpusVersion: "5.2.0",
5
+ corpusVersion: "6.0.0",
6
6
  result: "pass",
7
- digest: "sha256:012872542679539ec105f49b5ee502b0a9fa35c6bbd9ef3983b5f6e53af83d8e",
7
+ digest: "sha256:0d33564807a356fd3e2760ddaa0645d758bdb9a96ee74a392675e444343bbf20",
8
8
  },
9
9
  gaps: [],
10
10
  };
@@ -1,34 +1,7 @@
1
- /**
2
- * The consumers a scope declares, loaded beside the target: each a directory on the
3
- * local filesystem whose compiler configurations open as projects of the run's one
4
- * snapshot, and whose references to the target's declarations count against them.
5
- */
6
- import { consumerProjects } from "./discover.js";
7
- import { inventory, packageScope } from "./inventory.js";
1
+ import { inventory } from "./inventory.js";
8
2
  import { relativePath } from "./paths.js";
9
3
  import { references } from "./references.js";
10
4
  import { narrowedTo } from "./session.js";
11
- /** The name one module publishes itself under: the scope's, else its manifest's. */
12
- export function moduleIdentity(host, module) {
13
- return module.id === "" ? packageScope(host, module.path)("index.ts").package : module.id;
14
- }
15
- /**
16
- * Every consumer the scope declares, in its order, with the projects it opens as. A
17
- * configuration that is also one of the target's projects, or an earlier consumer's, is
18
- * that project's alone. A consumer whose directory is absent, is no directory or holds
19
- * no configuration ends the run naming it, before any project opens.
20
- */
21
- export function consumerLoads(engine, host, scope, targetConfigFiles) {
22
- const opened = new Set(targetConfigFiles);
23
- return scope.consumers.map((module) => {
24
- const configFiles = consumerProjects(engine, host, module.path).filter((configFile) => {
25
- const fresh = !opened.has(configFile);
26
- opened.add(configFile);
27
- return fresh;
28
- });
29
- return { consumer: { id: moduleIdentity(host, module), path: module.path }, configFiles };
30
- });
31
- }
32
5
  /**
33
6
  * Every reference one consumer project's own files make to a declaration of the target.
34
7
  * The target's declarations are enumerated over the target's files the consumer's
@@ -39,10 +12,10 @@ export function consumerLoads(engine, host, scope, targetConfigFiles) {
39
12
  export function consumerReferences(project, host, targetRoot, consumer, config) {
40
13
  const held = inventory(narrowedTo(project, (file) => relativePath(targetRoot, file.fileName) !== undefined), host, targetRoot);
41
14
  const found = references(project, held, targetRoot, {
42
- testFiles: config.ts.testFiles,
15
+ testFiles: config.ts.test_files,
43
16
  consumer,
44
17
  }).references;
45
- if (config.analysis.consumerTests !== "production") {
18
+ if (config.analysis.consumer_tests !== "production") {
46
19
  return found;
47
20
  }
48
21
  return found.map((reference) => (reference.test ? { ...reference, test: false } : reference));
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * The uses contextual types make of the target's members, which no name node resolves to.
3
3
  * An object-literal property writes the member of its name on each constituent of the
4
- * literal's contextual type that declares one. A value reaching a position typed by another
5
- * object type reads each of its members that type declares, through array elements and
6
- * the parameters and results of functions and constructors too; a class instance there is
7
- * the interface-satisfaction class's evidence instead.
4
+ * literal's contextual type that declares one, and so does a property of a literal whose
5
+ * type is inferred, where a value of that type reaches such a position. A value reaching a
6
+ * position typed by another object type reads each of its members that type declares,
7
+ * through array elements and the parameters and results of functions and constructors
8
+ * too; a class instance there is the interface-satisfaction class's evidence instead.
8
9
  */
9
10
  import { isClassDeclaration, isClassExpression, isComputedPropertyName, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isMethodSignatureDeclaration, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isPropertyAssignment, isSetAccessorDeclaration, isShorthandPropertyAssignment, isSpreadAssignment, isStringLiteral, } from "@typescript/native/ast";
10
11
  import { SignatureKind } from "@typescript/native/sync";
@@ -164,6 +165,33 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
164
165
  const members = [...membersOf(part).values()];
165
166
  return members.length > 0 && members.every((member) => !member.own);
166
167
  };
168
+ const literalTypes = new Map();
169
+ /**
170
+ * Whether an object type is an object literal's own, inferred where no contextual type
171
+ * names it: every member a property an object literal of the target writes.
172
+ */
173
+ const literalTyped = (part) => {
174
+ let known = literalTypes.get(part.id);
175
+ if (known === undefined) {
176
+ const members = [...membersOf(part).values()];
177
+ known =
178
+ members.length > 0 &&
179
+ members.every((member) => member.ids.length === 0 &&
180
+ member.declarations.length > 0 &&
181
+ member.declarations.every((handle) => literalProperty(handle) !== undefined));
182
+ literalTypes.set(part.id, known);
183
+ }
184
+ return known;
185
+ };
186
+ /** The name node of the object literal property one declaration handle names, if it is one. */
187
+ const literalProperty = (handle) => {
188
+ const node = project.declarationAt(handle)?.node;
189
+ return node !== undefined &&
190
+ (isPropertyAssignment(node) || isShorthandPropertyAssignment(node)) &&
191
+ isObjectLiteralExpression(node.parent)
192
+ ? node.name
193
+ : undefined;
194
+ };
167
195
  const elements = new Map();
168
196
  /** The element type of an array type, read once per type. */
169
197
  const elementOf = (type) => {
@@ -246,10 +274,13 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
246
274
  const sources = partsOf(source).filter((one) => !targets.some((part) => part.id === one.id));
247
275
  for (const one of sources) {
248
276
  if (carries(one)) {
249
- into.push({ source: one, targets, outside: false });
277
+ into.push({ source: one, targets, sourceKind: "members" });
250
278
  }
251
279
  else if (outside(one)) {
252
- into.push({ source: one, targets, outside: true });
280
+ into.push({ source: one, targets, sourceKind: "outside" });
281
+ }
282
+ else if (literalTyped(one)) {
283
+ into.push({ source: one, targets, sourceKind: "literal" });
253
284
  }
254
285
  const element = elementOf(one);
255
286
  const called = element === undefined ? callsOf(one) : [];
@@ -290,7 +321,7 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
290
321
  }
291
322
  seen.add(type.id);
292
323
  return partsOf(type).some((part) => {
293
- if (carries(part) || outside(part)) {
324
+ if (carries(part) || outside(part) || literalTyped(part)) {
294
325
  return true;
295
326
  }
296
327
  const element = elementOf(part);
@@ -307,6 +338,8 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
307
338
  batches += batchesOf(sourced.length);
308
339
  const positions = queries.contextualTypes(sourced.map((one) => one.flow.value), cap);
309
340
  const reads = [];
341
+ // A literal property writes a position's members once, however many values carry it there.
342
+ const carriedWrites = new Set();
310
343
  sourced.forEach(({ flow, type }, index) => {
311
344
  const position = positions[index];
312
345
  if (position === undefined || !isAnswered(position)) {
@@ -315,10 +348,24 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
315
348
  const checked = [];
316
349
  compared(type, position, checked, new Set());
317
350
  const ids = new Set();
318
- for (const { source, targets, outside: held } of checked) {
351
+ for (const { source, targets, sourceKind } of checked) {
319
352
  const tables = targets.map(membersOf);
320
353
  const sourceMembers = membersOf(source);
321
- if (held) {
354
+ if (sourceKind === "literal") {
355
+ for (const [name, member] of sourceMembers) {
356
+ const written = [...new Set(tables.flatMap((table) => table.get(name)?.ids ?? []))];
357
+ for (const at of written.length === 0 ? [] : member.declarations) {
358
+ const node = literalProperty(at);
359
+ const key = `${at.path}#${String(at.index)}>${written.join(",")}`;
360
+ if (node !== undefined && !carriedWrites.has(key)) {
361
+ carriedWrites.add(key);
362
+ writes.push({ at: node, from: flow.from, ids: written });
363
+ }
364
+ }
365
+ }
366
+ continue;
367
+ }
368
+ if (sourceKind === "outside") {
322
369
  for (const table of tables) {
323
370
  for (const [name, member] of table) {
324
371
  if (sourceMembers.has(name)) {