@cplieger/deadset-ts 8.0.0-dev.6 → 8.0.0-dev.8

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 (193) hide show
  1. package/dist/bin/mapper-connection.js +2 -1
  2. package/dist/bin/node-host.js +24 -5
  3. package/dist/src/analysis.js +37 -56
  4. package/dist/src/automatic-types.js +10 -29
  5. package/dist/src/build-scripts.js +17 -29
  6. package/dist/src/component-layer.js +4 -2
  7. package/dist/src/components.js +2 -3
  8. package/dist/src/config.js +6 -82
  9. package/dist/src/configuration-files.js +27 -168
  10. package/dist/src/configured-declarations.js +15 -20
  11. package/dist/src/consumers.js +2 -2
  12. package/dist/src/contract/config.generated.js +544 -0
  13. package/dist/src/contract/contract.generated.js +13 -0
  14. package/dist/src/contract/report.generated.js +3 -2
  15. package/dist/src/conventions.js +20 -60
  16. package/dist/src/dependencies.js +25 -43
  17. package/dist/src/discover.js +14 -40
  18. package/dist/src/edges.js +38 -122
  19. package/dist/src/element-access.js +4 -6
  20. package/dist/src/entry-point-gaps.js +2 -8
  21. package/dist/src/entry-points.js +3 -10
  22. package/dist/src/enum-group.js +3 -10
  23. package/dist/src/erased-stores.js +4 -6
  24. package/dist/src/exempt-classes.js +0 -4
  25. package/dist/src/exempt.js +6 -22
  26. package/dist/src/file-facts.js +2 -25
  27. package/dist/src/findings/completion.js +5 -4
  28. package/dist/src/findings/emitters.js +1 -1
  29. package/dist/src/findings/intra-function.js +1 -1
  30. package/dist/src/findings/non-code-artifacts.js +2 -2
  31. package/dist/src/findings/reads-and-writes.js +2 -1
  32. package/dist/src/findings/self-check.js +3 -2
  33. package/dist/src/findings/unused-declarations.js +1 -1
  34. package/dist/src/findings/visibility-narrowing.js +2 -2
  35. package/dist/src/findings-pass.js +7 -12
  36. package/dist/src/framework-lifecycle.js +7 -7
  37. package/dist/src/fs/document.js +59 -0
  38. package/dist/src/fs/manifest.js +43 -0
  39. package/dist/src/fs/tree.js +88 -0
  40. package/dist/src/git-ignore.js +34 -25
  41. package/dist/src/html-entries.js +6 -33
  42. package/dist/src/in-operands.js +2 -1
  43. package/dist/src/injection-container.js +1 -1
  44. package/dist/src/intra-function-parts.js +10 -13
  45. package/dist/src/inventory.js +2 -21
  46. package/dist/src/json-document.js +198 -67
  47. package/dist/src/keyof-comparisons.js +4 -6
  48. package/dist/src/kinds.js +0 -7
  49. package/dist/src/manifest.js +5 -43
  50. package/dist/src/mdx-entries.js +3 -26
  51. package/dist/src/partial.js +2 -1
  52. package/dist/src/paths.js +26 -15
  53. package/dist/src/position.js +2 -1
  54. package/dist/src/print.js +11 -79
  55. package/dist/src/product.js +13 -0
  56. package/dist/src/references.js +14 -27
  57. package/dist/src/report-schema.js +3 -225
  58. package/dist/src/report.js +22 -31
  59. package/dist/src/reporters/sarif.js +4 -2
  60. package/dist/src/reporters/template.js +4 -19
  61. package/dist/src/resolve.js +245 -724
  62. package/dist/src/roots.js +7 -17
  63. package/dist/src/run.js +55 -61
  64. package/dist/src/schema.js +320 -343
  65. package/dist/src/scope.js +34 -103
  66. package/dist/src/serialization-contract.js +5 -9
  67. package/dist/src/session.js +2 -1
  68. package/dist/src/suppress-file.js +21 -25
  69. package/dist/src/suppress.js +3 -3
  70. package/dist/src/symbol-keys.js +3 -5
  71. package/dist/src/template-field.js +9 -19
  72. package/dist/src/test-globals.js +6 -28
  73. package/dist/src/test-joins.js +6 -6
  74. package/dist/src/text/compare.js +26 -0
  75. package/dist/src/text/glob.js +194 -0
  76. package/dist/src/text/shell.js +159 -0
  77. package/dist/src/{yaml-document.js → text/yaml.js} +38 -0
  78. package/dist/src/type-errors.js +14 -38
  79. package/dist/src/unseen-loads.js +2 -9
  80. package/dist/src/unwrap.js +23 -0
  81. package/dist/src/usage.js +10 -0
  82. package/dist/src/verbs/analyze.js +25 -26
  83. package/dist/src/verbs/describe.js +4 -4
  84. package/dist/src/verbs/explain.js +8 -6
  85. package/dist/src/verbs/print-projects.js +4 -3
  86. package/dist/src/verbs/print-retained.js +2 -1
  87. package/dist/src/verbs/print-roots.js +2 -1
  88. package/dist/src/verbs/version.js +2 -1
  89. package/dist/src/workflow-steps.js +9 -46
  90. package/dist/src/workspace-manifests.js +2 -2
  91. package/dist/src/workspace-programs.js +5 -3
  92. package/dist/src/workspace-resolution.js +13 -17
  93. package/dist/src/workspace.js +28 -116
  94. package/jsr.json +1 -1
  95. package/package.json +1 -1
  96. package/src/analysis.ts +55 -76
  97. package/src/automatic-types.ts +11 -29
  98. package/src/build-scripts.ts +17 -38
  99. package/src/component-layer.ts +4 -2
  100. package/src/components.ts +2 -4
  101. package/src/config.ts +31 -296
  102. package/src/configuration-files.ts +27 -170
  103. package/src/configured-declarations.ts +18 -23
  104. package/src/consumers.ts +4 -4
  105. package/src/contract/config.generated.ts +798 -0
  106. package/src/contract/contract.generated.ts +25 -0
  107. package/src/contract/report.generated.ts +3 -2
  108. package/src/conventions.ts +26 -74
  109. package/src/dependencies.ts +32 -50
  110. package/src/discover.ts +15 -40
  111. package/src/edges.ts +41 -140
  112. package/src/element-access.ts +4 -7
  113. package/src/entry-point-gaps.ts +3 -9
  114. package/src/entry-points.ts +2 -13
  115. package/src/enum-group.ts +2 -11
  116. package/src/erased-stores.ts +3 -7
  117. package/src/exempt-classes.ts +0 -5
  118. package/src/exempt.ts +8 -30
  119. package/src/file-facts.ts +6 -24
  120. package/src/findings/completion.ts +5 -6
  121. package/src/findings/emitter.ts +2 -2
  122. package/src/findings/emitters.ts +1 -1
  123. package/src/findings/intra-function.ts +1 -1
  124. package/src/findings/non-code-artifacts.ts +4 -4
  125. package/src/findings/reads-and-writes.ts +2 -1
  126. package/src/findings/self-check.ts +3 -2
  127. package/src/findings/unused-declarations.ts +1 -1
  128. package/src/findings/visibility-narrowing.ts +2 -2
  129. package/src/findings-pass.ts +9 -15
  130. package/src/framework-lifecycle.ts +7 -7
  131. package/src/fs/document.ts +74 -0
  132. package/src/fs/manifest.ts +52 -0
  133. package/src/fs/tree.ts +110 -0
  134. package/src/git-ignore.ts +38 -25
  135. package/src/host.ts +12 -5
  136. package/src/html-entries.ts +6 -33
  137. package/src/in-operands.ts +2 -1
  138. package/src/injection-container.ts +1 -1
  139. package/src/intra-function-parts.ts +10 -14
  140. package/src/inventory.ts +2 -20
  141. package/src/json-document.ts +241 -76
  142. package/src/keyof-comparisons.ts +3 -7
  143. package/src/kinds.ts +0 -8
  144. package/src/manifest.ts +5 -46
  145. package/src/mdx-entries.ts +3 -26
  146. package/src/partial.ts +2 -1
  147. package/src/paths.ts +27 -15
  148. package/src/position.ts +2 -1
  149. package/src/print.ts +12 -92
  150. package/src/product.ts +18 -0
  151. package/src/references.ts +14 -29
  152. package/src/report-schema.ts +3 -301
  153. package/src/report.ts +24 -34
  154. package/src/reporters/sarif.ts +4 -4
  155. package/src/reporters/template.ts +4 -20
  156. package/src/resolve.ts +283 -1114
  157. package/src/roots.ts +7 -20
  158. package/src/run.ts +57 -75
  159. package/src/schema.ts +391 -388
  160. package/src/scope.ts +40 -126
  161. package/src/serialization-contract.ts +5 -10
  162. package/src/session.ts +2 -1
  163. package/src/suppress-file.ts +21 -29
  164. package/src/suppress.ts +3 -3
  165. package/src/symbol-keys.ts +2 -5
  166. package/src/template-field.ts +15 -25
  167. package/src/test-globals.ts +9 -27
  168. package/src/test-joins.ts +6 -8
  169. package/src/text/compare.ts +27 -0
  170. package/src/text/glob.ts +210 -0
  171. package/src/text/shell.ts +161 -0
  172. package/src/{yaml-document.ts → text/yaml.ts} +46 -0
  173. package/src/type-errors.ts +25 -36
  174. package/src/unseen-loads.ts +1 -12
  175. package/src/unwrap.ts +34 -0
  176. package/src/usage.ts +10 -0
  177. package/src/verbs/analyze.ts +32 -42
  178. package/src/verbs/describe.ts +4 -4
  179. package/src/verbs/explain.ts +8 -10
  180. package/src/verbs/print-projects.ts +4 -3
  181. package/src/verbs/print-retained.ts +2 -1
  182. package/src/verbs/print-roots.ts +2 -1
  183. package/src/verbs/verb.ts +2 -2
  184. package/src/verbs/version.ts +2 -1
  185. package/src/workflow-steps.ts +9 -44
  186. package/src/workspace-manifests.ts +2 -2
  187. package/src/workspace-programs.ts +5 -3
  188. package/src/workspace-resolution.ts +21 -17
  189. package/src/workspace.ts +35 -121
  190. package/dist/src/glob.js +0 -65
  191. package/dist/src/pnpm-workspace.js +0 -120
  192. package/src/glob.ts +0 -67
  193. package/src/pnpm-workspace.ts +0 -135
@@ -6,42 +6,30 @@
6
6
  * member is the workspace root or a directory the patterns name that holds a named
7
7
  * manifest, never one below `node_modules`.
8
8
  */
9
- import { DiscoveryError } from "./discover.js";
9
+ import { DiscoveryError, isConfigFile } from "./discover.js";
10
10
  import { commonDirectory } from "./emit-map.js";
11
- import { dirnamePath, joinPath, relativePath } from "./paths.js";
12
- import { pnpmPackages } from "./pnpm-workspace.js";
13
- const MANIFEST = "package.json";
11
+ import { ancestors, dirnamePath, joinPath, relativePath } from "./paths.js";
12
+ import { bytewise } from "./text/compare.js";
13
+ import { isRecord } from "./json-document.js";
14
+ import { directoriesIn, filesIn, repositoryOf } from "./fs/tree.js";
15
+ import { holdsManifest, inInstalledPackages, INSTALLED_PACKAGES, MANIFEST, manifestAt, } from "./fs/manifest.js";
16
+ import { braceExpanded, pathMatches, segmentMatches } from "./text/glob.js";
17
+ import { yamlKey } from "./text/yaml.js";
14
18
  const PNPM_WORKSPACE = "pnpm-workspace.yaml";
15
- const CONFIG_FILE = /^tsconfig.*\.json$/u;
16
- function isRecord(value) {
17
- return typeof value === "object" && value !== null && !Array.isArray(value);
18
- }
19
- /** One manifest, decoded, or `undefined` where there is none to read. */
20
- function manifestAt(host, dir) {
21
- let text;
22
- try {
23
- text = host.readFile(joinPath(dir, MANIFEST));
24
- }
25
- catch {
26
- return undefined;
27
- }
28
- try {
29
- const value = JSON.parse(text);
30
- return isRecord(value) ? value : undefined;
31
- }
32
- catch {
33
- return undefined;
34
- }
35
- }
36
19
  /** The patterns one directory declares a workspace with, or `undefined` where it declares none. */
37
20
  function declarationAt(host, dir) {
38
21
  const pnpm = joinPath(dir, PNPM_WORKSPACE);
39
22
  if (host.kindOf(pnpm) === "file") {
40
- const read = pnpmPackages(host.readFile(pnpm));
41
- if (read.kind === "refused") {
42
- throw new DiscoveryError(`${pnpm}: ${read.reason}; the workspace's packages are read from a sequence of strings`, []);
23
+ const read = yamlKey(host.readFile(pnpm), "packages");
24
+ if (read.kind === "absent") {
25
+ return { file: pnpm, patterns: [] };
26
+ }
27
+ const listed = read.kind === "value" ? read.value : undefined;
28
+ if (!Array.isArray(listed) || !listed.every((entry) => typeof entry === "string")) {
29
+ const reason = read.kind === "refused" ? read.reason : "packages is not a sequence of strings";
30
+ throw new DiscoveryError(`${pnpm}: ${reason}; the workspace's packages are read from a sequence of strings`, []);
43
31
  }
44
- return { file: pnpm, patterns: read.kind === "listed" ? read.patterns : [] };
32
+ return { file: pnpm, patterns: listed };
45
33
  }
46
34
  const manifest = manifestAt(host, dir);
47
35
  if (manifest === undefined || !Object.hasOwn(manifest, "workspaces")) {
@@ -62,20 +50,6 @@ function declaresWorkspace(host, dir) {
62
50
  const manifest = manifestAt(host, dir);
63
51
  return manifest !== undefined && Object.hasOwn(manifest, "workspaces");
64
52
  }
65
- /** A pattern with every brace list expanded into its alternatives. */
66
- function braceExpanded(pattern) {
67
- const open = pattern.indexOf("{");
68
- const close = open < 0 ? -1 : pattern.indexOf("}", open);
69
- if (close < 0) {
70
- return [pattern];
71
- }
72
- const before = pattern.slice(0, open);
73
- const after = pattern.slice(close + 1);
74
- return pattern
75
- .slice(open + 1, close)
76
- .split(",")
77
- .flatMap((member) => braceExpanded(before + member + after));
78
- }
79
53
  /**
80
54
  * The segments of one pattern below the workspace root, `.` and empty segments
81
55
  * dropped and `..` taken back, or `undefined` where it leaves the root.
@@ -96,52 +70,6 @@ function segmentsOf(pattern) {
96
70
  }
97
71
  return segments;
98
72
  }
99
- /**
100
- * Whether one directory name matches one pattern segment: `*` stands for any run of
101
- * characters and `?` for one, and neither matches a leading dot.
102
- */
103
- function segmentMatches(segment, name) {
104
- if (name.startsWith(".") && !segment.startsWith(".")) {
105
- return false;
106
- }
107
- const source = Array.from(segment)
108
- .map((character) => {
109
- if (character === "*") {
110
- return ".*";
111
- }
112
- if (character === "?") {
113
- return ".";
114
- }
115
- return character.replaceAll(/[.+^${}()|[\]\\/]/gu, (c) => `\\${c}`);
116
- })
117
- .join("");
118
- return new RegExp(`^${source}$`, "u").test(name);
119
- }
120
- /** Whether one path's segments match one pattern's, `**` standing for any run of segments. */
121
- function pathMatches(pattern, path) {
122
- const [head, ...rest] = pattern;
123
- if (head === undefined) {
124
- return path.length === 0;
125
- }
126
- if (head === "**") {
127
- return (pathMatches(rest, path) ||
128
- (path.length > 0 && !(path[0] ?? "").startsWith(".") && pathMatches(pattern, path.slice(1))));
129
- }
130
- const [name, ...below] = path;
131
- return name !== undefined && segmentMatches(head, name) && pathMatches(rest, below);
132
- }
133
- function subdirectories(host, dir) {
134
- try {
135
- return host
136
- .readDirectory(dir)
137
- .filter((entry) => entry.directory && entry.name !== "node_modules" && entry.name !== ".git")
138
- .map((entry) => entry.name)
139
- .sort();
140
- }
141
- catch {
142
- return [];
143
- }
144
- }
145
73
  /**
146
74
  * Every directory below `dir` one pattern names. `**` does not descend into a
147
75
  * directory that declares a workspace of its own, whose packages are that
@@ -155,7 +83,7 @@ function expand(host, dir, pattern, into) {
155
83
  }
156
84
  if (head === "**") {
157
85
  expand(host, dir, rest, into);
158
- for (const name of subdirectories(host, dir)) {
86
+ for (const name of directoriesIn(host, dir)) {
159
87
  if (name.startsWith(".")) {
160
88
  continue;
161
89
  }
@@ -166,12 +94,12 @@ function expand(host, dir, pattern, into) {
166
94
  }
167
95
  if (!head.includes("*") && !head.includes("?")) {
168
96
  const child = joinPath(dir, head);
169
- if (head !== "node_modules" && host.kindOf(child) === "directory") {
97
+ if (head !== INSTALLED_PACKAGES && host.kindOf(child) === "directory") {
170
98
  expand(host, child, rest, into);
171
99
  }
172
100
  return;
173
101
  }
174
- for (const name of subdirectories(host, dir)) {
102
+ for (const name of directoriesIn(host, dir)) {
175
103
  if (segmentMatches(head, name)) {
176
104
  expand(host, joinPath(dir, name), rest, into);
177
105
  }
@@ -208,10 +136,10 @@ function namesDirectory(root, patterns, dir) {
208
136
  if (below === undefined) {
209
137
  return false;
210
138
  }
211
- const path = below.split("/");
212
- if (path.includes("node_modules")) {
139
+ if (inInstalledPackages(below)) {
213
140
  return false;
214
141
  }
142
+ const path = below.split("/");
215
143
  let named = false;
216
144
  for (const written of patterns) {
217
145
  const negated = written.startsWith("!");
@@ -238,18 +166,7 @@ function layoutOf(configFile, parsed) {
238
166
  return { outDir: parsed.outDir, declarationDir: parsed.declarationDir, sourceDirs };
239
167
  }
240
168
  function configurationsOf(host, dir, parse) {
241
- let names;
242
- try {
243
- names = host
244
- .readDirectory(dir)
245
- .filter((entry) => !entry.directory && CONFIG_FILE.test(entry.name))
246
- .map((entry) => entry.name)
247
- .sort();
248
- }
249
- catch {
250
- return [];
251
- }
252
- return names.flatMap((name) => {
169
+ return filesIn(host, dir, isConfigFile).flatMap((name) => {
253
170
  const configFile = joinPath(dir, name);
254
171
  const parsed = parse(configFile);
255
172
  return parsed === undefined
@@ -267,22 +184,17 @@ function configurationsOf(host, dir, parse) {
267
184
  * highest directory below it, at or above the target root, that holds a manifest.
268
185
  */
269
186
  function nearestDeclaration(host, targetRoot) {
270
- let dir = targetRoot;
271
187
  let highestPackage;
272
- for (;;) {
188
+ for (const dir of ancestors(targetRoot, repositoryOf(host, targetRoot)?.root)) {
273
189
  const declared = declarationAt(host, dir);
274
190
  if (declared !== undefined) {
275
191
  return { root: dir, ...declared, highestPackage };
276
192
  }
277
- if (host.kindOf(joinPath(dir, MANIFEST)) === "file") {
193
+ if (holdsManifest(host, dir)) {
278
194
  highestPackage = dir;
279
195
  }
280
- const up = dirnamePath(dir);
281
- if (up === dir || host.kindOf(joinPath(dir, ".git")) !== "absent") {
282
- return undefined;
283
- }
284
- dir = up;
285
196
  }
197
+ return undefined;
286
198
  }
287
199
  /**
288
200
  * The workspace the target sits in, or `undefined` where none holds it: no directory
@@ -320,7 +232,7 @@ export function findWorkspace(host, targetRoot, parse) {
320
232
  if (members.length < 2) {
321
233
  return undefined;
322
234
  }
323
- members.sort((a, b) => a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : a.dir < b.dir ? -1 : 1);
235
+ members.sort((a, b) => a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : bytewise(a.dir, b.dir));
324
236
  const named = new Map();
325
237
  for (const member of members) {
326
238
  named.set(member.name, [...(named.get(member.name) ?? []), member]);
@@ -332,7 +244,7 @@ export function findWorkspace(host, targetRoot, parse) {
332
244
  return scopes.get(dir);
333
245
  }
334
246
  const up = dirnamePath(dir);
335
- const found = host.kindOf(joinPath(dir, MANIFEST)) === "file" ? dir : up === dir ? undefined : scopeOf(up);
247
+ const found = holdsManifest(host, dir) ? dir : up === dir ? undefined : scopeOf(up);
336
248
  scopes.set(dir, found);
337
249
  return found;
338
250
  };
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.6",
3
+ "version": "8.0.0-dev.8",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.6",
3
+ "version": "8.0.0-dev.8",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Deterministic dead-code analysis for TypeScript, class and type members included",
package/src/analysis.ts CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { isStringLiteralLikeNode, type SourceFile } from "@typescript/native/ast";
10
10
  import { lineOf, readComponent } from "./component-files.ts";
11
- import { kindOf, type Config, type Provenance } from "./config.ts";
11
+ import { kindOf, type PartialConfig, type Provenance, type ResolvedConfig } from "./config.ts";
12
12
  import {
13
13
  consumerLoads,
14
14
  consumerReferences,
@@ -62,7 +62,6 @@ import {
62
62
  } from "./exempt.ts";
63
63
  import type { TSExemptionClass } from "./exempt-classes.ts";
64
64
  import { heldByInclusion, sourcePaths, sourceTree } from "./file-facts.ts";
65
- import { globExpression } from "./glob.ts";
66
65
  import type { Finding } from "./finding.ts";
67
66
  import { findingsPass, recordedFindings, rowKey, type PassResult } from "./findings-pass.ts";
68
67
  import type { EmitterInput } from "./findings/emitter.ts";
@@ -85,6 +84,7 @@ import {
85
84
  type SweepResult,
86
85
  } from "./matrix.ts";
87
86
  import {
87
+ ancestors,
88
88
  dirnamePath,
89
89
  extensionOf,
90
90
  joinPath,
@@ -94,7 +94,7 @@ import {
94
94
  } from "./paths.ts";
95
95
  import type { UnansweredQuestion } from "./query.ts";
96
96
  import { byPosition, isComponentFile, type Position } from "./position.ts";
97
- import { LANGUAGE, type TypeErrorSkip, type UnansweredCount } from "./report.ts";
97
+ import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
98
98
  import { SetupError, setupLine, type SetupFailure } from "./setup-failure.ts";
99
99
  import {
100
100
  pathsOf,
@@ -136,6 +136,9 @@ import { inlineDirectives, inlineSuppressions, type InlineDirective } from "./su
136
136
  import type { Exemption, Mode, SweepInput } from "./sweep.ts";
137
137
  import { readTemplates, templateField } from "./template-field.ts";
138
138
  import { ownErrors, workspaceRun } from "./workspace-run.ts";
139
+ import { bytewise } from "./text/compare.ts";
140
+ import { holdsManifest, inInstalledPackages, MANIFEST, manifestAt } from "./fs/manifest.ts";
141
+ import { globExpression } from "./text/glob.ts";
139
142
 
140
143
  /** The setting whose source decides where a finding about a configured root sits. */
141
144
  const ROOTS_SETTING = "roots.patterns";
@@ -192,14 +195,6 @@ function documentOf(
192
195
  return relativePath(resolvePath(host.workingDirectory(), targetRoot), document) ?? "";
193
196
  }
194
197
 
195
- /** Two strings ordered bytewise. */
196
- function compare(a: string, b: string): number {
197
- if (a === b) {
198
- return 0;
199
- }
200
- return a < b ? -1 : 1;
201
- }
202
-
203
198
  /** What every stage after the root set reads of one project. */
204
199
  interface ProjectRead {
205
200
  readonly configuration: string;
@@ -272,8 +267,8 @@ function conventionManifests(
272
267
  nestedDirs: readonly string[],
273
268
  ): readonly ConventionManifest[] {
274
269
  const found = new Map<string, ConventionManifest>();
275
- if (host.kindOf(joinPath(targetRoot, "package.json")) === "file") {
276
- found.set(targetRoot, { dir: targetRoot, path: "package.json" });
270
+ if (holdsManifest(host, targetRoot)) {
271
+ found.set(targetRoot, { dir: targetRoot, path: MANIFEST });
277
272
  }
278
273
  for (const [dirs, nested] of [
279
274
  [memberDirs, false],
@@ -303,23 +298,18 @@ function nestedPackageDirs(
303
298
  const seen = new Set<string>();
304
299
  const found: string[] = [];
305
300
  for (const file of files) {
306
- for (
307
- let dir = dirnamePath(file);
308
- dir !== targetRoot && !seen.has(dir) && relativePath(targetRoot, dir) !== undefined;
309
- dir = dirnamePath(dir)
310
- ) {
301
+ for (const dir of ancestors(dirnamePath(file), targetRoot)) {
302
+ if (seen.has(dir) || relativePath(targetRoot, dir) === undefined) {
303
+ break;
304
+ }
311
305
  seen.add(dir);
312
306
  const below = relativePath(targetRoot, dir) ?? "";
313
- if (
314
- !below.split("/").includes("node_modules") &&
315
- !known.has(dir) &&
316
- host.kindOf(joinPath(dir, "package.json")) === "file"
317
- ) {
307
+ if (!inInstalledPackages(below) && !known.has(dir) && holdsManifest(host, dir)) {
318
308
  found.push(dir);
319
309
  }
320
310
  }
321
311
  }
322
- return found.sort(compare);
312
+ return found.sort(bytewise);
323
313
  }
324
314
 
325
315
  /**
@@ -334,7 +324,7 @@ function readProjects<Answer>(
334
324
  engine: Engine,
335
325
  host: Host,
336
326
  scope: Scope,
337
- config: Config,
327
+ config: PartialConfig,
338
328
  stage: <Brand>(project: ProjectView<Brand>, opened: OpenedProject) => Answer,
339
329
  ): ReadProjects<Answer> {
340
330
  const targetRoot = scope.target.path;
@@ -365,7 +355,7 @@ function readProjects<Answer>(
365
355
  // The nested packages are known only once every program is open, and every visit reads
366
356
  // the conventions and options they decide, so both are set in the session's first step.
367
357
  let nestedDirs: readonly string[] = [];
368
- let conventions = readConventions(host, parse, [], config.ts.disabledConventions);
358
+ let conventions = readConventions(host, parse, [], config.ts.disabled_conventions);
369
359
  let options: RootOptions & { readonly parse: typeof parse; readonly conventions: Conventions };
370
360
  const readManifests = (opened: readonly OpenedFiles[]): void => {
371
361
  nestedDirs = nestedPackageDirs(
@@ -380,7 +370,7 @@ function readProjects<Answer>(
380
370
  host,
381
371
  parse,
382
372
  conventionManifests(host, absoluteRoot, memberDirs, nestedDirs),
383
- config.ts.disabledConventions,
373
+ config.ts.disabled_conventions,
384
374
  );
385
375
  options = {
386
376
  host,
@@ -389,12 +379,12 @@ function readProjects<Answer>(
389
379
  ...(workspace?.entries ?? []),
390
380
  ...packageEntries(host, normalizePath(absoluteRoot), nestedDirs),
391
381
  ],
392
- patterns: config.rootPatterns,
393
- entryFiles: config.ts.entryFiles,
382
+ patterns: config.roots.patterns,
383
+ entryFiles: config.ts.entry_files,
394
384
  conventions,
395
- testFiles: config.ts.testFiles,
385
+ testFiles: config.ts.test_files,
396
386
  parse,
397
- publishedAPI: config.targetKind !== "" && kindOf(config.targetKind, LANGUAGE) === "library",
387
+ publishedAPI: config.target.kind !== undefined && kindOf(config.target.kind) === "library",
398
388
  publishedMembers: new Set(
399
389
  (workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir),
400
390
  ),
@@ -429,7 +419,7 @@ function readProjects<Answer>(
429
419
  unbuildable.push(...met);
430
420
  }
431
421
  };
432
- const testPatterns = config.ts.testFiles.map(globExpression);
422
+ const testPatterns = config.ts.test_files.map(globExpression);
433
423
  const joins = discovered.derived
434
424
  ? joinedTestFiles(engine, host, absoluteRoot, discovered.configFiles, testPatterns)
435
425
  : new Map<string, readonly string[]>();
@@ -666,7 +656,12 @@ interface RunProjects {
666
656
  * for the errors and setup failures that decide whether it is analyzed, dropped or
667
657
  * fails the run, exactly as the analysis reads them.
668
658
  */
669
- export function runProjects(engine: Engine, host: Host, scope: Scope, config: Config): RunProjects {
659
+ export function runProjects(
660
+ engine: Engine,
661
+ host: Host,
662
+ scope: Scope,
663
+ config: PartialConfig,
664
+ ): RunProjects {
670
665
  const read = readProjects(
671
666
  engine,
672
667
  host,
@@ -687,7 +682,7 @@ export function runRoots(
687
682
  engine: Engine,
688
683
  host: Host,
689
684
  scope: Scope,
690
- config: Config,
685
+ config: ResolvedConfig,
691
686
  provenance: Provenance,
692
687
  ): RunRoots {
693
688
  const targetRoot = scope.target.path;
@@ -723,10 +718,12 @@ export function runRoots(
723
718
 
724
719
  const ordered = [...merged.values()].sort(
725
720
  (a, b) =>
726
- byPosition(a.position, b.position) || compare(a.kind, b.kind) || compare(a.source, b.source),
721
+ byPosition(a.position, b.position) ||
722
+ bytewise(a.kind, b.kind) ||
723
+ bytewise(a.source, b.source),
727
724
  );
728
725
  const unmatched = unmatchedEverywhere(
729
- config.rootPatterns,
726
+ config.roots.patterns,
730
727
  projects.map((answer) => answer.rooted),
731
728
  );
732
729
  return {
@@ -872,20 +869,9 @@ function testEvidence(
872
869
  }
873
870
  }
874
871
  const manifests = new Map<string, Readonly<Record<string, unknown>> | undefined>();
875
- const manifestAt = (dir: string): Readonly<Record<string, unknown>> | undefined => {
872
+ const manifestIn = (dir: string): Readonly<Record<string, unknown>> | undefined => {
876
873
  if (!manifests.has(dir)) {
877
- let value: unknown;
878
- try {
879
- value = JSON.parse(host.readFile(joinPath(dir, "package.json")));
880
- } catch {
881
- value = undefined;
882
- }
883
- manifests.set(
884
- dir,
885
- typeof value === "object" && value !== null && !Array.isArray(value)
886
- ? (value as Record<string, unknown>)
887
- : undefined,
888
- );
874
+ manifests.set(dir, manifestAt(host, dir));
889
875
  }
890
876
  return manifests.get(dir);
891
877
  };
@@ -895,16 +881,14 @@ function testEvidence(
895
881
  return {
896
882
  imports,
897
883
  devDependency: (path, name) => {
898
- for (let dir = dirnamePath(joinPath(root, path)); ; dir = dirnamePath(dir)) {
899
- const manifest = manifestAt(dir);
884
+ for (const dir of ancestors(dirnamePath(joinPath(root, path)), root)) {
885
+ const manifest = manifestIn(dir);
900
886
  const other = OTHER_SECTIONS.some((one) => declares(manifest?.[one], name));
901
887
  if (declares(manifest?.[DEV_SECTION], name) || other) {
902
888
  return !other;
903
889
  }
904
- if (normalizePath(dir) === normalizePath(root) || dirnamePath(dir) === dir) {
905
- return false;
906
- }
907
890
  }
891
+ return false;
908
892
  },
909
893
  };
910
894
  }
@@ -919,7 +903,7 @@ function readRun<Extra>(
919
903
  engine: Engine,
920
904
  host: Host,
921
905
  scope: Scope,
922
- config: Config,
906
+ config: ResolvedConfig,
923
907
  mode: Mode,
924
908
  detectors: Detectors,
925
909
  extra: <Brand>(
@@ -932,13 +916,13 @@ function readRun<Extra>(
932
916
  const targetRoot = scope.target.path;
933
917
  const disabled = disabledClasses(config);
934
918
  const templates = disabled.has("template-field")
935
- ? { delimiters: config.analysis.templateDelimiters, files: [] }
919
+ ? { delimiters: config.analysis.template_delimiters, files: [] }
936
920
  : readTemplates(host, targetRoot, config.analysis);
937
921
  const consumers = scope.consumers.map((consumer) => consumer.path);
938
922
  const read = readProjects<ReadProject<Extra>>(engine, host, scope, config, (project, opened) => {
939
923
  const projectRead = opened.read();
940
924
  const resolved = references(project, projectRead.held, targetRoot, {
941
- testFiles: config.ts.testFiles,
925
+ testFiles: config.ts.test_files,
942
926
  testFileNames: projectRead.testFileNames,
943
927
  });
944
928
  const evidence = evidenceRecords(
@@ -949,7 +933,7 @@ function readRun<Extra>(
949
933
  templates,
950
934
  ts: config.ts,
951
935
  consumers,
952
- ...(config.analysis.generatedFiles === "include"
936
+ ...(config.analysis.generated_files === "include"
953
937
  ? {}
954
938
  : { generated: projectRead.generated }),
955
939
  },
@@ -1056,8 +1040,8 @@ function readRun<Extra>(
1056
1040
  const exempt = detected.records;
1057
1041
  const made = supportReferences([...one.references, ...(satisfied?.references ?? [])], support);
1058
1042
  const held = new Set(projectRead.held.symbols.map((symbol) => symbol.position.path));
1059
- const supportFiles = [...support].filter((path) => held.has(path)).sort();
1060
- const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort();
1043
+ const supportFiles = [...support].filter((path) => held.has(path)).sort(bytewise);
1044
+ const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort(bytewise);
1061
1045
  const configured: Configured = {
1062
1046
  configuration: projectRead.configuration,
1063
1047
  symbols: projectRead.held.symbols,
@@ -1285,7 +1269,7 @@ function sweptOf<Extra>(
1285
1269
  unanswered: read.unanswered,
1286
1270
  heldByUnanswered: [...held],
1287
1271
  skipped: read.projects.flatMap((one) => one.skipped),
1288
- componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(compare),
1272
+ componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise),
1289
1273
  };
1290
1274
  }
1291
1275
 
@@ -1301,7 +1285,7 @@ export function runSweep(
1301
1285
  engine: Engine,
1302
1286
  host: Host,
1303
1287
  scope: Scope,
1304
- config: Config,
1288
+ config: ResolvedConfig,
1305
1289
  input: Pick<SweepInput, "marked" | "mode">,
1306
1290
  detectors: Detectors = DETECTORS,
1307
1291
  ): RunSweep {
@@ -1344,7 +1328,7 @@ export function readEmitterRun(
1344
1328
  engine: Engine,
1345
1329
  host: Host,
1346
1330
  scope: Scope,
1347
- config: Config,
1331
+ config: ResolvedConfig,
1348
1332
  mode: Mode,
1349
1333
  detectors: Detectors,
1350
1334
  ): ReadRun<EmitterExtra> {
@@ -1427,12 +1411,7 @@ function judgedMembers(
1427
1411
 
1428
1412
  /** The directory of the nearest `package.json` at or above a file, inside `root`, else `root`. */
1429
1413
  function nearestManifestDir(host: Host, root: string, file: string): string {
1430
- for (let dir = dirnamePath(file); relativePath(root, dir) !== undefined; dir = dirnamePath(dir)) {
1431
- if (host.kindOf(joinPath(dir, "package.json")) === "file") {
1432
- return dir;
1433
- }
1434
- }
1435
- return root;
1414
+ return ancestors(dirnamePath(file), root).find((dir) => holdsManifest(host, dir)) ?? root;
1436
1415
  }
1437
1416
 
1438
1417
  /**
@@ -1442,7 +1421,7 @@ function nearestManifestDir(host: Host, root: string, file: string): string {
1442
1421
  export function emitterInputOver(
1443
1422
  host: Host,
1444
1423
  scope: Scope,
1445
- config: Config,
1424
+ config: ResolvedConfig,
1446
1425
  read: ReadRun<EmitterExtra>,
1447
1426
  input: Pick<SweepInput, "marked" | "mode">,
1448
1427
  selfCheck: SelfCheckFacts,
@@ -1530,7 +1509,7 @@ function readAnalysis(
1530
1509
  engine: Engine,
1531
1510
  host: Host,
1532
1511
  scope: Scope,
1533
- config: Config,
1512
+ config: ResolvedConfig,
1534
1513
  provenance: Provenance,
1535
1514
  mode: Mode,
1536
1515
  detectors: Detectors,
@@ -1551,7 +1530,7 @@ function readAnalysis(
1551
1530
  configFile: one.configFile,
1552
1531
  })),
1553
1532
  testFileRules: testFileRulesOf(
1554
- config.ts.testFiles,
1533
+ config.ts.test_files,
1555
1534
  read.projects.flatMap((one) => one.configured.testFiles),
1556
1535
  read.projects.flatMap((one) => one.configured.supportFiles ?? []),
1557
1536
  ),
@@ -1582,7 +1561,7 @@ function readAnalysis(
1582
1561
  function inputsOver(
1583
1562
  host: Host,
1584
1563
  scope: Scope,
1585
- config: Config,
1564
+ config: ResolvedConfig,
1586
1565
  provenance: Provenance,
1587
1566
  mode: Mode,
1588
1567
  read: ReadRun<EmitterExtra>,
@@ -1596,7 +1575,7 @@ function inputsOver(
1596
1575
  const facts: SelfCheckFacts = {
1597
1576
  refusals: suppressions.refusals,
1598
1577
  unmatchedRoots: unmatchedEverywhere(
1599
- config.rootPatterns,
1578
+ config.roots.patterns,
1600
1579
  read.projects.map((one) => one.extra.rooted),
1601
1580
  ),
1602
1581
  rootsDocument: documentOf(host, provenance, ROOTS_SETTING, targetRoot),
@@ -1706,7 +1685,7 @@ export function runAnalysis(
1706
1685
  engine: Engine,
1707
1686
  host: Host,
1708
1687
  scope: Scope,
1709
- config: Config,
1688
+ config: ResolvedConfig,
1710
1689
  provenance: Provenance,
1711
1690
  mode: Mode,
1712
1691
  detectors: Detectors = DETECTORS,
@@ -1762,7 +1741,7 @@ export function runBaselineWrite(
1762
1741
  engine: Engine,
1763
1742
  host: Host,
1764
1743
  scope: Scope,
1765
- config: Config,
1744
+ config: ResolvedConfig,
1766
1745
  provenance: Provenance,
1767
1746
  mode: Mode,
1768
1747
  reason: string,