@cplieger/deadset-ts 8.0.0-dev.1 → 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 (276) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/mapper-connection.js +2 -1
  3. package/dist/bin/node-host.js +24 -5
  4. package/dist/src/analysis.js +181 -341
  5. package/dist/src/augmentations.js +106 -13
  6. package/dist/src/automatic-types.js +10 -29
  7. package/dist/src/build-scripts.js +425 -0
  8. package/dist/src/component-layer.js +4 -2
  9. package/dist/src/components.js +2 -3
  10. package/dist/src/config.js +6 -82
  11. package/dist/src/configuration-files.js +28 -176
  12. package/dist/src/configured-declarations.js +15 -20
  13. package/dist/src/conformance-record.js +1 -1
  14. package/dist/src/consumers.js +3 -30
  15. package/dist/src/contextual-uses.js +56 -9
  16. package/dist/src/contract/config.generated.js +558 -0
  17. package/dist/src/contract/contract.generated.js +1244 -0
  18. package/dist/src/contract/report.generated.js +862 -0
  19. package/dist/src/convention-rows.js +73 -3
  20. package/dist/src/conventions.js +36 -66
  21. package/dist/src/dependencies.js +103 -40
  22. package/dist/src/edges.js +38 -122
  23. package/dist/src/element-access.js +4 -6
  24. package/dist/src/entry-point-gaps.js +2 -8
  25. package/dist/src/entry-points.js +11 -27
  26. package/dist/src/enum-group.js +3 -10
  27. package/dist/src/erased-stores.js +4 -6
  28. package/dist/src/exempt-classes.js +1 -63
  29. package/dist/src/exempt.js +21 -40
  30. package/dist/src/failure.js +36 -0
  31. package/dist/src/file-facts.js +2 -25
  32. package/dist/src/findings/completion.js +139 -72
  33. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  34. package/dist/src/findings/emitters.js +5 -5
  35. package/dist/src/findings/interfaces.js +4 -5
  36. package/dist/src/findings/intra-function.js +13 -10
  37. package/dist/src/findings/non-code-artifacts.js +7 -8
  38. package/dist/src/findings/reads-and-writes.js +16 -13
  39. package/dist/src/findings/self-check.js +10 -12
  40. package/dist/src/findings/unused-declarations.js +41 -32
  41. package/dist/src/findings/visibility-narrowing.js +19 -16
  42. package/dist/src/findings-pass.js +8 -15
  43. package/dist/src/framework-lifecycle.js +7 -7
  44. package/dist/src/fs/document.js +52 -0
  45. package/dist/src/fs/manifest.js +72 -0
  46. package/dist/src/fs/tree.js +88 -0
  47. package/dist/src/generated-file.js +7 -4
  48. package/dist/src/git-ignore.js +39 -26
  49. package/dist/src/graph.js +5 -3
  50. package/dist/src/html-entries.js +6 -33
  51. package/dist/src/in-operands.js +2 -1
  52. package/dist/src/index.js +1 -1
  53. package/dist/src/injection-container.js +1 -1
  54. package/dist/src/intra-function-parts.js +63 -43
  55. package/dist/src/inventory.js +2 -21
  56. package/dist/src/javascript-file.js +33 -0
  57. package/dist/src/json-document.js +198 -67
  58. package/dist/src/keyof-comparisons.js +4 -6
  59. package/dist/src/kinds.js +3 -366
  60. package/dist/src/load/classify.js +270 -0
  61. package/dist/src/load/diagnostics.js +80 -0
  62. package/dist/src/load/discover.js +277 -0
  63. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  64. package/dist/src/load/expected-module.js +136 -0
  65. package/dist/src/load/load.js +27 -0
  66. package/dist/src/load/module-lookup.js +37 -0
  67. package/dist/src/load/plan.js +313 -0
  68. package/dist/src/load/specifiers.js +115 -0
  69. package/dist/src/load/synthesize.js +140 -0
  70. package/dist/src/load/test-globals.js +207 -0
  71. package/dist/src/load/test-joins.js +82 -0
  72. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  73. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  74. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
  75. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  76. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  77. package/dist/src/manifest.js +49 -54
  78. package/dist/src/matrix.js +76 -4
  79. package/dist/src/mdx-entries.js +3 -26
  80. package/dist/src/outside-tests.js +1 -1
  81. package/dist/src/partial.js +2 -1
  82. package/dist/src/paths.js +26 -15
  83. package/dist/src/position.js +2 -1
  84. package/dist/src/print.js +11 -79
  85. package/dist/src/product.js +13 -0
  86. package/dist/src/query.js +1 -0
  87. package/dist/src/ref.js +1 -0
  88. package/dist/src/references.js +48 -30
  89. package/dist/src/report-schema.js +51 -0
  90. package/dist/src/report.js +57 -41
  91. package/dist/src/reporters/annotations.js +15 -8
  92. package/dist/src/reporters/sarif.js +16 -10
  93. package/dist/src/reporters/template.js +4 -19
  94. package/dist/src/resolve.js +245 -729
  95. package/dist/src/roots.js +273 -87
  96. package/dist/src/run.js +66 -77
  97. package/dist/src/schema.js +320 -343
  98. package/dist/src/scope.js +38 -115
  99. package/dist/src/serialization-contract.js +595 -21
  100. package/dist/src/session.js +34 -29
  101. package/dist/src/source-files.js +1 -1
  102. package/dist/src/suppress-file.js +25 -28
  103. package/dist/src/suppress-inline.js +3 -2
  104. package/dist/src/suppress.js +33 -51
  105. package/dist/src/sweep.js +7 -9
  106. package/dist/src/symbol-keys.js +3 -5
  107. package/dist/src/template-field.js +9 -19
  108. package/dist/src/test-support.js +8 -11
  109. package/dist/src/text/compare.js +26 -0
  110. package/dist/src/text/glob.js +194 -0
  111. package/dist/src/text/named-files.js +54 -0
  112. package/dist/src/text/shell.js +159 -0
  113. package/dist/src/text/yaml.js +393 -0
  114. package/dist/src/type-errors.js +11 -232
  115. package/dist/src/unseen-loads.js +100 -0
  116. package/dist/src/unwrap.js +23 -0
  117. package/dist/src/usage.js +10 -0
  118. package/dist/src/verbs/analyze.js +166 -126
  119. package/dist/src/verbs/describe.js +9 -16
  120. package/dist/src/verbs/explain.js +10 -7
  121. package/dist/src/verbs/print-config.js +2 -1
  122. package/dist/src/verbs/print-projects.js +6 -4
  123. package/dist/src/verbs/print-retained.js +4 -2
  124. package/dist/src/verbs/print-roots.js +4 -2
  125. package/dist/src/verbs/verb.js +1 -5
  126. package/dist/src/verbs/version.js +14 -3
  127. package/dist/src/workflow-steps.js +62 -64
  128. package/jsr.json +1 -1
  129. package/package.json +4 -3
  130. package/src/analysis.ts +245 -502
  131. package/src/augmentations.ts +135 -17
  132. package/src/automatic-types.ts +11 -29
  133. package/src/build-scripts.ts +543 -0
  134. package/src/component-layer.ts +4 -2
  135. package/src/components.ts +2 -4
  136. package/src/config.ts +31 -302
  137. package/src/configuration-files.ts +34 -179
  138. package/src/configured-declarations.ts +18 -23
  139. package/src/conformance-record.ts +1 -1
  140. package/src/consumers.ts +6 -57
  141. package/src/contextual-uses.ts +64 -12
  142. package/src/contract/config.generated.ts +818 -0
  143. package/src/contract/contract.generated.ts +1308 -0
  144. package/src/contract/report.generated.ts +1237 -0
  145. package/src/convention-rows.ts +85 -3
  146. package/src/conventions.ts +46 -84
  147. package/src/dependencies.ts +118 -46
  148. package/src/edges.ts +41 -140
  149. package/src/element-access.ts +4 -7
  150. package/src/entry-point-gaps.ts +3 -9
  151. package/src/entry-points.ts +12 -38
  152. package/src/enum-group.ts +2 -11
  153. package/src/erased-stores.ts +3 -7
  154. package/src/exempt-classes.ts +1 -75
  155. package/src/exempt.ts +36 -50
  156. package/src/failure.ts +63 -0
  157. package/src/file-facts.ts +9 -24
  158. package/src/finding.ts +14 -9
  159. package/src/findings/boundary.ts +2 -0
  160. package/src/findings/completion.ts +181 -84
  161. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  162. package/src/findings/emitter.ts +8 -9
  163. package/src/findings/emitters.ts +7 -7
  164. package/src/findings/interfaces.ts +4 -6
  165. package/src/findings/intra-function.ts +13 -11
  166. package/src/findings/non-code-artifacts.ts +9 -11
  167. package/src/findings/reads-and-writes.ts +20 -16
  168. package/src/findings/self-check.ts +11 -17
  169. package/src/findings/unused-declarations.ts +45 -32
  170. package/src/findings/visibility-narrowing.ts +20 -17
  171. package/src/findings-pass.ts +10 -19
  172. package/src/framework-lifecycle.ts +7 -7
  173. package/src/fs/document.ts +70 -0
  174. package/src/fs/manifest.ts +92 -0
  175. package/src/fs/tree.ts +110 -0
  176. package/src/generated-file.ts +7 -4
  177. package/src/git-ignore.ts +50 -26
  178. package/src/graph.ts +5 -3
  179. package/src/host.ts +12 -5
  180. package/src/html-entries.ts +6 -33
  181. package/src/in-operands.ts +2 -1
  182. package/src/index.ts +1 -1
  183. package/src/injection-container.ts +1 -1
  184. package/src/intra-function-parts.ts +73 -44
  185. package/src/inventory.ts +2 -20
  186. package/src/javascript-file.ts +37 -0
  187. package/src/json-document.ts +241 -76
  188. package/src/keyof-comparisons.ts +3 -7
  189. package/src/kinds.ts +5 -413
  190. package/src/load/classify.ts +397 -0
  191. package/src/load/diagnostics.ts +144 -0
  192. package/src/load/discover.ts +455 -0
  193. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  194. package/src/load/expected-module.ts +188 -0
  195. package/src/load/load.ts +73 -0
  196. package/src/load/module-lookup.ts +50 -0
  197. package/src/load/plan.ts +504 -0
  198. package/src/load/specifiers.ts +168 -0
  199. package/src/load/synthesize.ts +211 -0
  200. package/src/load/test-globals.ts +251 -0
  201. package/src/load/test-joins.ts +98 -0
  202. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  203. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  204. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
  205. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  206. package/src/{workspace.ts → load/workspace.ts} +90 -134
  207. package/src/manifest.ts +81 -75
  208. package/src/matrix.ts +122 -4
  209. package/src/mdx-entries.ts +3 -26
  210. package/src/outside-tests.ts +1 -1
  211. package/src/partial.ts +3 -2
  212. package/src/paths.ts +27 -15
  213. package/src/position.ts +2 -1
  214. package/src/print.ts +12 -92
  215. package/src/product.ts +18 -0
  216. package/src/query.ts +11 -0
  217. package/src/ref.ts +1 -0
  218. package/src/references.ts +52 -31
  219. package/src/report-schema.ts +68 -0
  220. package/src/report.ts +101 -224
  221. package/src/reporters/annotations.ts +22 -11
  222. package/src/reporters/json.ts +1 -1
  223. package/src/reporters/reporter.ts +2 -4
  224. package/src/reporters/reporters.ts +1 -1
  225. package/src/reporters/sarif.ts +29 -16
  226. package/src/reporters/template.ts +4 -20
  227. package/src/reporters/text.ts +3 -3
  228. package/src/resolve.ts +283 -1114
  229. package/src/roots.ts +339 -104
  230. package/src/run.ts +75 -91
  231. package/src/schema.ts +391 -388
  232. package/src/scope.ts +46 -138
  233. package/src/serialization-contract.ts +763 -22
  234. package/src/session.ts +70 -54
  235. package/src/source-files.ts +1 -1
  236. package/src/suppress-file.ts +24 -33
  237. package/src/suppress-inline.ts +2 -2
  238. package/src/suppress.ts +43 -58
  239. package/src/sweep.ts +23 -9
  240. package/src/symbol-keys.ts +2 -5
  241. package/src/template-field.ts +15 -25
  242. package/src/test-support.ts +17 -13
  243. package/src/text/compare.ts +27 -0
  244. package/src/text/glob.ts +210 -0
  245. package/src/text/named-files.inputs.json +1522 -0
  246. package/src/text/named-files.ts +68 -0
  247. package/src/text/shell.ts +161 -0
  248. package/src/text/yaml.ts +423 -0
  249. package/src/type-errors.ts +18 -288
  250. package/src/unseen-loads.ts +134 -0
  251. package/src/unwrap.ts +34 -0
  252. package/src/usage.ts +10 -0
  253. package/src/verbs/analyze.ts +224 -162
  254. package/src/verbs/describe.ts +14 -15
  255. package/src/verbs/explain.ts +10 -11
  256. package/src/verbs/print-config.ts +2 -1
  257. package/src/verbs/print-projects.ts +6 -4
  258. package/src/verbs/print-retained.ts +4 -2
  259. package/src/verbs/print-roots.ts +4 -2
  260. package/src/verbs/verb.ts +2 -8
  261. package/src/verbs/version.ts +15 -3
  262. package/src/workflow-steps.ts +74 -64
  263. package/dist/src/base-url.js +0 -18
  264. package/dist/src/discover.js +0 -468
  265. package/dist/src/glob.js +0 -65
  266. package/dist/src/pnpm-workspace.js +0 -120
  267. package/dist/src/setup-failure.js +0 -23
  268. package/dist/src/test-joins.js +0 -101
  269. package/dist/src/version.js +0 -7
  270. package/src/base-url.ts +0 -24
  271. package/src/discover.ts +0 -653
  272. package/src/glob.ts +0 -67
  273. package/src/pnpm-workspace.ts +0 -135
  274. package/src/setup-failure.ts +0 -39
  275. package/src/test-joins.ts +0 -120
  276. package/src/version.ts +0 -7
@@ -0,0 +1,393 @@
1
+ /**
2
+ * The value of a YAML configuration document in the subset tool configurations write:
3
+ * block mappings and sequences, flow mappings and sequences, and plain, single-quoted and
4
+ * double-quoted scalars, with comments. A document using anything else, an anchor, an
5
+ * alias, a tag, a block scalar, a complex key or a second document, has no value here,
6
+ * as a JSON document that does not parse has none.
7
+ */
8
+ import { isRecord } from "../json-document.js";
9
+ /** A failure to read the document within the subset; it ends the whole reading. */
10
+ class Unsupported extends Error {
11
+ }
12
+ /** The characters after which a quote opens a quoted scalar rather than standing in a plain one. */
13
+ const SCALAR_START = new Set(["", ":", "-", "[", "{", ","]);
14
+ /** The plain scalars YAML's core schema reads as something other than a string. */
15
+ const CORE = new Map([
16
+ ["true", true],
17
+ ["True", true],
18
+ ["TRUE", true],
19
+ ["false", false],
20
+ ["False", false],
21
+ ["FALSE", false],
22
+ ["null", null],
23
+ ["Null", null],
24
+ ["NULL", null],
25
+ ["~", null],
26
+ ["", null],
27
+ ]);
28
+ const NUMBER = /^[-+]?(?:\d+|\d*\.\d+|\d+\.\d*)(?:[eE][-+]?\d+)?$/u;
29
+ /** The text of one line before its comment: a `#` at the line's start or after a blank, outside quotes. */
30
+ function withoutComment(line) {
31
+ let quote;
32
+ let previous = "";
33
+ for (let at = 0; at < line.length; at += 1) {
34
+ const char = line.charAt(at);
35
+ if (quote !== undefined) {
36
+ if (quote === '"' && char === "\\") {
37
+ at += 1;
38
+ }
39
+ else if (char === quote) {
40
+ quote = undefined;
41
+ }
42
+ continue;
43
+ }
44
+ if ((char === "'" || char === '"') && SCALAR_START.has(previous)) {
45
+ quote = char;
46
+ }
47
+ else if (char === "#" && (at === 0 || /\s/u.test(line.charAt(at - 1)))) {
48
+ return line.slice(0, at);
49
+ }
50
+ if (!/\s/u.test(char)) {
51
+ previous = char;
52
+ }
53
+ }
54
+ return line;
55
+ }
56
+ /** The value one scalar's text denotes, quoted or plain. */
57
+ function scalar(text) {
58
+ const value = text.trim();
59
+ if (value.startsWith("'")) {
60
+ if (!value.endsWith("'") || value.length < 2) {
61
+ throw new Unsupported();
62
+ }
63
+ return value.slice(1, -1).replaceAll("''", "'");
64
+ }
65
+ if (value.startsWith('"')) {
66
+ try {
67
+ return JSON.parse(value);
68
+ }
69
+ catch {
70
+ throw new Unsupported();
71
+ }
72
+ }
73
+ if (/^[&*!|>%@`?]/u.test(value) || value === "---" || value === "...") {
74
+ throw new Unsupported();
75
+ }
76
+ if (/:(?:\s|$)/u.test(value)) {
77
+ throw new Unsupported();
78
+ }
79
+ if (CORE.has(value)) {
80
+ return CORE.get(value);
81
+ }
82
+ return NUMBER.test(value) ? Number(value) : value;
83
+ }
84
+ /** Reads one flow collection or flow scalar from `text` at `at`, answering the value and where it ends. */
85
+ function flow(text, start) {
86
+ let at = start;
87
+ const skip = () => {
88
+ while (at < text.length && /\s/u.test(text.charAt(at))) {
89
+ at += 1;
90
+ }
91
+ };
92
+ skip();
93
+ const open = text.charAt(at);
94
+ if (open === "[" || open === "{") {
95
+ const close = open === "[" ? "]" : "}";
96
+ const items = [];
97
+ const object = {};
98
+ at += 1;
99
+ for (;;) {
100
+ skip();
101
+ if (text.charAt(at) === close) {
102
+ return { value: open === "[" ? items : object, end: at + 1 };
103
+ }
104
+ const item = flow(text, at);
105
+ at = item.end;
106
+ skip();
107
+ if (open === "{") {
108
+ if (text.charAt(at) !== ":" || typeof item.value !== "string") {
109
+ throw new Unsupported();
110
+ }
111
+ const held = flow(text, at + 1);
112
+ object[item.value] = held.value;
113
+ at = held.end;
114
+ skip();
115
+ }
116
+ else {
117
+ items.push(item.value);
118
+ }
119
+ if (text.charAt(at) === ",") {
120
+ at += 1;
121
+ }
122
+ else if (text.charAt(at) !== close) {
123
+ throw new Unsupported();
124
+ }
125
+ }
126
+ }
127
+ if (open === "'" || open === '"') {
128
+ let end = at + 1;
129
+ for (; end < text.length; end += 1) {
130
+ const char = text.charAt(end);
131
+ if (open === '"' && char === "\\") {
132
+ end += 1;
133
+ }
134
+ else if (char === open && !(open === "'" && text.charAt(end + 1) === "'")) {
135
+ break;
136
+ }
137
+ else if (char === open) {
138
+ end += 1;
139
+ }
140
+ }
141
+ if (end >= text.length) {
142
+ throw new Unsupported();
143
+ }
144
+ return { value: scalar(text.slice(at, end + 1)), end: end + 1 };
145
+ }
146
+ let end = at;
147
+ while (end < text.length && !",]}".includes(text.charAt(end))) {
148
+ if (text.charAt(end) === ":" && /[\s,\]}]/u.test(text.charAt(end + 1) || " ")) {
149
+ break;
150
+ }
151
+ end += 1;
152
+ }
153
+ return { value: scalar(text.slice(at, end)), end };
154
+ }
155
+ /** A value written on one or more lines of a block, read whole: a flow node, else one plain scalar. */
156
+ function flowValue(text) {
157
+ if (!/^["'[{]/u.test(text)) {
158
+ return scalar(text);
159
+ }
160
+ const read = flow(text, 0);
161
+ if (text.slice(read.end).trim() !== "") {
162
+ throw new Unsupported();
163
+ }
164
+ return read.value;
165
+ }
166
+ /** The position of a mapping key's `:` in one line's content, or -1 where the content is no mapping entry. */
167
+ function keyEnd(text) {
168
+ let quote;
169
+ for (let at = 0; at < text.length; at += 1) {
170
+ const char = text.charAt(at);
171
+ if (quote !== undefined) {
172
+ if (quote === '"' && char === "\\") {
173
+ at += 1;
174
+ }
175
+ else if (char === quote) {
176
+ quote = undefined;
177
+ }
178
+ }
179
+ else if (at === 0 && (char === "'" || char === '"')) {
180
+ quote = char;
181
+ }
182
+ else if (char === "[" || char === "{") {
183
+ return -1;
184
+ }
185
+ else if (char === ":" && (at + 1 === text.length || /\s/u.test(text.charAt(at + 1)))) {
186
+ return at;
187
+ }
188
+ }
189
+ return -1;
190
+ }
191
+ /** How many flow collections one text opens and leaves unclosed, quotes read through. */
192
+ function openCollections(text) {
193
+ let depth = 0;
194
+ let quote;
195
+ for (let at = 0; at < text.length; at += 1) {
196
+ const char = text.charAt(at);
197
+ if (quote !== undefined) {
198
+ if (quote === '"' && char === "\\") {
199
+ at += 1;
200
+ }
201
+ else if (char === quote) {
202
+ quote = undefined;
203
+ }
204
+ }
205
+ else if ((char === "'" || char === '"') &&
206
+ SCALAR_START.has(text.slice(0, at).trim().slice(-1))) {
207
+ quote = char;
208
+ }
209
+ else if (char === "[" || char === "{") {
210
+ depth += 1;
211
+ }
212
+ else if (char === "]" || char === "}") {
213
+ depth -= 1;
214
+ }
215
+ }
216
+ return depth;
217
+ }
218
+ /** Whether one line's content is a block sequence item. */
219
+ function isItem(text) {
220
+ return text === "-" || text.startsWith("- ");
221
+ }
222
+ /** Reads the document's lines as one value. */
223
+ class Reader {
224
+ at = 0;
225
+ lines;
226
+ constructor(lines) {
227
+ this.lines = lines;
228
+ }
229
+ /** The whole document's value. */
230
+ document() {
231
+ const first = this.lines[0];
232
+ if (first === undefined) {
233
+ return null;
234
+ }
235
+ const value = this.block(first.indent);
236
+ if (this.at < this.lines.length) {
237
+ throw new Unsupported();
238
+ }
239
+ return value;
240
+ }
241
+ /** The block node whose first line is the next one, at `indent`. */
242
+ block(indent) {
243
+ const line = this.lines[this.at];
244
+ if (line === undefined) {
245
+ return null;
246
+ }
247
+ if (isItem(line.text)) {
248
+ return this.sequence(indent);
249
+ }
250
+ if (keyEnd(line.text) >= 0) {
251
+ return this.mapping(indent);
252
+ }
253
+ return this.multiline(indent);
254
+ }
255
+ /** A scalar or a flow collection whose text runs over the next lines indented deeper than `indent`. */
256
+ multiline(indent) {
257
+ const parts = [];
258
+ for (let line = this.lines[this.at]; line !== undefined && (parts.length === 0 || line.indent > indent); line = this.lines[this.at]) {
259
+ parts.push(line.text);
260
+ this.at += 1;
261
+ }
262
+ return flowValue(parts.join(" "));
263
+ }
264
+ /** The value after a key or an item marker: inline text, else the deeper block below it. */
265
+ valueAfter(inline, indent, sequenceAtIndent) {
266
+ if (inline !== "") {
267
+ const parts = [inline];
268
+ // A flow collection's closing bracket may stand at the key's own column.
269
+ for (let line = this.lines[this.at]; line !== undefined && (line.indent > indent || openCollections(parts.join(" ")) > 0); line = this.lines[this.at]) {
270
+ parts.push(line.text);
271
+ this.at += 1;
272
+ }
273
+ return flowValue(parts.join(" "));
274
+ }
275
+ const next = this.lines[this.at];
276
+ if (next === undefined) {
277
+ return null;
278
+ }
279
+ if (next.indent > indent || (sequenceAtIndent && next.indent === indent && isItem(next.text))) {
280
+ return this.block(next.indent);
281
+ }
282
+ return null;
283
+ }
284
+ mapping(indent) {
285
+ const object = {};
286
+ for (let line = this.lines[this.at]; line?.indent === indent; line = this.lines[this.at]) {
287
+ const end = keyEnd(line.text);
288
+ if (end < 0) {
289
+ throw new Unsupported();
290
+ }
291
+ const key = scalar(line.text.slice(0, end));
292
+ if (typeof key === "object" && key !== null) {
293
+ throw new Unsupported();
294
+ }
295
+ this.at += 1;
296
+ object[String(key)] = this.valueAfter(line.text.slice(end + 1).trim(), indent, true);
297
+ }
298
+ const left = this.lines[this.at];
299
+ if (left !== undefined && left.indent > indent) {
300
+ throw new Unsupported();
301
+ }
302
+ return object;
303
+ }
304
+ sequence(indent) {
305
+ const items = [];
306
+ for (let line = this.lines[this.at]; line?.indent === indent && isItem(line.text); line = this.lines[this.at]) {
307
+ const content = line.text.slice(1);
308
+ const inline = content.trimStart();
309
+ if (inline !== "" && (isItem(inline) || keyEnd(inline) >= 0)) {
310
+ // A node on the item's own line continues at its own column: read it as a block there.
311
+ const moved = { indent: indent + 1 + content.length - inline.length, text: inline };
312
+ this.lines[this.at] = moved;
313
+ items.push(this.block(moved.indent));
314
+ }
315
+ else {
316
+ this.at += 1;
317
+ items.push(this.valueAfter(inline, indent, false));
318
+ }
319
+ }
320
+ const left = this.lines[this.at];
321
+ if (left !== undefined && left.indent > indent) {
322
+ throw new Unsupported();
323
+ }
324
+ return items;
325
+ }
326
+ }
327
+ /** The JSON value one YAML document's text denotes, or undefined where it lies outside the subset. */
328
+ export function yamlDocument(text) {
329
+ const lines = [];
330
+ for (const raw of text.replace(/^\uFEFF/u, "").split(/\r?\n/u)) {
331
+ if (/^ *\t/u.test(raw)) {
332
+ return undefined;
333
+ }
334
+ const content = withoutComment(raw).trimEnd();
335
+ const trimmed = content.trimStart();
336
+ if (trimmed === "") {
337
+ continue;
338
+ }
339
+ if (trimmed === "---" && lines.length === 0) {
340
+ continue;
341
+ }
342
+ if (trimmed.startsWith("%") || trimmed === "---" || trimmed === "...") {
343
+ return undefined;
344
+ }
345
+ lines.push({ indent: content.length - trimmed.length, text: trimmed });
346
+ }
347
+ try {
348
+ return new Reader(lines).document();
349
+ }
350
+ catch (error) {
351
+ if (error instanceof Unsupported) {
352
+ return undefined;
353
+ }
354
+ throw error;
355
+ }
356
+ }
357
+ /** The key one line of the document's top-level mapping opens, or undefined where it opens none. */
358
+ function topLevelKey(raw) {
359
+ const content = withoutComment(raw).trimEnd();
360
+ const end = /^\s/u.test(content) ? -1 : keyEnd(content);
361
+ if (end < 0) {
362
+ return undefined;
363
+ }
364
+ try {
365
+ const key = scalar(content.slice(0, end));
366
+ return typeof key === "object" && key !== null ? undefined : String(key);
367
+ }
368
+ catch {
369
+ return undefined;
370
+ }
371
+ }
372
+ /**
373
+ * The value of one key of a document's top-level block mapping, read from that key's lines
374
+ * alone, so a document whose other keys lie outside the subset still yields it. A key
375
+ * declared twice, or one whose own lines lie outside the subset, is refused.
376
+ */
377
+ export function yamlKey(text, key) {
378
+ const lines = text.replace(/^\uFEFF/u, "").split(/\r?\n/u);
379
+ const starts = lines.flatMap((line, at) => (topLevelKey(line) === undefined ? [] : [at]));
380
+ const named = starts.filter((at) => topLevelKey(lines[at] ?? "") === key);
381
+ const [start] = named;
382
+ if (start === undefined) {
383
+ return { kind: "absent" };
384
+ }
385
+ if (named.length > 1) {
386
+ return { kind: "refused", reason: `the ${key} key is declared more than once` };
387
+ }
388
+ const end = starts.find((at) => at > start) ?? lines.length;
389
+ const value = yamlDocument(lines.slice(start, end).join("\n"));
390
+ return isRecord(value)
391
+ ? { kind: "value", value: value[key] }
392
+ : { kind: "refused", reason: `the value of ${key} is outside the YAML subset read here` };
393
+ }
@@ -1,177 +1,15 @@
1
1
  /**
2
- * What the errors a project's own files carry do to the run. An import of a module the
3
- * project expects and nothing provides is a setup failure. Any other error in an own file
4
- * is a type error: the function, method or file-level statement holding it is skipped,
5
- * which only ever withholds a finding. `projectErrors` decides, before any of these, which
6
- * errors the compiler places in no file leave the project unloaded.
2
+ * What a type error in a project's own file does: the function, method or file-level statement
3
+ * holding it is skipped, which only ever withholds a finding.
7
4
  */
8
- import { isClassDeclaration, isFunctionLikeDeclaration, isNoSubstitutionTemplateLiteral, isPropertyAccessExpression, isStringLiteral, } from "@typescript/native/ast";
9
- import { packageOfSpecifier } from "./dependencies.js";
5
+ import { isClassDeclaration, isFunctionLikeDeclaration, isPropertyAccessExpression, } from "@typescript/native/ast";
10
6
  import { nodeKey } from "./inventory.js";
11
- import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
7
+ import { relativePath } from "./paths.js";
12
8
  import { isAnswered } from "./query.js";
13
- import { findWorkspace } from "./workspace.js";
14
- /** The compiler's codes for an import whose module it cannot find. */
15
- const MODULE_NOT_FOUND = new Set([2307, 2792]);
16
- const MANIFEST = "package.json";
17
- const MANIFEST_SECTIONS = [
18
- "dependencies",
19
- "devDependencies",
20
- "peerDependencies",
21
- "optionalDependencies",
22
- ];
23
9
  /** Whether a finding's position falls inside a skipped unit. */
24
10
  export function insideSkipped(skipped, position) {
25
11
  return skipped.some((unit) => unit.path === position.path && unit.fromLine <= position.line && position.line <= unit.toLine);
26
12
  }
27
- /** The string literal holding one offset, innermost first, a doc comment's included. */
28
- function literalAt(file, offset) {
29
- let found;
30
- const visit = (node) => {
31
- if (offset < node.pos || offset >= node.end) {
32
- return;
33
- }
34
- if (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node)) {
35
- found = node.text;
36
- }
37
- // `forEachChild` does not enter a doc comment, where a JavaScript file imports types.
38
- for (const doc of node.jsDoc ?? []) {
39
- visit(doc);
40
- }
41
- node.forEachChild(visit);
42
- };
43
- file.forEachChild(visit);
44
- return found;
45
- }
46
- /**
47
- * What one `paths` key's `*` matches in one specifier, `""` for a key with no `*` that
48
- * is the specifier, or `undefined` where the key does not match it.
49
- */
50
- function mappedBy(key, specifier) {
51
- const star = key.indexOf("*");
52
- if (star < 0) {
53
- return key === specifier ? "" : undefined;
54
- }
55
- const prefix = key.slice(0, star);
56
- const suffix = key.slice(star + 1);
57
- return specifier.length >= prefix.length + suffix.length &&
58
- specifier.startsWith(prefix) &&
59
- specifier.endsWith(suffix)
60
- ? specifier.slice(prefix.length, specifier.length - suffix.length)
61
- : undefined;
62
- }
63
- /**
64
- * One `paths` target, its first `*` replaced by `star` taken literally, as the compiler does:
65
- * https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/module/resolver.go#L1282
66
- */
67
- function substituted(target, star) {
68
- const at = target.indexOf("*");
69
- return at < 0 ? target : target.slice(0, at) + star + target.slice(at + 1);
70
- }
71
- /**
72
- * Whether a path names something on disk, as written or with any extension; under
73
- * `fileOnly`, only a file counts.
74
- */
75
- function provided(host, path, fileOnly) {
76
- const counts = (at) => fileOnly ? host.kindOf(at) === "file" : host.kindOf(at) !== "absent";
77
- if (counts(path)) {
78
- return true;
79
- }
80
- const dir = dirnamePath(path);
81
- const stem = `${path.slice(dir.length + 1)}.`;
82
- try {
83
- return host
84
- .readDirectory(dir)
85
- .some((entry) => entry.name.startsWith(stem) && counts(joinPath(dir, entry.name)));
86
- }
87
- catch {
88
- return false;
89
- }
90
- }
91
- /** The manifest at or above `dir`, no higher than the target root, that declares `name`. */
92
- function declaringManifest(host, dir, targetRoot, name) {
93
- for (let at = dir;; at = dirnamePath(at)) {
94
- const path = joinPath(at, MANIFEST);
95
- let manifest;
96
- try {
97
- manifest = JSON.parse(host.readFile(path));
98
- }
99
- catch {
100
- manifest = undefined;
101
- }
102
- if (typeof manifest === "object" && manifest !== null) {
103
- const sections = manifest;
104
- for (const section of MANIFEST_SECTIONS) {
105
- const declared = sections[section];
106
- if (typeof declared === "object" && declared !== null && Object.hasOwn(declared, name)) {
107
- return path;
108
- }
109
- }
110
- }
111
- // The target root is not below itself, so the walk stops after reading its manifest.
112
- if (relativePath(targetRoot, at) === undefined) {
113
- return undefined;
114
- }
115
- }
116
- }
117
- /**
118
- * Whether a package is installed for an importer in `dir`: in the `node_modules` of
119
- * `dir` or of any ancestor, above the target root too, as Node and the compiler look.
120
- */
121
- function installed(host, dir, name) {
122
- for (let at = dir;; at = dirnamePath(at)) {
123
- if (host.kindOf(joinPath(at, "node_modules", name)) === "directory") {
124
- return true;
125
- }
126
- if (dirnamePath(at) === at) {
127
- return false;
128
- }
129
- }
130
- }
131
- /**
132
- * Why one import that the compiler cannot resolve names a module the project expects,
133
- * with the fix, or undefined where the module is one the project does not expect.
134
- */
135
- function expectedModule(context, importer, specifier) {
136
- const { host, targetRoot } = context;
137
- const fix = "run the generator or the build that writes it";
138
- if (specifier.startsWith(".") || isAbsolutePath(specifier)) {
139
- return provided(host, resolvePath(dirnamePath(importer), specifier), false)
140
- ? undefined
141
- : `which names no file of the target: ${fix}`;
142
- }
143
- if (specifier.startsWith("#")) {
144
- return `which the manifest's imports map to no file: ${fix}`;
145
- }
146
- const mapped = context.paths.entries.flatMap(({ key, targets }) => {
147
- const star = mappedBy(key, specifier);
148
- return star === undefined
149
- ? []
150
- : targets.map((target) => resolvePath(context.paths.base, substituted(target, star)));
151
- });
152
- const name = packageOfSpecifier(specifier);
153
- // The compiler looks up in `node_modules` what `paths` does not resolve, so the error
154
- // of an installed package's import is the import's own, a subpath its exports omit:
155
- // https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/module/resolver.go#L548-L575
156
- if (name !== undefined && installed(host, dirnamePath(importer), name)) {
157
- return undefined;
158
- }
159
- if (mapped.length > 0) {
160
- return mapped.some((path) => provided(host, path, true))
161
- ? undefined
162
- : `which the compiler configuration's paths map to no file: ${fix}`;
163
- }
164
- const manifest = name === undefined
165
- ? undefined
166
- : declaringManifest(host, dirnamePath(importer), targetRoot, name);
167
- // No run requires a nested package that is no workspace member to be installed.
168
- if (manifest === undefined ||
169
- (dirnamePath(manifest) !== targetRoot && !context.member(manifest))) {
170
- return undefined;
171
- }
172
- const where = relativePath(targetRoot, manifest) ?? manifest;
173
- return `a dependency ${where} declares that is not installed: run the package manager's install`;
174
- }
175
13
  /**
176
14
  * The unit a type error at one offset skips: the innermost function holding it, else the
177
15
  * class member holding it where a top-level class does, else the top-level statement.
@@ -202,53 +40,13 @@ function messageOf(text, targetRoot) {
202
40
  const line = text.split(`${targetRoot}/`).join("").split(/\s+/u).join(" ").trim();
203
41
  return line === "" ? "a type error" : line;
204
42
  }
205
- /**
206
- * Reads the errors one project's own files carry. A diagnostic marking unnecessary code
207
- * is no error here, and one in a file that is not the project's own is that file's
208
- * package's to answer for.
209
- */
210
- export function readErrors(project, errors, where) {
211
- let workspace = null;
212
- const context = {
213
- ...where,
214
- member: (manifest) => {
215
- if (workspace === null) {
216
- workspace = findWorkspace(where.host, where.targetRoot, () => undefined);
217
- }
218
- return workspace?.memberOf(manifest) !== undefined;
219
- },
220
- };
221
- const files = new Map(project.ownSourceFiles().map((file) => [file.fileName, file]));
222
- const missing = [];
223
- const skips = [];
224
- for (const { diagnostic, at } of errors) {
225
- const file = diagnostic.fileName === undefined ? undefined : files.get(diagnostic.fileName);
226
- if (file === undefined || diagnostic.reportsUnnecessary === true) {
227
- continue;
228
- }
229
- const path = relativePath(context.targetRoot, file.fileName) ?? file.fileName;
230
- const line = at?.line ?? 1;
231
- const specifier = MODULE_NOT_FOUND.has(diagnostic.code)
232
- ? literalAt(file, diagnostic.pos)
233
- : undefined;
234
- const expected = specifier === undefined ? undefined : expectedModule(context, file.fileName, specifier);
235
- if (specifier !== undefined && expected !== undefined) {
236
- missing.push({
237
- setupClass: "missing-module",
238
- detail: `${path}:${String(line)}: ${path} imports ${JSON.stringify(specifier)}, ${expected}`,
239
- });
240
- continue;
241
- }
242
- const unit = unitAt(file, diagnostic.pos);
243
- if (unit !== undefined) {
244
- skips.push({
245
- record: { path, line, message: messageOf(diagnostic.text, context.targetRoot) },
246
- file,
247
- unit,
248
- });
249
- }
250
- }
251
- return { missing, skips };
43
+ /** The skip one type error at `offset` of an own file makes, the root spelled relative to itself. */
44
+ export function skipAt(file, offset, line, text, targetRoot) {
45
+ const unit = unitAt(file, offset);
46
+ const path = relativePath(targetRoot, file.fileName) ?? file.fileName;
47
+ return unit === undefined
48
+ ? undefined
49
+ : { record: { path, line, message: messageOf(text, targetRoot) }, file, unit };
252
50
  }
253
51
  /** The lines each skip withholds, below the target root. */
254
52
  export function skippedUnits(skips, targetRoot) {
@@ -313,22 +111,3 @@ export function skippedRoots(project, held, skips) {
313
111
  }
314
112
  return [...kept];
315
113
  }
316
- /** One compiler configuration's `paths` mapping, empty where it cannot be read. */
317
- export function pathsOf(engine, configFile) {
318
- const base = dirnamePath(configFile);
319
- try {
320
- const { options } = engine.parseConfigFile(configFile);
321
- const { paths } = options;
322
- // The parse answers `pathsBasePath`, the directory of the configuration that writes
323
- // `paths` (an extended one included), though the client's option type omits it:
324
- // https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/tsoptions/tsconfigparsing.go#L1120
325
- const declaredAt = options.pathsBasePath;
326
- return {
327
- base: typeof declaredAt === "string" ? declaredAt : base,
328
- entries: Object.entries(paths ?? {}).map(([key, targets]) => ({ key, targets })),
329
- };
330
- }
331
- catch {
332
- return { base, entries: [] };
333
- }
334
- }