@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
@@ -1,43 +1,22 @@
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
5
 
9
6
  import {
10
7
  isClassDeclaration,
11
8
  isFunctionLikeDeclaration,
12
- isNoSubstitutionTemplateLiteral,
13
9
  isPropertyAccessExpression,
14
- isStringLiteral,
15
10
  type Node,
16
11
  type PropertyAccessExpression,
17
12
  type SourceFile,
18
13
  } from "@typescript/native/ast";
19
14
  import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
20
- import { packageOfSpecifier } from "./dependencies.ts";
21
- import type { LocatedDiagnostic } from "./discover.ts";
22
- import type { Host } from "./host.ts";
23
15
  import { nodeKey, type Inventory } from "./inventory.ts";
24
- import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.ts";
16
+ import { relativePath } from "./paths.ts";
25
17
  import { isAnswered } from "./query.ts";
26
18
  import type { TypeErrorSkip } from "./report.ts";
27
- import type { Engine, ProjectView } from "./session.ts";
28
- import type { SetupFailure } from "./setup-failure.ts";
29
- import { findWorkspace, type Workspace } from "./workspace.ts";
30
-
31
- /** The compiler's codes for an import whose module it cannot find. */
32
- const MODULE_NOT_FOUND: ReadonlySet<number> = new Set([2307, 2792]);
33
-
34
- const MANIFEST = "package.json";
35
- const MANIFEST_SECTIONS = [
36
- "dependencies",
37
- "devDependencies",
38
- "peerDependencies",
39
- "optionalDependencies",
40
- ] as const;
19
+ import type { ProjectView } from "./session.ts";
41
20
 
42
21
  /** The lines of one file a skip withholds every finding on. */
43
22
  export interface SkippedUnit {
@@ -59,203 +38,12 @@ export function insideSkipped(
59
38
  }
60
39
 
61
40
  /** One type error of an own file, with the unit it skips. */
62
- interface Skip {
41
+ export interface Skip {
63
42
  readonly record: TypeErrorSkip;
64
43
  readonly file: SourceFile;
65
44
  readonly unit: Node;
66
45
  }
67
46
 
68
- /** What the errors of one project's own files are. */
69
- interface ProjectErrorReading {
70
- /** The imports of a module the project expects that nothing provides, one failure each. */
71
- readonly missing: readonly SetupFailure[];
72
- /** The type errors, each with the unit it skips. */
73
- readonly skips: readonly Skip[];
74
- }
75
-
76
- /** Where one project reads what its imports may name. */
77
- interface ErrorContext {
78
- readonly host: Host;
79
- /** The target root, absolute. */
80
- readonly targetRoot: string;
81
- /** The project configuration's `paths` mapping. */
82
- readonly paths: PathsMapping;
83
- /** Whether one manifest is a workspace member's, whose dependencies the workspace installs. */
84
- readonly member: (manifest: string) => boolean;
85
- }
86
-
87
- /** One configuration's `paths` mapping, its targets read against `base`. */
88
- interface PathsMapping {
89
- readonly base: string;
90
- readonly entries: readonly { readonly key: string; readonly targets: readonly string[] }[];
91
- }
92
-
93
- /** The string literal holding one offset, innermost first, a doc comment's included. */
94
- function literalAt(file: SourceFile, offset: number): string | undefined {
95
- let found: string | undefined;
96
- const visit = (node: Node): void => {
97
- if (offset < node.pos || offset >= node.end) {
98
- return;
99
- }
100
- if (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node)) {
101
- found = node.text;
102
- }
103
- // `forEachChild` does not enter a doc comment, where a JavaScript file imports types.
104
- for (const doc of (node as { readonly jsDoc?: readonly Node[] }).jsDoc ?? []) {
105
- visit(doc);
106
- }
107
- node.forEachChild(visit);
108
- };
109
- file.forEachChild(visit);
110
- return found;
111
- }
112
-
113
- /**
114
- * What one `paths` key's `*` matches in one specifier, `""` for a key with no `*` that
115
- * is the specifier, or `undefined` where the key does not match it.
116
- */
117
- function mappedBy(key: string, specifier: string): string | undefined {
118
- const star = key.indexOf("*");
119
- if (star < 0) {
120
- return key === specifier ? "" : undefined;
121
- }
122
- const prefix = key.slice(0, star);
123
- const suffix = key.slice(star + 1);
124
- return specifier.length >= prefix.length + suffix.length &&
125
- specifier.startsWith(prefix) &&
126
- specifier.endsWith(suffix)
127
- ? specifier.slice(prefix.length, specifier.length - suffix.length)
128
- : undefined;
129
- }
130
-
131
- /**
132
- * One `paths` target, its first `*` replaced by `star` taken literally, as the compiler does:
133
- * https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/module/resolver.go#L1282
134
- */
135
- function substituted(target: string, star: string): string {
136
- const at = target.indexOf("*");
137
- return at < 0 ? target : target.slice(0, at) + star + target.slice(at + 1);
138
- }
139
-
140
- /**
141
- * Whether a path names something on disk, as written or with any extension; under
142
- * `fileOnly`, only a file counts.
143
- */
144
- function provided(host: Host, path: string, fileOnly: boolean): boolean {
145
- const counts = (at: string): boolean =>
146
- fileOnly ? host.kindOf(at) === "file" : host.kindOf(at) !== "absent";
147
- if (counts(path)) {
148
- return true;
149
- }
150
- const dir = dirnamePath(path);
151
- const stem = `${path.slice(dir.length + 1)}.`;
152
- try {
153
- return host
154
- .readDirectory(dir)
155
- .some((entry) => entry.name.startsWith(stem) && counts(joinPath(dir, entry.name)));
156
- } catch {
157
- return false;
158
- }
159
- }
160
-
161
- /** The manifest at or above `dir`, no higher than the target root, that declares `name`. */
162
- function declaringManifest(
163
- host: Host,
164
- dir: string,
165
- targetRoot: string,
166
- name: string,
167
- ): string | undefined {
168
- for (let at = dir; ; at = dirnamePath(at)) {
169
- const path = joinPath(at, MANIFEST);
170
- let manifest: unknown;
171
- try {
172
- manifest = JSON.parse(host.readFile(path)) as unknown;
173
- } catch {
174
- manifest = undefined;
175
- }
176
- if (typeof manifest === "object" && manifest !== null) {
177
- const sections = manifest as Record<string, unknown>;
178
- for (const section of MANIFEST_SECTIONS) {
179
- const declared = sections[section];
180
- if (typeof declared === "object" && declared !== null && Object.hasOwn(declared, name)) {
181
- return path;
182
- }
183
- }
184
- }
185
- // The target root is not below itself, so the walk stops after reading its manifest.
186
- if (relativePath(targetRoot, at) === undefined) {
187
- return undefined;
188
- }
189
- }
190
- }
191
-
192
- /**
193
- * Whether a package is installed for an importer in `dir`: in the `node_modules` of
194
- * `dir` or of any ancestor, above the target root too, as Node and the compiler look.
195
- */
196
- function installed(host: Host, dir: string, name: string): boolean {
197
- for (let at = dir; ; at = dirnamePath(at)) {
198
- if (host.kindOf(joinPath(at, "node_modules", name)) === "directory") {
199
- return true;
200
- }
201
- if (dirnamePath(at) === at) {
202
- return false;
203
- }
204
- }
205
- }
206
-
207
- /**
208
- * Why one import that the compiler cannot resolve names a module the project expects,
209
- * with the fix, or undefined where the module is one the project does not expect.
210
- */
211
- function expectedModule(
212
- context: ErrorContext,
213
- importer: string,
214
- specifier: string,
215
- ): string | undefined {
216
- const { host, targetRoot } = context;
217
- const fix = "run the generator or the build that writes it";
218
- if (specifier.startsWith(".") || isAbsolutePath(specifier)) {
219
- return provided(host, resolvePath(dirnamePath(importer), specifier), false)
220
- ? undefined
221
- : `which names no file of the target: ${fix}`;
222
- }
223
- if (specifier.startsWith("#")) {
224
- return `which the manifest's imports map to no file: ${fix}`;
225
- }
226
- const mapped = context.paths.entries.flatMap(({ key, targets }) => {
227
- const star = mappedBy(key, specifier);
228
- return star === undefined
229
- ? []
230
- : targets.map((target) => resolvePath(context.paths.base, substituted(target, star)));
231
- });
232
- const name = packageOfSpecifier(specifier);
233
- // The compiler looks up in `node_modules` what `paths` does not resolve, so the error
234
- // of an installed package's import is the import's own, a subpath its exports omit:
235
- // https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/module/resolver.go#L548-L575
236
- if (name !== undefined && installed(host, dirnamePath(importer), name)) {
237
- return undefined;
238
- }
239
- if (mapped.length > 0) {
240
- return mapped.some((path) => provided(host, path, true))
241
- ? undefined
242
- : `which the compiler configuration's paths map to no file: ${fix}`;
243
- }
244
- const manifest =
245
- name === undefined
246
- ? undefined
247
- : declaringManifest(host, dirnamePath(importer), targetRoot, name);
248
- // No run requires a nested package that is no workspace member to be installed.
249
- if (
250
- manifest === undefined ||
251
- (dirnamePath(manifest) !== targetRoot && !context.member(manifest))
252
- ) {
253
- return undefined;
254
- }
255
- const where = relativePath(targetRoot, manifest) ?? manifest;
256
- return `a dependency ${where} declares that is not installed: run the package manager's install`;
257
- }
258
-
259
47
  /**
260
48
  * The unit a type error at one offset skips: the innermost function holding it, else the
261
49
  * class member holding it where a top-level class does, else the top-level statement.
@@ -288,58 +76,19 @@ function messageOf(text: string, targetRoot: string): string {
288
76
  return line === "" ? "a type error" : line;
289
77
  }
290
78
 
291
- /**
292
- * Reads the errors one project's own files carry. A diagnostic marking unnecessary code
293
- * is no error here, and one in a file that is not the project's own is that file's
294
- * package's to answer for.
295
- */
296
- export function readErrors<Brand>(
297
- project: ProjectView<Brand>,
298
- errors: readonly LocatedDiagnostic[],
299
- where: Omit<ErrorContext, "member">,
300
- ): ProjectErrorReading {
301
- let workspace: Workspace | undefined | null = null;
302
- const context: ErrorContext = {
303
- ...where,
304
- member: (manifest) => {
305
- if (workspace === null) {
306
- workspace = findWorkspace(where.host, where.targetRoot, () => undefined);
307
- }
308
- return workspace?.memberOf(manifest) !== undefined;
309
- },
310
- };
311
- const files = new Map(project.ownSourceFiles().map((file) => [file.fileName, file]));
312
- const missing: SetupFailure[] = [];
313
- const skips: Skip[] = [];
314
- for (const { diagnostic, at } of errors) {
315
- const file = diagnostic.fileName === undefined ? undefined : files.get(diagnostic.fileName);
316
- if (file === undefined || diagnostic.reportsUnnecessary === true) {
317
- continue;
318
- }
319
- const path = relativePath(context.targetRoot, file.fileName) ?? file.fileName;
320
- const line = at?.line ?? 1;
321
- const specifier = MODULE_NOT_FOUND.has(diagnostic.code)
322
- ? literalAt(file, diagnostic.pos)
323
- : undefined;
324
- const expected =
325
- specifier === undefined ? undefined : expectedModule(context, file.fileName, specifier);
326
- if (specifier !== undefined && expected !== undefined) {
327
- missing.push({
328
- setupClass: "missing-module",
329
- detail: `${path}:${String(line)}: ${path} imports ${JSON.stringify(specifier)}, ${expected}`,
330
- });
331
- continue;
332
- }
333
- const unit = unitAt(file, diagnostic.pos);
334
- if (unit !== undefined) {
335
- skips.push({
336
- record: { path, line, message: messageOf(diagnostic.text, context.targetRoot) },
337
- file,
338
- unit,
339
- });
340
- }
341
- }
342
- return { missing, skips };
79
+ /** The skip one type error at `offset` of an own file makes, the root spelled relative to itself. */
80
+ export function skipAt(
81
+ file: SourceFile,
82
+ offset: number,
83
+ line: number,
84
+ text: string,
85
+ targetRoot: string,
86
+ ): Skip | undefined {
87
+ const unit = unitAt(file, offset);
88
+ const path = relativePath(targetRoot, file.fileName) ?? file.fileName;
89
+ return unit === undefined
90
+ ? undefined
91
+ : { record: { path, line, message: messageOf(text, targetRoot) }, file, unit };
343
92
  }
344
93
 
345
94
  /** The lines each skip withholds, below the target root. */
@@ -411,22 +160,3 @@ export function skippedRoots<Brand>(
411
160
  }
412
161
  return [...kept];
413
162
  }
414
-
415
- /** One compiler configuration's `paths` mapping, empty where it cannot be read. */
416
- export function pathsOf(engine: Engine, configFile: string): PathsMapping {
417
- const base = dirnamePath(configFile);
418
- try {
419
- const { options } = engine.parseConfigFile(configFile);
420
- const { paths } = options;
421
- // The parse answers `pathsBasePath`, the directory of the configuration that writes
422
- // `paths` (an extended one included), though the client's option type omits it:
423
- // https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/tsoptions/tsconfigparsing.go#L1120
424
- const declaredAt = (options as { readonly pathsBasePath?: unknown }).pathsBasePath;
425
- return {
426
- base: typeof declaredAt === "string" ? declaredAt : base,
427
- entries: Object.entries(paths ?? {}).map(([key, targets]) => ({ key, targets })),
428
- };
429
- } catch {
430
- return { base, entries: [] };
431
- }
432
- }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The loads of a project's production files whose target the analysis cannot resolve: an
3
+ * `import()` or `require()` call whose path is computed and starts with no relative literal
4
+ * naming a directory below the calling file's. The files such a load runs are roots the
5
+ * analysis does not see, so the configuration's roots are never complete.
6
+ */
7
+
8
+ import {
9
+ isBinaryExpression,
10
+ isCallExpression,
11
+ isIdentifier,
12
+ isNoSubstitutionTemplateLiteral,
13
+ isStringLiteral,
14
+ isTemplateExpression,
15
+ SyntaxKind,
16
+ type Node,
17
+ } from "@typescript/native/ast";
18
+ import { dirnamePath, relativePath, resolvePath } from "./paths.ts";
19
+ import { renderPosition, type Position } from "./position.ts";
20
+ import { UNANSWERED } from "./query.ts";
21
+ import type { ProjectView } from "./session.ts";
22
+ import { sourceFilesOf } from "./source-files.ts";
23
+ import { unwrapped } from "./unwrap.ts";
24
+
25
+ /** One load the analysis cannot resolve, in the configuration whose production file writes it. */
26
+ export interface UnseenLoad {
27
+ readonly configuration: string;
28
+ /** The position of the call. */
29
+ readonly position: Position;
30
+ }
31
+
32
+ /** The literal text a computed path starts with: a template's head, or a concatenation's first literal. */
33
+ export function leadingLiteral(node: Node): string | undefined {
34
+ const at = unwrapped(node);
35
+ if (isTemplateExpression(at)) {
36
+ return at.head.text;
37
+ }
38
+ if (isBinaryExpression(at) && at.operatorToken.kind === SyntaxKind.PlusToken) {
39
+ const left = unwrapped(at.left);
40
+ return isStringLiteral(left) || isNoSubstitutionTemplateLiteral(left)
41
+ ? left.text
42
+ : leadingLiteral(at.left);
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ /**
48
+ * The directory below `dir` a computed load's leading literal names, which the entry
49
+ * rules root every file of, or none where it names a package, `dir` itself or a
50
+ * directory holding it, each of which would name files no rule roots.
51
+ */
52
+ export function computedLoadDirectory(dir: string, prefix: string): string | undefined {
53
+ if (!(prefix.startsWith("./") || prefix.startsWith("../")) || !prefix.endsWith("/")) {
54
+ return undefined;
55
+ }
56
+ const target = resolvePath(dir, prefix);
57
+ return target === dir || relativePath(target, dir) !== undefined ? undefined : target;
58
+ }
59
+
60
+ /** Whether a computed path's leading literal names a package, whose files are none of the target's. */
61
+ function namesPackage(prefix: string | undefined): boolean {
62
+ return (
63
+ prefix !== undefined &&
64
+ prefix !== "" &&
65
+ !prefix.startsWith(".") &&
66
+ !prefix.startsWith("/") &&
67
+ !/^[A-Za-z][A-Za-z\d+.-]*:/u.test(prefix)
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Every unresolved load the project's production files write, in file then source order.
73
+ * `testFiles` holds the test files by their path below the target root. A `require` call
74
+ * loads a module only where the name resolves to no declaration of the project's own
75
+ * files: one the project declares is its own function.
76
+ */
77
+ export function unseenLoadsOf<Brand>(
78
+ project: ProjectView<Brand>,
79
+ configuration: string,
80
+ targetRoot: string,
81
+ testFiles: readonly string[],
82
+ ): readonly UnseenLoad[] {
83
+ const tests = new Set(testFiles);
84
+ const sites: { readonly position: Position; readonly require: Node | undefined }[] = [];
85
+ for (const [path, file] of sourceFilesOf(project, targetRoot).byPath) {
86
+ if (tests.has(path) || file.isDeclarationFile) {
87
+ continue;
88
+ }
89
+ const dir = dirnamePath(file.fileName);
90
+ const visit = (node: Node): void => {
91
+ if (isCallExpression(node)) {
92
+ const callee = node.expression;
93
+ const required = isIdentifier(callee) && callee.text === "require";
94
+ const [first] = node.arguments;
95
+ const argument = first === undefined ? undefined : unwrapped(first);
96
+ if (
97
+ (callee.kind === SyntaxKind.ImportKeyword || required) &&
98
+ argument !== undefined &&
99
+ !isStringLiteral(argument) &&
100
+ !isNoSubstitutionTemplateLiteral(argument)
101
+ ) {
102
+ const prefix = leadingLiteral(argument);
103
+ const seen =
104
+ namesPackage(prefix) ||
105
+ (prefix !== undefined && computedLoadDirectory(dir, prefix) !== undefined);
106
+ if (!seen) {
107
+ sites.push({
108
+ position: renderPosition(file, targetRoot, node.getStart()),
109
+ require: required ? callee : undefined,
110
+ });
111
+ }
112
+ }
113
+ }
114
+ node.forEachChild(visit);
115
+ };
116
+ file.forEachChild(visit);
117
+ }
118
+ const requires = sites.flatMap((site) => (site.require === undefined ? [] : [site.require]));
119
+ const own = project.ownPaths();
120
+ const local = new Set<Node>();
121
+ project.symbolsAt(requires.map((node) => project.handle(node))).forEach((symbol, at) => {
122
+ const node = requires[at];
123
+ if (
124
+ node !== undefined &&
125
+ symbol !== UNANSWERED &&
126
+ symbol?.declarations.some((handle) => own.has(handle.path)) === true
127
+ ) {
128
+ local.add(node);
129
+ }
130
+ });
131
+ return sites
132
+ .filter((site) => site.require === undefined || !local.has(site.require))
133
+ .map((site) => ({ configuration, position: site.position }));
134
+ }
package/src/unwrap.ts ADDED
@@ -0,0 +1,34 @@
1
+ import {
2
+ isAsExpression,
3
+ isNonNullExpression,
4
+ isParenthesizedExpression,
5
+ isSatisfiesExpression,
6
+ type Expression,
7
+ type Node,
8
+ } from "@typescript/native/ast";
9
+
10
+ /** The expression inside every pair of parentheses around `node`. */
11
+ export function parenthesesRemoved<T extends Node>(node: T): T | Expression {
12
+ let at: T | Expression = node;
13
+ while (isParenthesizedExpression(at)) {
14
+ at = at.expression;
15
+ }
16
+ return at;
17
+ }
18
+
19
+ /**
20
+ * The expression inside every wrapper around `node` that changes no value it evaluates to:
21
+ * parentheses, `as` and `satisfies` assertions and the non-null assertion.
22
+ */
23
+ export function unwrapped<T extends Node>(node: T): T | Expression {
24
+ let at: T | Expression = node;
25
+ while (
26
+ isParenthesizedExpression(at) ||
27
+ isAsExpression(at) ||
28
+ isSatisfiesExpression(at) ||
29
+ isNonNullExpression(at)
30
+ ) {
31
+ at = at.expression;
32
+ }
33
+ return at;
34
+ }
package/src/usage.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A refusal of the invocation: the fix is to the command line or to a document it
3
+ * names, so the run ends with the usage code before any analysis.
4
+ */
5
+ export class UsageError extends Error {
6
+ constructor(message: string) {
7
+ super(message);
8
+ this.name = "UsageError";
9
+ }
10
+ }