@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
package/src/workspace.ts CHANGED
@@ -7,12 +7,23 @@
7
7
  * manifest, never one below `node_modules`.
8
8
  */
9
9
 
10
- import { DiscoveryError } from "./discover.ts";
10
+ import { DiscoveryError, isConfigFile } from "./discover.ts";
11
11
  import type { EmitLayout } from "./emit-map.ts";
12
12
  import { commonDirectory } from "./emit-map.ts";
13
13
  import type { Host } from "./host.ts";
14
- import { dirnamePath, joinPath, relativePath } from "./paths.ts";
15
- import { pnpmPackages } from "./pnpm-workspace.ts";
14
+ import { ancestors, dirnamePath, joinPath, relativePath } from "./paths.ts";
15
+ import { bytewise } from "./text/compare.ts";
16
+ import { isRecord } from "./json-document.ts";
17
+ import { directoriesIn, filesIn, repositoryOf } from "./fs/tree.ts";
18
+ import {
19
+ holdsManifest,
20
+ inInstalledPackages,
21
+ INSTALLED_PACKAGES,
22
+ MANIFEST,
23
+ manifestAt,
24
+ } from "./fs/manifest.ts";
25
+ import { braceExpanded, pathMatches, segmentMatches } from "./text/glob.ts";
26
+ import { yamlKey } from "./text/yaml.ts";
16
27
 
17
28
  /** What one compiler configuration of a member says about where it reads and writes. */
18
29
  export interface ParsedLayout {
@@ -56,29 +67,7 @@ export interface Workspace {
56
67
  memberOf(path: string): WorkspaceMember | undefined;
57
68
  }
58
69
 
59
- const MANIFEST = "package.json";
60
70
  const PNPM_WORKSPACE = "pnpm-workspace.yaml";
61
- const CONFIG_FILE = /^tsconfig.*\.json$/u;
62
-
63
- function isRecord(value: unknown): value is Record<string, unknown> {
64
- return typeof value === "object" && value !== null && !Array.isArray(value);
65
- }
66
-
67
- /** One manifest, decoded, or `undefined` where there is none to read. */
68
- function manifestAt(host: Host, dir: string): Record<string, unknown> | undefined {
69
- let text: string;
70
- try {
71
- text = host.readFile(joinPath(dir, MANIFEST));
72
- } catch {
73
- return undefined;
74
- }
75
- try {
76
- const value: unknown = JSON.parse(text);
77
- return isRecord(value) ? value : undefined;
78
- } catch {
79
- return undefined;
80
- }
81
- }
82
71
 
83
72
  /** The patterns one directory declares a workspace with, or `undefined` where it declares none. */
84
73
  function declarationAt(
@@ -87,14 +76,20 @@ function declarationAt(
87
76
  ): { readonly file: string; readonly patterns: readonly string[] } | undefined {
88
77
  const pnpm = joinPath(dir, PNPM_WORKSPACE);
89
78
  if (host.kindOf(pnpm) === "file") {
90
- const read = pnpmPackages(host.readFile(pnpm));
91
- if (read.kind === "refused") {
79
+ const read = yamlKey(host.readFile(pnpm), "packages");
80
+ if (read.kind === "absent") {
81
+ return { file: pnpm, patterns: [] };
82
+ }
83
+ const listed = read.kind === "value" ? read.value : undefined;
84
+ if (!Array.isArray(listed) || !listed.every((entry) => typeof entry === "string")) {
85
+ const reason =
86
+ read.kind === "refused" ? read.reason : "packages is not a sequence of strings";
92
87
  throw new DiscoveryError(
93
- `${pnpm}: ${read.reason}; the workspace's packages are read from a sequence of strings`,
88
+ `${pnpm}: ${reason}; the workspace's packages are read from a sequence of strings`,
94
89
  [],
95
90
  );
96
91
  }
97
- return { file: pnpm, patterns: read.kind === "listed" ? read.patterns : [] };
92
+ return { file: pnpm, patterns: listed };
98
93
  }
99
94
  const manifest = manifestAt(host, dir);
100
95
  if (manifest === undefined || !Object.hasOwn(manifest, "workspaces")) {
@@ -120,21 +115,6 @@ function declaresWorkspace(host: Host, dir: string): boolean {
120
115
  return manifest !== undefined && Object.hasOwn(manifest, "workspaces");
121
116
  }
122
117
 
123
- /** A pattern with every brace list expanded into its alternatives. */
124
- function braceExpanded(pattern: string): readonly string[] {
125
- const open = pattern.indexOf("{");
126
- const close = open < 0 ? -1 : pattern.indexOf("}", open);
127
- if (close < 0) {
128
- return [pattern];
129
- }
130
- const before = pattern.slice(0, open);
131
- const after = pattern.slice(close + 1);
132
- return pattern
133
- .slice(open + 1, close)
134
- .split(",")
135
- .flatMap((member) => braceExpanded(before + member + after));
136
- }
137
-
138
118
  /**
139
119
  * The segments of one pattern below the workspace root, `.` and empty segments
140
120
  * dropped and `..` taken back, or `undefined` where it leaves the root.
@@ -156,56 +136,6 @@ function segmentsOf(pattern: string): readonly string[] | undefined {
156
136
  return segments;
157
137
  }
158
138
 
159
- /**
160
- * Whether one directory name matches one pattern segment: `*` stands for any run of
161
- * characters and `?` for one, and neither matches a leading dot.
162
- */
163
- function segmentMatches(segment: string, name: string): boolean {
164
- if (name.startsWith(".") && !segment.startsWith(".")) {
165
- return false;
166
- }
167
- const source = Array.from(segment)
168
- .map((character) => {
169
- if (character === "*") {
170
- return ".*";
171
- }
172
- if (character === "?") {
173
- return ".";
174
- }
175
- return character.replaceAll(/[.+^${}()|[\]\\/]/gu, (c) => `\\${c}`);
176
- })
177
- .join("");
178
- return new RegExp(`^${source}$`, "u").test(name);
179
- }
180
-
181
- /** Whether one path's segments match one pattern's, `**` standing for any run of segments. */
182
- function pathMatches(pattern: readonly string[], path: readonly string[]): boolean {
183
- const [head, ...rest] = pattern;
184
- if (head === undefined) {
185
- return path.length === 0;
186
- }
187
- if (head === "**") {
188
- return (
189
- pathMatches(rest, path) ||
190
- (path.length > 0 && !(path[0] ?? "").startsWith(".") && pathMatches(pattern, path.slice(1)))
191
- );
192
- }
193
- const [name, ...below] = path;
194
- return name !== undefined && segmentMatches(head, name) && pathMatches(rest, below);
195
- }
196
-
197
- function subdirectories(host: Host, dir: string): readonly string[] {
198
- try {
199
- return host
200
- .readDirectory(dir)
201
- .filter((entry) => entry.directory && entry.name !== "node_modules" && entry.name !== ".git")
202
- .map((entry) => entry.name)
203
- .sort();
204
- } catch {
205
- return [];
206
- }
207
- }
208
-
209
139
  /**
210
140
  * Every directory below `dir` one pattern names. `**` does not descend into a
211
141
  * directory that declares a workspace of its own, whose packages are that
@@ -219,7 +149,7 @@ function expand(host: Host, dir: string, pattern: readonly string[], into: Set<s
219
149
  }
220
150
  if (head === "**") {
221
151
  expand(host, dir, rest, into);
222
- for (const name of subdirectories(host, dir)) {
152
+ for (const name of directoriesIn(host, dir)) {
223
153
  if (name.startsWith(".")) {
224
154
  continue;
225
155
  }
@@ -230,12 +160,12 @@ function expand(host: Host, dir: string, pattern: readonly string[], into: Set<s
230
160
  }
231
161
  if (!head.includes("*") && !head.includes("?")) {
232
162
  const child = joinPath(dir, head);
233
- if (head !== "node_modules" && host.kindOf(child) === "directory") {
163
+ if (head !== INSTALLED_PACKAGES && host.kindOf(child) === "directory") {
234
164
  expand(host, child, rest, into);
235
165
  }
236
166
  return;
237
167
  }
238
- for (const name of subdirectories(host, dir)) {
168
+ for (const name of directoriesIn(host, dir)) {
239
169
  if (segmentMatches(head, name)) {
240
170
  expand(host, joinPath(dir, name), rest, into);
241
171
  }
@@ -277,10 +207,10 @@ function namesDirectory(root: string, patterns: readonly string[], dir: string):
277
207
  if (below === undefined) {
278
208
  return false;
279
209
  }
280
- const path = below.split("/");
281
- if (path.includes("node_modules")) {
210
+ if (inInstalledPackages(below)) {
282
211
  return false;
283
212
  }
213
+ const path = below.split("/");
284
214
  let named = false;
285
215
  for (const written of patterns) {
286
216
  const negated = written.startsWith("!");
@@ -314,17 +244,7 @@ function configurationsOf(
314
244
  dir: string,
315
245
  parse: (configFile: string) => ParsedLayout | undefined,
316
246
  ): readonly MemberConfiguration[] {
317
- let names: readonly string[];
318
- try {
319
- names = host
320
- .readDirectory(dir)
321
- .filter((entry) => !entry.directory && CONFIG_FILE.test(entry.name))
322
- .map((entry) => entry.name)
323
- .sort();
324
- } catch {
325
- return [];
326
- }
327
- return names.flatMap((name) => {
247
+ return filesIn(host, dir, isConfigFile).flatMap((name) => {
328
248
  const configFile = joinPath(dir, name);
329
249
  const parsed = parse(configFile);
330
250
  return parsed === undefined
@@ -353,22 +273,17 @@ function nearestDeclaration(
353
273
  readonly highestPackage: string | undefined;
354
274
  }
355
275
  | undefined {
356
- let dir = targetRoot;
357
276
  let highestPackage: string | undefined;
358
- for (;;) {
277
+ for (const dir of ancestors(targetRoot, repositoryOf(host, targetRoot)?.root)) {
359
278
  const declared = declarationAt(host, dir);
360
279
  if (declared !== undefined) {
361
280
  return { root: dir, ...declared, highestPackage };
362
281
  }
363
- if (host.kindOf(joinPath(dir, MANIFEST)) === "file") {
282
+ if (holdsManifest(host, dir)) {
364
283
  highestPackage = dir;
365
284
  }
366
- const up = dirnamePath(dir);
367
- if (up === dir || host.kindOf(joinPath(dir, ".git")) !== "absent") {
368
- return undefined;
369
- }
370
- dir = up;
371
285
  }
286
+ return undefined;
372
287
  }
373
288
 
374
289
  /**
@@ -414,7 +329,7 @@ export function findWorkspace(
414
329
  return undefined;
415
330
  }
416
331
  members.sort((a, b) =>
417
- a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : a.dir < b.dir ? -1 : 1,
332
+ a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : bytewise(a.dir, b.dir),
418
333
  );
419
334
  const named = new Map<string, WorkspaceMember[]>();
420
335
  for (const member of members) {
@@ -427,8 +342,7 @@ export function findWorkspace(
427
342
  return scopes.get(dir);
428
343
  }
429
344
  const up = dirnamePath(dir);
430
- const found =
431
- host.kindOf(joinPath(dir, MANIFEST)) === "file" ? dir : up === dir ? undefined : scopeOf(up);
345
+ const found = holdsManifest(host, dir) ? dir : up === dir ? undefined : scopeOf(up);
432
346
  scopes.set(dir, found);
433
347
  return found;
434
348
  };
package/dist/src/glob.js DELETED
@@ -1,65 +0,0 @@
1
- /**
2
- * Glob patterns over the paths of the target's own files, as the configuration
3
- * writes them. Both `ts.test_files` and `ts.entry_files` name files this way, and
4
- * one grammar answers for both.
5
- *
6
- * A configured root is not written in this grammar. It names a symbol reference
7
- * rather than a path, `*` there spans the solidus and a brace list means nothing,
8
- * so that rule is written where it is read.
9
- */
10
- /**
11
- * One literal run of a pattern, with every character the expression syntax claims
12
- * escaped.
13
- *
14
- * The set is the syntax characters and the solidus, and it is closed: an escape of any
15
- * other character is refused outright by the expression syntax under the unicode flag,
16
- * so escaping a hyphen, which needs none outside a character class, would refuse every
17
- * pattern that names a hyphenated path.
18
- */
19
- function quoted(text) {
20
- return text.replaceAll(/[.*+?^${}()|[\]\\/]/gu, (character) => `\\${character}`);
21
- }
22
- /**
23
- * The expression one glob pattern denotes. `**` spans path separators while `*` and `?`
24
- * do not, a brace list is an alternation of its members, and every other character
25
- * stands for itself.
26
- */
27
- export function globExpression(pattern) {
28
- let source = "^";
29
- let at = 0;
30
- while (at < pattern.length) {
31
- const rest = pattern.slice(at);
32
- if (rest.startsWith("**/")) {
33
- source += "(?:[^/]*/)*";
34
- at += 3;
35
- continue;
36
- }
37
- if (rest.startsWith("**")) {
38
- source += ".*";
39
- at += 2;
40
- continue;
41
- }
42
- const character = pattern[at] ?? "";
43
- at += 1;
44
- if (character === "*") {
45
- source += "[^/]*";
46
- continue;
47
- }
48
- if (character === "?") {
49
- source += "[^/]";
50
- continue;
51
- }
52
- if (character === "{") {
53
- const close = pattern.indexOf("}", at);
54
- if (close < 0) {
55
- source += "\\{";
56
- continue;
57
- }
58
- source += `(?:${pattern.slice(at, close).split(",").map(quoted).join("|")})`;
59
- at = close + 1;
60
- continue;
61
- }
62
- source += quoted(character);
63
- }
64
- return new RegExp(`${source}$`, "u");
65
- }
@@ -1,120 +0,0 @@
1
- /**
2
- * The `packages` list of a `pnpm-workspace.yaml`. The document is read as far as that
3
- * one key needs and no further: a top-level `packages` key holding a block sequence or
4
- * a one-line flow sequence of plain or quoted strings. Any other value of the key is
5
- * refused rather than guessed at, because a list read wrong names the wrong packages.
6
- */
7
- /** The characters a plain scalar may not start with. */
8
- const INDICATORS = new Set(["[", "]", "{", "}", ",", "&", "*", "!", "|", ">", "%", "@", "`", "#"]);
9
- /** One line with its comment removed, a `#` inside quotes kept. */
10
- function uncommented(line) {
11
- let quote;
12
- for (let at = 0; at < line.length; at += 1) {
13
- const character = line[at];
14
- if (quote !== undefined) {
15
- if (character === quote) {
16
- quote = undefined;
17
- }
18
- continue;
19
- }
20
- if (character === "'" || character === '"') {
21
- quote = character;
22
- }
23
- else if (character === "#" && (at === 0 || /\s/u.test(line[at - 1] ?? ""))) {
24
- return line.slice(0, at).trimEnd();
25
- }
26
- }
27
- return line.trimEnd();
28
- }
29
- /** One scalar as written, or `undefined` where it is not a string this reader reads. */
30
- function scalarOf(text) {
31
- const value = text.trim();
32
- if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
33
- const inner = value.slice(1, -1);
34
- return /'(?!')/u.test(inner.replaceAll("''", "")) ? undefined : inner.replaceAll("''", "'");
35
- }
36
- if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
37
- const inner = value.slice(1, -1);
38
- return inner.includes("\\") || inner.includes('"') ? undefined : inner;
39
- }
40
- if (value === "" ||
41
- INDICATORS.has(value[0] ?? "") ||
42
- value.startsWith("'") ||
43
- value.startsWith('"')) {
44
- return undefined;
45
- }
46
- if (value.startsWith("- ") || value.startsWith("? ") || /:(?:\s|$)/u.test(value)) {
47
- return undefined;
48
- }
49
- return value;
50
- }
51
- /** The entries of a one-line flow sequence, or `undefined` where one does not read. */
52
- function flowOf(text) {
53
- const inner = text.slice(1, -1).trim();
54
- if (inner === "") {
55
- return [];
56
- }
57
- const entries = inner.split(",");
58
- if (entries.length > 1 && entries.at(-1)?.trim() === "") {
59
- entries.pop();
60
- }
61
- const found = [];
62
- for (const entry of entries) {
63
- const value = scalarOf(entry);
64
- if (value === undefined) {
65
- return undefined;
66
- }
67
- found.push(value);
68
- }
69
- return found;
70
- }
71
- const KEY = /^(?:packages|'packages'|"packages")\s*:(?:\s(.*))?$/u;
72
- /** The `packages` list one `pnpm-workspace.yaml` text declares. */
73
- export function pnpmPackages(text) {
74
- const lines = text.split(/\r?\n/u).map(uncommented);
75
- const keys = lines.flatMap((line, at) => (KEY.test(line) ? [at] : []));
76
- const [at] = keys;
77
- if (at === undefined) {
78
- return { kind: "absent" };
79
- }
80
- if (keys.length > 1) {
81
- return { kind: "refused", reason: "the packages key is declared more than once" };
82
- }
83
- const rest = (KEY.exec(lines[at] ?? "")?.[1] ?? "").trim();
84
- if (rest !== "") {
85
- const listed = rest.startsWith("[") && rest.endsWith("]") ? flowOf(rest) : undefined;
86
- return listed === undefined
87
- ? {
88
- kind: "refused",
89
- reason: `packages holds ${JSON.stringify(rest)}, not a sequence of strings`,
90
- }
91
- : { kind: "listed", patterns: listed };
92
- }
93
- const patterns = [];
94
- for (const line of lines.slice(at + 1)) {
95
- if (line.trim() === "") {
96
- continue;
97
- }
98
- const item = /^(\s*)-(?:\s+(.*))?$/u.exec(line);
99
- if (item === null) {
100
- if (/^\s/u.test(line)) {
101
- return {
102
- kind: "refused",
103
- reason: `packages holds ${JSON.stringify(line.trim())}, not a sequence item`,
104
- };
105
- }
106
- break;
107
- }
108
- const value = scalarOf(item[2] ?? "");
109
- if (value === undefined) {
110
- return {
111
- kind: "refused",
112
- reason: `the packages item ${JSON.stringify(line.trim())} is not a plain or quoted string`,
113
- };
114
- }
115
- patterns.push(value);
116
- }
117
- return patterns.length === 0
118
- ? { kind: "refused", reason: "packages holds no sequence" }
119
- : { kind: "listed", patterns };
120
- }
package/src/glob.ts DELETED
@@ -1,67 +0,0 @@
1
- /**
2
- * Glob patterns over the paths of the target's own files, as the configuration
3
- * writes them. Both `ts.test_files` and `ts.entry_files` name files this way, and
4
- * one grammar answers for both.
5
- *
6
- * A configured root is not written in this grammar. It names a symbol reference
7
- * rather than a path, `*` there spans the solidus and a brace list means nothing,
8
- * so that rule is written where it is read.
9
- */
10
-
11
- /**
12
- * One literal run of a pattern, with every character the expression syntax claims
13
- * escaped.
14
- *
15
- * The set is the syntax characters and the solidus, and it is closed: an escape of any
16
- * other character is refused outright by the expression syntax under the unicode flag,
17
- * so escaping a hyphen, which needs none outside a character class, would refuse every
18
- * pattern that names a hyphenated path.
19
- */
20
- function quoted(text: string): string {
21
- return text.replaceAll(/[.*+?^${}()|[\]\\/]/gu, (character) => `\\${character}`);
22
- }
23
-
24
- /**
25
- * The expression one glob pattern denotes. `**` spans path separators while `*` and `?`
26
- * do not, a brace list is an alternation of its members, and every other character
27
- * stands for itself.
28
- */
29
- export function globExpression(pattern: string): RegExp {
30
- let source = "^";
31
- let at = 0;
32
- while (at < pattern.length) {
33
- const rest = pattern.slice(at);
34
- if (rest.startsWith("**/")) {
35
- source += "(?:[^/]*/)*";
36
- at += 3;
37
- continue;
38
- }
39
- if (rest.startsWith("**")) {
40
- source += ".*";
41
- at += 2;
42
- continue;
43
- }
44
- const character = pattern[at] ?? "";
45
- at += 1;
46
- if (character === "*") {
47
- source += "[^/]*";
48
- continue;
49
- }
50
- if (character === "?") {
51
- source += "[^/]";
52
- continue;
53
- }
54
- if (character === "{") {
55
- const close = pattern.indexOf("}", at);
56
- if (close < 0) {
57
- source += "\\{";
58
- continue;
59
- }
60
- source += `(?:${pattern.slice(at, close).split(",").map(quoted).join("|")})`;
61
- at = close + 1;
62
- continue;
63
- }
64
- source += quoted(character);
65
- }
66
- return new RegExp(`${source}$`, "u");
67
- }
@@ -1,135 +0,0 @@
1
- /**
2
- * The `packages` list of a `pnpm-workspace.yaml`. The document is read as far as that
3
- * one key needs and no further: a top-level `packages` key holding a block sequence or
4
- * a one-line flow sequence of plain or quoted strings. Any other value of the key is
5
- * refused rather than guessed at, because a list read wrong names the wrong packages.
6
- */
7
-
8
- /** What the document says about `packages`. */
9
- type PnpmPackages =
10
- | { readonly kind: "listed"; readonly patterns: readonly string[] }
11
- /** The document declares no `packages` key. */
12
- | { readonly kind: "absent" }
13
- /** The key holds something this reader does not read, with what it holds. */
14
- | { readonly kind: "refused"; readonly reason: string };
15
-
16
- /** The characters a plain scalar may not start with. */
17
- const INDICATORS = new Set(["[", "]", "{", "}", ",", "&", "*", "!", "|", ">", "%", "@", "`", "#"]);
18
-
19
- /** One line with its comment removed, a `#` inside quotes kept. */
20
- function uncommented(line: string): string {
21
- let quote: string | undefined;
22
- for (let at = 0; at < line.length; at += 1) {
23
- const character = line[at];
24
- if (quote !== undefined) {
25
- if (character === quote) {
26
- quote = undefined;
27
- }
28
- continue;
29
- }
30
- if (character === "'" || character === '"') {
31
- quote = character;
32
- } else if (character === "#" && (at === 0 || /\s/u.test(line[at - 1] ?? ""))) {
33
- return line.slice(0, at).trimEnd();
34
- }
35
- }
36
- return line.trimEnd();
37
- }
38
-
39
- /** One scalar as written, or `undefined` where it is not a string this reader reads. */
40
- function scalarOf(text: string): string | undefined {
41
- const value = text.trim();
42
- if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
43
- const inner = value.slice(1, -1);
44
- return /'(?!')/u.test(inner.replaceAll("''", "")) ? undefined : inner.replaceAll("''", "'");
45
- }
46
- if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
47
- const inner = value.slice(1, -1);
48
- return inner.includes("\\") || inner.includes('"') ? undefined : inner;
49
- }
50
- if (
51
- value === "" ||
52
- INDICATORS.has(value[0] ?? "") ||
53
- value.startsWith("'") ||
54
- value.startsWith('"')
55
- ) {
56
- return undefined;
57
- }
58
- if (value.startsWith("- ") || value.startsWith("? ") || /:(?:\s|$)/u.test(value)) {
59
- return undefined;
60
- }
61
- return value;
62
- }
63
-
64
- /** The entries of a one-line flow sequence, or `undefined` where one does not read. */
65
- function flowOf(text: string): readonly string[] | undefined {
66
- const inner = text.slice(1, -1).trim();
67
- if (inner === "") {
68
- return [];
69
- }
70
- const entries = inner.split(",");
71
- if (entries.length > 1 && entries.at(-1)?.trim() === "") {
72
- entries.pop();
73
- }
74
- const found: string[] = [];
75
- for (const entry of entries) {
76
- const value = scalarOf(entry);
77
- if (value === undefined) {
78
- return undefined;
79
- }
80
- found.push(value);
81
- }
82
- return found;
83
- }
84
-
85
- const KEY = /^(?:packages|'packages'|"packages")\s*:(?:\s(.*))?$/u;
86
-
87
- /** The `packages` list one `pnpm-workspace.yaml` text declares. */
88
- export function pnpmPackages(text: string): PnpmPackages {
89
- const lines = text.split(/\r?\n/u).map(uncommented);
90
- const keys = lines.flatMap((line, at) => (KEY.test(line) ? [at] : []));
91
- const [at] = keys;
92
- if (at === undefined) {
93
- return { kind: "absent" };
94
- }
95
- if (keys.length > 1) {
96
- return { kind: "refused", reason: "the packages key is declared more than once" };
97
- }
98
- const rest = (KEY.exec(lines[at] ?? "")?.[1] ?? "").trim();
99
- if (rest !== "") {
100
- const listed = rest.startsWith("[") && rest.endsWith("]") ? flowOf(rest) : undefined;
101
- return listed === undefined
102
- ? {
103
- kind: "refused",
104
- reason: `packages holds ${JSON.stringify(rest)}, not a sequence of strings`,
105
- }
106
- : { kind: "listed", patterns: listed };
107
- }
108
- const patterns: string[] = [];
109
- for (const line of lines.slice(at + 1)) {
110
- if (line.trim() === "") {
111
- continue;
112
- }
113
- const item = /^(\s*)-(?:\s+(.*))?$/u.exec(line);
114
- if (item === null) {
115
- if (/^\s/u.test(line)) {
116
- return {
117
- kind: "refused",
118
- reason: `packages holds ${JSON.stringify(line.trim())}, not a sequence item`,
119
- };
120
- }
121
- break;
122
- }
123
- const value = scalarOf(item[2] ?? "");
124
- if (value === undefined) {
125
- return {
126
- kind: "refused",
127
- reason: `the packages item ${JSON.stringify(line.trim())} is not a plain or quoted string`,
128
- };
129
- }
130
- patterns.push(value);
131
- }
132
- return patterns.length === 0
133
- ? { kind: "refused", reason: "packages holds no sequence" }
134
- : { kind: "listed", patterns };
135
- }