@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,174 @@
1
+ /**
2
+ * The files the import declarations of the target's Markdown and MDX documents load. A
3
+ * document is read as text: a line outside a fenced code block that starts with `import`
4
+ * is an import declaration, which an MDX compiler hoists into the module it builds.
5
+ */
6
+ import { resolveRelativeSpecifier } from "./configuration-files.js";
7
+ import { dirnamePath, joinPath, resolvePath } from "./paths.js";
8
+ import { filesBelow } from "./fs/tree.js";
9
+ /** The extensions of a document whose imports an MDX compiler reads. */
10
+ const DOCUMENT = /\.mdx?$/u;
11
+ /** A fence line, its marker run and what follows it captured. */
12
+ const FENCE = /^ {0,3}(`{3,}|~{3,})(.*)$/u;
13
+ /** A string literal in either quote, its escapes included. */
14
+ const STRING = String.raw `"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'`;
15
+ /** An import declaration at the start of a line, its clause and its specifier captured. */
16
+ const IMPORT_LINE = new RegExp(String.raw `^import\s+(?:((?:[^"'\n]|${STRING})*?)\s+from\s*)?["']([^"'\n]+)["']`, "u");
17
+ /** The braces of an import clause, what they hold captured. */
18
+ const BRACES = new RegExp(String.raw `\{((?:[^}"']|${STRING})*)\}`, "u");
19
+ /** One comma-separated part of a clause's braces. */
20
+ const PART = new RegExp(String.raw `(?:[^,"']|${STRING})+`, "gu");
21
+ /** A named import of a clause's braces: the exported name and an optional local alias. */
22
+ const NAMED_IMPORT = new RegExp(String.raw `^([\w$]+|${STRING})(?:\s+as\s+[\w$]+)?$`, "u");
23
+ /** The single-character escapes of a string literal and the character each stands for. */
24
+ const SINGLE_ESCAPES = new Map([
25
+ ["b", "\b"],
26
+ ["f", "\f"],
27
+ ["n", "\n"],
28
+ ["r", "\r"],
29
+ ["t", "\t"],
30
+ ["v", "\v"],
31
+ ["0", "\0"],
32
+ ["'", "'"],
33
+ ['"', '"'],
34
+ ["\\", "\\"],
35
+ ]);
36
+ /**
37
+ * One escape of a string literal: two hexadecimal digits, a code unit, a code point, or one
38
+ * character and the digit after it.
39
+ */
40
+ const ESCAPE = /\\(?:x([\da-fA-F]{2})|u([\da-fA-F]{4})|u\{([\da-fA-F]+)\}|(.)(\d?))/gu;
41
+ /** What one escape stands for, or undefined for one a module cannot hold. */
42
+ function unescaped([, hex, unit, point, single, digit]) {
43
+ if (single === undefined) {
44
+ const code = Number.parseInt(hex ?? unit ?? point ?? "", 16);
45
+ return code <= 0x10ffff ? String.fromCodePoint(code) : undefined;
46
+ }
47
+ return /[1-9xu]/u.test(single) || (single === "0" && digit !== "")
48
+ ? undefined
49
+ : `${SINGLE_ESCAPES.get(single) ?? single}${digit ?? ""}`;
50
+ }
51
+ /**
52
+ * The value of a quoted export name, its escapes decoded, or undefined for an escape this
53
+ * reading does not decode. A module is strict code, so no legacy octal escape is valid.
54
+ */
55
+ function quotedName(literal) {
56
+ const body = literal.slice(1, -1);
57
+ let value = "";
58
+ let from = 0;
59
+ for (const escape of body.matchAll(ESCAPE)) {
60
+ const decoded = unescaped(escape);
61
+ if (decoded === undefined) {
62
+ return undefined;
63
+ }
64
+ value += body.slice(from, escape.index) + decoded;
65
+ from = escape.index + escape[0].length;
66
+ }
67
+ return value + body.slice(from);
68
+ }
69
+ /**
70
+ * The export names one import clause reads: `default` for a default binding and each name
71
+ * its braces list, or undefined for a namespace import, which reads every export, and for
72
+ * a clause this reading does not parse, which may read any.
73
+ */
74
+ function clauseNames(clause) {
75
+ const braces = BRACES.exec(clause);
76
+ const head = (braces === null ? clause : clause.slice(0, braces.index))
77
+ .replace(/,\s*$/u, "")
78
+ .trim();
79
+ if (head.includes("*")) {
80
+ return undefined;
81
+ }
82
+ const names = head === "" ? [] : ["default"];
83
+ if (head !== "" && !/^[\w$]+$/u.test(head)) {
84
+ return undefined;
85
+ }
86
+ for (const part of (braces?.[1]?.match(PART) ?? []).map((one) => one.trim())) {
87
+ const named = NAMED_IMPORT.exec(part);
88
+ if (part === "") {
89
+ continue;
90
+ }
91
+ if (named?.[1] === undefined) {
92
+ return undefined;
93
+ }
94
+ const name = /^["']/u.test(named[1]) ? quotedName(named[1]) : named[1];
95
+ if (name === undefined) {
96
+ return undefined;
97
+ }
98
+ names.push(name);
99
+ }
100
+ return names;
101
+ }
102
+ /** The relative specifiers one document's import lines name, its fenced code blocks skipped. */
103
+ function importedBy(text) {
104
+ const found = [];
105
+ let open;
106
+ for (const line of text.split(/\r?\n/u)) {
107
+ const fence = FENCE.exec(line);
108
+ const marker = fence?.[1];
109
+ if (marker !== undefined && open === undefined) {
110
+ open = marker;
111
+ continue;
112
+ }
113
+ // https://spec.commonmark.org/0.31.2/#fenced-code-blocks: only a run of the opening
114
+ // character, at least as long and followed by nothing, closes a fence.
115
+ if (marker !== undefined &&
116
+ open !== undefined &&
117
+ marker.startsWith(open.slice(0, 1)) &&
118
+ marker.length >= open.length &&
119
+ fence?.[2]?.trim() === "") {
120
+ open = undefined;
121
+ continue;
122
+ }
123
+ const matched = open === undefined ? IMPORT_LINE.exec(line) : null;
124
+ const specifier = matched?.[2];
125
+ if (specifier?.startsWith("./") === true || specifier?.startsWith("../") === true) {
126
+ found.push({ specifier, names: clauseNames(matched?.[1] ?? "") });
127
+ }
128
+ }
129
+ return found;
130
+ }
131
+ /**
132
+ * The documents each host's target roots hold, which every project of a run reads alike,
133
+ * kept for the host's life as {@link Host} allows.
134
+ */
135
+ const documentsOf = new WeakMap();
136
+ /** Every readable document below `root` and the import lines it writes, read once per host. */
137
+ function documentImports(host, root) {
138
+ const byRoot = documentsOf.get(host) ?? new Map();
139
+ documentsOf.set(host, byRoot);
140
+ const known = byRoot.get(root);
141
+ if (known !== undefined) {
142
+ return known;
143
+ }
144
+ const found = filesBelow(host, root, (name) => DOCUMENT.test(name)).flatMap((below) => {
145
+ const path = joinPath(root, below);
146
+ try {
147
+ return [{ path, imports: importedBy(host.readFile(path)) }];
148
+ }
149
+ catch {
150
+ return [];
151
+ }
152
+ });
153
+ byRoot.set(root, found);
154
+ return found;
155
+ }
156
+ /**
157
+ * Every own file one document below the target root imports by a relative specifier,
158
+ * read against the document's directory, on disk or through the emit mapping.
159
+ */
160
+ export function documentEntries(host, targetRoot, files) {
161
+ const root = resolvePath(host.workingDirectory(), targetRoot);
162
+ const found = [];
163
+ for (const { path, imports } of documentImports(host, root)) {
164
+ const dir = dirnamePath(path);
165
+ for (const { specifier, names } of imports) {
166
+ const reached = resolveRelativeSpecifier(host, dir, specifier);
167
+ const held = reached === undefined ? undefined : files.byName.get(reached);
168
+ for (const file of held === undefined ? files.named(resolvePath(dir, specifier)) : [held]) {
169
+ found.push({ file, source: specifier, names });
170
+ }
171
+ }
172
+ }
173
+ return found;
174
+ }
@@ -103,7 +103,7 @@ function namedBy(held) {
103
103
  `${scope}#${aliasFragment(one, name)}`,
104
104
  ]));
105
105
  const members = (children.get(id) ?? []).filter((child) => wanted.has(child.ref));
106
- return [{ id, members: members.map((child) => child.id) }];
106
+ return [{ id, members: members.map((child) => child.id), wildcard: name.includes("*") }];
107
107
  });
108
108
  }
109
109
  /**
@@ -149,26 +149,22 @@ export function moduleDeclarationReferences(project, held, root, testFiles) {
149
149
  imports.forEach((one, index) => {
150
150
  const resolved = moduleOf(declared.length + index);
151
151
  const position = renderPosition(one.file, root, one.specifier.getStart());
152
- const read = (to, resolution) => {
153
- found.push({
154
- from: one.from,
155
- to,
156
- position,
157
- use: "read",
158
- resolution,
159
- test: testFiles.has(position.path),
160
- });
152
+ const read = (to, resolution, test) => {
153
+ found.push({ from: one.from, to, position, use: "read", resolution, test });
161
154
  };
155
+ const test = testFiles.has(position.path);
162
156
  const same = new Set(declared
163
157
  .filter((_, at) => resolved !== undefined && resolved === moduleOf(at))
164
158
  .map((declaration) => declaration.id));
165
159
  for (const id of same) {
166
- read(id, "batch");
160
+ read(id, "batch", test);
167
161
  }
168
162
  for (const declaration of named(one.specifier.text, one.names)) {
169
- if (!same.has(declaration.id)) {
163
+ // A wildcard declaration is how the compiler types the file a matching specifier
164
+ // names, so the import uses it whoever writes it, a test included.
165
+ if (declaration.wildcard || !same.has(declaration.id)) {
170
166
  for (const to of [declaration.id, ...declaration.members]) {
171
- read(to, "syntax");
167
+ read(to, "syntax", test && !declaration.wildcard);
172
168
  }
173
169
  }
174
170
  }
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { isCallExpression, isExportDeclaration, isImportDeclaration, isNamespaceExport, isNamespaceImport, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
10
10
  import { RESOLVED_EXTENSIONS } from "./configuration-files.js";
11
- import { emittedFrom } from "./emit-map.js";
11
+ import { emittedFrom } from "./load/emit-map.js";
12
12
  import { namesImported } from "./module-declarations.js";
13
13
  import { dirnamePath, joinPath, normalizePath, queriedModulePath } from "./paths.js";
14
14
  import { renderPosition } from "./position.js";
@@ -1,10 +1,9 @@
1
1
  /**
2
- * The overrides of the target's class members. A call through the base class names the
3
- * base's member and runs the override of whichever subclass the value is, so the base
4
- * member's declaration references every override. A method, an accessor or a property,
5
- * static or not, overriding a member of a class declared outside the target, at any depth
6
- * of its `extends` chain, is used by outside code holding the instance or the class
7
- * through that class, so its own class references it.
2
+ * The overrides of the target's class members. A call through the base class runs the
3
+ * override of whichever subclass the value is, so the base member references every
4
+ * override. A member, static or not, overriding one of a class declared outside the target
5
+ * at any depth of its `extends` chain, a library's interface-and-variable class such as
6
+ * `Error` included, is used by outside code holding the value, so its own class references it.
8
7
  */
9
8
  import { isClassDeclaration, isClassExpression, isExpressionWithTypeArguments, isPropertyAccessExpression, isQualifiedName, SyntaxKind, } from "@typescript/native/ast";
10
9
  import { memberComponent } from "./class-members.js";
@@ -85,10 +84,12 @@ export function overrideReferences(project, held, chains, testFiles) {
85
84
  const declared = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
86
85
  if (inTarget.length === 0 &&
87
86
  declared !== UNANSWERED &&
88
- (declared.flags & SymbolFlags.Class) !== 0 &&
89
- // The target's augmentation of an outside class merges an interface into it,
90
- // so only a class declaration says where the class is.
91
- declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) {
87
+ (((declared.flags & SymbolFlags.Class) !== 0 &&
88
+ // The target's augmentation of an outside class merges an interface into it,
89
+ // so only a class declaration says where the class is.
90
+ declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) ||
91
+ ((declared.flags & SymbolFlags.Interface) !== 0 &&
92
+ declared.declarations.some((handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path))))) {
92
93
  outsideBases.set(id, declared);
93
94
  }
94
95
  }
@@ -145,11 +146,13 @@ export function overrideReferences(project, held, chains, testFiles) {
145
146
  */
146
147
  function outsideMemberNames(project, bases, outsideBases) {
147
148
  const direct = new Map();
149
+ const error = errorType(project);
148
150
  for (const [classId, base] of outsideBases) {
149
151
  const instance = project.queries.declaredTypeOf(base);
150
152
  const statics = project.queries.typeOfSymbol(base);
151
153
  const components = [
152
154
  ...namesOf(project, instance === UNANSWERED ? undefined : instance).map(nameComponent),
155
+ ...(instance !== UNANSWERED && reaches(project, instance, error) ? ["toString"] : []),
153
156
  ...namesOf(project, statics === UNANSWERED ? undefined : statics).map((name) => `${nameComponent(name)}:static`),
154
157
  ];
155
158
  if (components.length > 0) {
@@ -175,6 +178,34 @@ function outsideMemberNames(project, bases, outsideBases) {
175
178
  }
176
179
  return names;
177
180
  }
181
+ /** The global `Error` instance type's identifier, where the program's library declares one. */
182
+ function errorType(project) {
183
+ const symbol = project.queries.resolveName("Error", SymbolFlags.Type, undefined, false);
184
+ if (symbol === UNANSWERED || symbol === undefined) {
185
+ return undefined;
186
+ }
187
+ const type = project.queries.declaredTypeOf(symbol);
188
+ return type === UNANSWERED ? undefined : type.id;
189
+ }
190
+ /**
191
+ * Whether one type is the `Error` instance type or extends it at any depth: the runtime
192
+ * prints an error through its `toString`, a member the library's `Error` interface omits.
193
+ */
194
+ function reaches(project, type, target) {
195
+ const seen = new Set();
196
+ const pending = [type];
197
+ for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
198
+ if (next.id === target) {
199
+ return true;
200
+ }
201
+ if (!seen.has(next.id)) {
202
+ seen.add(next.id);
203
+ const bases = project.queries.baseTypes(next);
204
+ pending.push(...(bases === UNANSWERED ? [] : bases));
205
+ }
206
+ }
207
+ return false;
208
+ }
178
209
  /** The names of a type's properties; none where the type or its properties are unanswered. */
179
210
  function namesOf(project, type) {
180
211
  const properties = type === undefined ? UNANSWERED : project.queries.propertiesOf(type);
@@ -1,4 +1,5 @@
1
1
  import { relativePath } from "./paths.js";
2
+ import { bytewise } from "./text/compare.js";
2
3
  /** A count with its noun, so a line reads for one as well as for several. */
3
4
  function counted(n, one, many) {
4
5
  return `${String(n)} ${n === 1 ? one : many}`;
@@ -10,7 +11,7 @@ function counted(n, one, many) {
10
11
  */
11
12
  export function partialNotes(partial) {
12
13
  const notes = [...partial.notBuilt]
13
- .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
14
+ .sort((a, b) => bytewise(a.id, b.id))
14
15
  .map((one) => `the derived configuration ${one.id} was not built and is not analyzed: ${one.error}`);
15
16
  if (partial.unanswered.length > 0) {
16
17
  const byProject = new Map();
package/dist/src/paths.js CHANGED
@@ -1,11 +1,7 @@
1
1
  /**
2
- * Filesystem paths, as the supported platform spells them: a solidus separates
3
- * segments and a leading solidus makes a path absolute.
4
- *
5
- * They are written here rather than taken from the platform's own library because
6
- * every module below the command line stays free of the platform, and because the
7
- * supported platform set is one, so a second spelling would be a rule nothing
8
- * exercises.
2
+ * Filesystem paths as the supported platform spells them: a solidus between segments, a
3
+ * leading one for an absolute path. They are written here rather than taken from the
4
+ * platform's library so every module below the command line stays free of the platform.
9
5
  */
10
6
  const SEPARATOR = "/";
11
7
  /** Whether one path names a place without a directory to resolve it against. */
@@ -61,6 +57,24 @@ export function dirnamePath(path) {
61
57
  }
62
58
  return cut === 0 ? SEPARATOR : normalized.slice(0, cut);
63
59
  }
60
+ /**
61
+ * `dir` and every directory above it, nearest first, normalized. With `top`, the list ends
62
+ * at `top` and is empty where `dir` is neither `top` nor below it; without, it ends at the
63
+ * root of the path.
64
+ */
65
+ export function ancestors(dir, top) {
66
+ const end = top === undefined ? undefined : normalizePath(top);
67
+ const found = [];
68
+ for (let at = normalizePath(dir);; at = dirnamePath(at)) {
69
+ if (end !== undefined && at !== end && relativePath(end, at) === undefined) {
70
+ return [];
71
+ }
72
+ found.push(at);
73
+ if (at === end || dirnamePath(at) === at) {
74
+ return found;
75
+ }
76
+ }
77
+ }
64
78
  /**
65
79
  * The extension of one path's last segment, from its last full stop, or the empty string
66
80
  * where it has none. A segment that only starts with a full stop has none.
@@ -92,14 +106,11 @@ export function queriedModulePath(specifier) {
92
106
  return raw ? undefined : specifier.slice(0, query);
93
107
  }
94
108
  /**
95
- * One path as it reads below a root, or undefined when it is not below the root.
96
- * Both are normalized first, so a root written with a trailing separator or a `.`
97
- * segment reaches the same answer, and the comparison is then lexical, so a root
98
- * and a path spelled through different symbolic links name nothing in common. The
99
- * root itself is not below itself.
100
- *
101
- * The caller resolves both against the same directory before calling: a relative
102
- * root and an absolute path are two spellings this cannot reconcile.
109
+ * One path as it reads below a root, or undefined when it is not strictly below the root:
110
+ * the root itself is not below itself. Both are normalized, then compared lexically, so a
111
+ * root and a path spelled through different symbolic links name nothing in common. The
112
+ * caller resolves both against the same directory first: a relative root and an absolute
113
+ * path cannot be reconciled.
103
114
  */
104
115
  export function relativePath(root, path) {
105
116
  const at = normalizePath(root);
@@ -1,4 +1,5 @@
1
1
  import { relativePath } from "./paths.js";
2
+ import { bytewise } from "./text/compare.js";
2
3
  /**
3
4
  * A file the program holds whose path is not below the target root, which leaves
4
5
  * the analysis unable to name a declaration it found. It ends the run rather than
@@ -58,7 +59,7 @@ export function positionKey(position) {
58
59
  */
59
60
  export function byPosition(a, b) {
60
61
  if (a.path !== b.path) {
61
- return a.path < b.path ? -1 : 1;
62
+ return bytewise(a.path, b.path);
62
63
  }
63
64
  if (a.line !== b.line) {
64
65
  return a.line - b.line;
package/dist/src/print.js CHANGED
@@ -1,82 +1,15 @@
1
- import { renderOrigin, } from "./config.js";
2
- /** One declaration entry as a document writes it: the members of its shape alone. */
3
- function declarationOf(entry) {
4
- switch (entry.shape) {
5
- case "symbol":
6
- return { symbol: entry.symbol };
7
- case "module":
8
- return { module: entry.module, name: entry.name };
9
- case "global":
10
- return { global: entry.global };
11
- }
12
- }
13
- /** One provider entry as a document writes it: the members of its shape alone. */
14
- function providerOf(entry) {
15
- const installed = { name: entry.name, languages: entry.languages, command: entry.command };
16
- if (entry.shape === "installed") {
17
- return installed;
18
- }
19
- return { ...installed, source: entry.source, version: entry.version, digest: entry.digest };
20
- }
1
+ import { renderOrigin } from "./config.js";
2
+ import { bytewise } from "./text/compare.js";
21
3
  /**
22
- * The resolved configuration and its provenance as one JSON object, indented,
23
- * ending in a newline.
24
- *
25
- * The output is an instance of the closed key list, so reading it back as a
26
- * repository configuration resolves to the same configuration: the provenance
27
- * object is a declared key resolution ignores. Keys are written in the schema's
28
- * order and the severity and provenance objects in ascending key order, so one
29
- * configuration renders to one string whatever order a source named its settings
30
- * in.
4
+ * The resolved configuration and its provenance as one JSON object, indented, ending
5
+ * in a newline: the configuration in the order resolution gives it, then the provenance
6
+ * object in ascending key order. Read back as a repository configuration it resolves to
7
+ * the same configuration, because resolution checks the provenance object and then
8
+ * ignores it.
31
9
  */
32
10
  export function printConfig(config, provenance) {
33
- const document = {
34
- contract_version: config.contractVersion,
35
- target: { kind: config.targetKind },
36
- analysis: {
37
- languages: config.analysis.languages,
38
- min_confidence: config.analysis.minConfidence,
39
- generated_files: config.analysis.generatedFiles,
40
- consumer_tests: config.analysis.consumerTests,
41
- configurations: config.analysis.configurations.map((entry) => entry.shape === "project"
42
- ? { id: entry.id, project: entry.project }
43
- : { id: entry.id, os: entry.os, arch: entry.arch, tags: entry.tags }),
44
- matrix: { complete: config.analysis.matrixComplete },
45
- template_dirs: config.analysis.templateDirs,
46
- template_delimiters: {
47
- left: config.analysis.templateDelimiters.left,
48
- right: config.analysis.templateDelimiters.right,
49
- },
50
- },
51
- consumers: { complete: config.consumersComplete },
52
- roots: { patterns: config.rootPatterns },
53
- severity: Object.fromEntries([...config.severity].sort(([a], [b]) => (a < b ? -1 : 1))),
54
- exemptions: { disabled: config.exemptionsDisabled },
55
- reporters: {
56
- formats: config.reporters.formats,
57
- sort: config.reporters.sort,
58
- cascade: config.reporters.cascade,
59
- max_findings: config.reporters.maxFindings,
60
- fail_on: config.reporters.failOn,
61
- },
62
- providers: { analyzers: config.providers.analyzers.map(providerOf) },
63
- go: {},
64
- ts: {
65
- test_files: config.ts.testFiles,
66
- entry_files: config.ts.entryFiles,
67
- component_extensions: config.ts.componentExtensions,
68
- disabled_conventions: config.ts.disabledConventions,
69
- injection_registrations: config.ts.injectionRegistrations.map(declarationOf),
70
- lifecycle_contracts: config.ts.lifecycleContracts.map((entry) => ({
71
- components: entry.components.map(declarationOf),
72
- bases: entry.bases.map(declarationOf),
73
- members: entry.members,
74
- })),
75
- serializers: config.ts.serializers.map(declarationOf),
76
- },
77
- provenance: Object.fromEntries([...provenance]
78
- .sort(([a], [b]) => (a < b ? -1 : 1))
79
- .map(([path, origin]) => [path, renderOrigin(origin)])),
80
- };
81
- return `${JSON.stringify(document, undefined, 2)}\n`;
11
+ const origins = [...provenance]
12
+ .sort(([a], [b]) => bytewise(a, b))
13
+ .map(([path, origin]) => [path, renderOrigin(origin)]);
14
+ return `${JSON.stringify({ ...config, provenance: Object.fromEntries(origins) }, undefined, 2)}\n`;
82
15
  }
@@ -0,0 +1,13 @@
1
+ import { LANGUAGE_ROWS } from "./contract/contract.generated.js";
2
+ /** The language this analyzer reads. */
3
+ export const LANGUAGE = "ts";
4
+ /** The analyzer's name: the one its language's row names, which every output spells it with. */
5
+ export const PRODUCT = LANGUAGE_ROWS[LANGUAGE].analyzer;
6
+ /** Writes one diagnostic line to the error stream, named for the analyzer. */
7
+ export function diagnose(err, text) {
8
+ err.write(`${PRODUCT}: ${text}\n`);
9
+ }
10
+ /** The text one thrown value carries. */
11
+ export function message(error) {
12
+ return error instanceof Error ? error.message : String(error);
13
+ }
package/dist/src/query.js CHANGED
@@ -220,6 +220,7 @@ export function queriesOf(checker, configFile, batch, log, ask = ASK_DIRECTLY) {
220
220
  symbolOfType: (type) => one("getSymbol", () => typeLocation(type), () => type.getSymbol()),
221
221
  signaturesOf: (type, kind) => one("getSignaturesOfType", () => typeLocation(type), () => checker.getSignaturesOfType(type, kind)),
222
222
  resolvedSignature: (node) => one("getResolvedSignature", () => nodeLocation(node), () => checker.getResolvedSignature(node)),
223
+ resolvedSignatures: (nodes, cap = DEFAULT_BATCH_CAP) => each("getResolvedSignature", nodes, cap, nodeLocation, (node) => checker.getResolvedSignature.gen(node), (node) => checker.getResolvedSignature(node)),
223
224
  parameterType: (signature, index) => one("getParameterType", () => `signature ${String(signature.id)} parameter ${String(index)}`, () => checker.getParameterType(signature, index)),
224
225
  genericOf: (signature) => signature.target === undefined
225
226
  ? undefined
package/dist/src/ref.js CHANGED
@@ -45,9 +45,12 @@ export const REF_EXPRESSIONS = [
45
45
  `^${SCOPE}#$`,
46
46
  `^${SCOPE}#${TS_HEAD}$`,
47
47
  `^${SCOPE}#(?:${TS_QUOTED}\\.)?${TS_HEAD}:alias$`,
48
+ `^${SCOPE}#${TS_HEAD}:import$`,
48
49
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+$`,
49
50
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+:static$`,
51
+ `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+\\.${TS_QUOTED}:case$`,
50
52
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})*(?::static)?<${TS_IDENT}>$`,
53
+ `^${SCOPE}#(?:${TS_HEAD}(?:\\.${TS_MEMBER})*(?::static)?)?\\(${TS_IDENT}\\)$`,
51
54
  `^${MANIFEST_SCOPE}#${TS_PACKAGE}:(?:dependency|dev-dependency|peer-dependency)$`,
52
55
  ];
53
56
  const REF_FORMS = REF_EXPRESSIONS.map((expression) => new RegExp(expression, "u"));
@@ -86,6 +89,14 @@ function quoted(name) {
86
89
  export function computedComponent(text) {
87
90
  return `[${text.replaceAll(ASCII_WHITESPACE, "")}]`;
88
91
  }
92
+ /** The reference of an import binding, read against the reference of the module that holds it. */
93
+ export function importRef(moduleRef, name) {
94
+ return `${moduleRef}${nameComponent(name)}:import`;
95
+ }
96
+ /** The reference of a parameter, read against the reference of the declaration that holds it. */
97
+ export function parameterRef(holderRef, name) {
98
+ return `${holderRef}(${name})`;
99
+ }
89
100
  /** The fragment of an export alias, inside the `declare module` block named, if one is. */
90
101
  export function aliasFragment(name, block) {
91
102
  const head = block === undefined ? "" : `${quoted(block)}.`;