@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.1

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 (122) hide show
  1. package/dist/src/analysis.js +106 -10
  2. package/dist/src/augmentations.js +21 -8
  3. package/dist/src/automatic-types.js +189 -0
  4. package/dist/src/base-url.js +18 -0
  5. package/dist/src/components.js +7 -6
  6. package/dist/src/config.js +1 -0
  7. package/dist/src/configuration-files.js +50 -12
  8. package/dist/src/conformance-record.js +2 -2
  9. package/dist/src/convention-rows.js +75 -0
  10. package/dist/src/conventions.js +101 -5
  11. package/dist/src/decorator.js +21 -18
  12. package/dist/src/dependencies.js +183 -28
  13. package/dist/src/discover.js +132 -11
  14. package/dist/src/entry-point-gaps.js +7 -10
  15. package/dist/src/entry-points.js +112 -11
  16. package/dist/src/findings/completion.js +116 -21
  17. package/dist/src/findings/dependencies-and-module-machinery.js +71 -25
  18. package/dist/src/findings/emitters.js +1 -1
  19. package/dist/src/findings/intra-function.js +44 -13
  20. package/dist/src/findings/non-code-artifacts.js +66 -33
  21. package/dist/src/findings/unused-declarations.js +21 -1
  22. package/dist/src/findings/visibility-narrowing.js +22 -3
  23. package/dist/src/generated-file.js +11 -0
  24. package/dist/src/git-ignore.js +86 -0
  25. package/dist/src/graph.js +4 -1
  26. package/dist/src/import-bindings.js +97 -0
  27. package/dist/src/indexed-access.js +130 -28
  28. package/dist/src/interface-satisfaction.js +30 -12
  29. package/dist/src/intra-function-parts.js +297 -45
  30. package/dist/src/inventory.js +52 -8
  31. package/dist/src/kinds.js +13 -13
  32. package/dist/src/matrix.js +18 -9
  33. package/dist/src/mdx-entries.js +197 -0
  34. package/dist/src/module-declarations.js +9 -13
  35. package/dist/src/overrides.js +41 -10
  36. package/dist/src/print.js +1 -0
  37. package/dist/src/ref.js +10 -0
  38. package/dist/src/references.js +4 -1
  39. package/dist/src/report.js +4 -2
  40. package/dist/src/resolve.js +2 -0
  41. package/dist/src/roots.js +110 -38
  42. package/dist/src/schema.js +1 -0
  43. package/dist/src/serialization-contract.js +54 -8
  44. package/dist/src/session.js +7 -0
  45. package/dist/src/source-files.js +39 -14
  46. package/dist/src/suppress-file.js +10 -2
  47. package/dist/src/suppress-inline.js +18 -2
  48. package/dist/src/suppress.js +7 -5
  49. package/dist/src/sweep.js +27 -10
  50. package/dist/src/symbol-keys.js +103 -2
  51. package/dist/src/test-support.js +121 -17
  52. package/dist/src/type-errors.js +34 -7
  53. package/dist/src/type-parameter-reads.js +172 -0
  54. package/dist/src/verbs/analyze.js +16 -0
  55. package/dist/src/verbs/explain.js +4 -2
  56. package/dist/src/version.js +1 -1
  57. package/dist/src/workflow-steps.js +119 -22
  58. package/dist/src/workspace-programs.js +7 -3
  59. package/dist/src/workspace-resolution.js +53 -10
  60. package/jsr.json +1 -1
  61. package/package.json +2 -2
  62. package/src/analysis.ts +143 -10
  63. package/src/augmentations.ts +21 -10
  64. package/src/automatic-types.ts +246 -0
  65. package/src/base-url.ts +24 -0
  66. package/src/components.ts +7 -6
  67. package/src/config.ts +3 -0
  68. package/src/configuration-files.ts +66 -12
  69. package/src/conformance-record.ts +2 -2
  70. package/src/convention-rows.ts +103 -0
  71. package/src/conventions.ts +121 -7
  72. package/src/decorator.ts +28 -18
  73. package/src/dependencies.ts +247 -41
  74. package/src/discover.ts +156 -13
  75. package/src/entry-point-gaps.ts +8 -11
  76. package/src/entry-points.ts +133 -11
  77. package/src/finding.ts +10 -0
  78. package/src/findings/completion.ts +147 -24
  79. package/src/findings/dependencies-and-module-machinery.ts +90 -28
  80. package/src/findings/emitters.ts +1 -1
  81. package/src/findings/intra-function.ts +56 -14
  82. package/src/findings/non-code-artifacts.ts +81 -41
  83. package/src/findings/unused-declarations.ts +25 -1
  84. package/src/findings/visibility-narrowing.ts +32 -3
  85. package/src/generated-file.ts +16 -0
  86. package/src/git-ignore.ts +98 -0
  87. package/src/graph.ts +6 -0
  88. package/src/host.ts +5 -9
  89. package/src/import-bindings.ts +128 -0
  90. package/src/indexed-access.ts +152 -30
  91. package/src/interface-satisfaction.ts +33 -13
  92. package/src/intra-function-parts.ts +394 -59
  93. package/src/inventory.ts +71 -7
  94. package/src/kinds.ts +13 -13
  95. package/src/matrix.ts +32 -14
  96. package/src/mdx-entries.ts +241 -0
  97. package/src/module-declarations.ts +11 -13
  98. package/src/overrides.ts +51 -12
  99. package/src/print.ts +1 -0
  100. package/src/ref.ts +12 -0
  101. package/src/references.ts +10 -1
  102. package/src/report.ts +16 -3
  103. package/src/resolve.ts +3 -0
  104. package/src/roots.ts +163 -45
  105. package/src/schema.ts +1 -0
  106. package/src/serialization-contract.ts +55 -4
  107. package/src/session.ts +7 -0
  108. package/src/source-files.ts +66 -16
  109. package/src/suppress-file.ts +12 -1
  110. package/src/suppress-inline.ts +18 -1
  111. package/src/suppress.ts +9 -5
  112. package/src/sweep.ts +32 -11
  113. package/src/symbol-keys.ts +129 -2
  114. package/src/test-support.ts +149 -16
  115. package/src/type-errors.ts +38 -6
  116. package/src/type-parameter-reads.ts +227 -0
  117. package/src/verbs/analyze.ts +22 -0
  118. package/src/verbs/explain.ts +5 -2
  119. package/src/version.ts +1 -1
  120. package/src/workflow-steps.ts +127 -22
  121. package/src/workspace-programs.ts +7 -3
  122. package/src/workspace-resolution.ts +62 -10
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { Inventory, InventorySymbol } from "./inventory.ts";
11
+ import { referenceKey } from "./matrix.ts";
11
12
  import type { Reference } from "./references.ts";
12
13
 
13
14
  /** Whether one declaration is a `declare global` block: a module-level namespace named `global`. */
@@ -62,14 +63,24 @@ export function augmentationReferences(
62
63
  }
63
64
  return [];
64
65
  };
65
- return found.flatMap((reference) =>
66
- holders(reference.to).map((to) => ({
67
- from: reference.from,
68
- to,
69
- position: reference.position,
70
- use: "read" as const,
71
- resolution: reference.resolution,
72
- test: reference.test,
73
- })),
74
- );
66
+ // Two uses at one place of declarations one block holds read the block once there.
67
+ const made = new Set(found.map(referenceKey));
68
+ const reads = new Map<string, Reference>();
69
+ for (const reference of found) {
70
+ for (const to of holders(reference.to)) {
71
+ const read: Reference = {
72
+ from: reference.from,
73
+ to,
74
+ position: reference.position,
75
+ use: "read",
76
+ resolution: reference.resolution,
77
+ test: reference.test,
78
+ };
79
+ const key = referenceKey(read);
80
+ if (!made.has(key) && !reads.has(key)) {
81
+ reads.set(key, read);
82
+ }
83
+ }
84
+ }
85
+ return [...reads.values()];
75
86
  }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * The global names and ambient modules of the `@types` packages a compiler before version 6
3
+ * includes automatically: every package of each type root when the configuration lists no
4
+ * `types`. Version 6 and later include none, so the analysis program holds none of their
5
+ * declarations, and a use of one resolves to nothing; each such use is a use of the package.
6
+ */
7
+
8
+ import {
9
+ isClassDeclaration,
10
+ isEnumDeclaration,
11
+ isExportAssignment,
12
+ isExportDeclaration,
13
+ isFunctionDeclaration,
14
+ isIdentifier,
15
+ isImportDeclaration,
16
+ isImportEqualsDeclaration,
17
+ isInterfaceDeclaration,
18
+ isModuleBlock,
19
+ isModuleDeclaration,
20
+ isStringLiteral,
21
+ isTypeAliasDeclaration,
22
+ isVariableStatement,
23
+ SyntaxKind,
24
+ type Node,
25
+ type SourceFile,
26
+ type Statement,
27
+ } from "@typescript/native/ast";
28
+ import type { ParseFile } from "./configuration-files.ts";
29
+ import type { DirectoryEntry, Host } from "./host.ts";
30
+ import { dirnamePath, joinPath, resolvePath } from "./paths.ts";
31
+
32
+ const TYPE_ROOT = "node_modules/@types";
33
+
34
+ /** What the automatically included `@types` packages of one configuration declare. */
35
+ export interface AutomaticTypes {
36
+ /** Per global name, the packages that declare it. */
37
+ readonly globals: ReadonlyMap<string, readonly string[]>;
38
+ /** Per ambient module name, the packages that declare it. */
39
+ readonly modules: ReadonlyMap<string, readonly string[]>;
40
+ }
41
+
42
+ const NONE: AutomaticTypes = { globals: new Map(), modules: new Map() };
43
+
44
+ /** What one installed type package declares, by its directory. */
45
+ interface PackageDeclarations {
46
+ readonly globals: readonly string[];
47
+ readonly modules: readonly string[];
48
+ }
49
+
50
+ /**
51
+ * The packages each host has read, so a run's configurations share one reading of a type
52
+ * root; a host reads one unchanging tree for its whole life.
53
+ */
54
+ const READ = new WeakMap<Host, Map<string, PackageDeclarations>>();
55
+
56
+ /** Whether one file is a module: it imports, exports or assigns an export at its top level. */
57
+ function isModuleFile(file: SourceFile): boolean {
58
+ return file.statements.some(
59
+ (statement) =>
60
+ isImportDeclaration(statement) ||
61
+ isImportEqualsDeclaration(statement) ||
62
+ isExportDeclaration(statement) ||
63
+ isExportAssignment(statement) ||
64
+ ((statement as { readonly modifiers?: readonly Node[] }).modifiers ?? []).some(
65
+ (modifier) => modifier.kind === SyntaxKind.ExportKeyword,
66
+ ),
67
+ );
68
+ }
69
+
70
+ /** The names one statement of a global scope declares, and the ambient modules it declares. */
71
+ function declared(statement: Statement, globals: string[], modules: string[]): void {
72
+ if (isVariableStatement(statement)) {
73
+ for (const declaration of statement.declarationList.declarations) {
74
+ if (isIdentifier(declaration.name)) {
75
+ globals.push(declaration.name.text);
76
+ }
77
+ }
78
+ } else if (
79
+ isFunctionDeclaration(statement) ||
80
+ isClassDeclaration(statement) ||
81
+ isInterfaceDeclaration(statement) ||
82
+ isTypeAliasDeclaration(statement) ||
83
+ isEnumDeclaration(statement)
84
+ ) {
85
+ if (statement.name !== undefined) {
86
+ globals.push(statement.name.text);
87
+ }
88
+ } else if (isModuleDeclaration(statement)) {
89
+ if (isStringLiteral(statement.name)) {
90
+ modules.push(statement.name.text);
91
+ } else {
92
+ globals.push(statement.name.text);
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * What one declaration file declares in the global scope: its top level where it is no
99
+ * module, and the body of each `declare global` block where it is one.
100
+ */
101
+ function fileDeclarations(file: SourceFile, globals: string[], modules: string[]): void {
102
+ if (!isModuleFile(file)) {
103
+ file.statements.forEach((statement) => {
104
+ declared(statement, globals, modules);
105
+ });
106
+ return;
107
+ }
108
+ for (const statement of file.statements) {
109
+ if (
110
+ isModuleDeclaration(statement) &&
111
+ isIdentifier(statement.name) &&
112
+ statement.name.text === "global" &&
113
+ statement.body !== undefined &&
114
+ isModuleBlock(statement.body)
115
+ ) {
116
+ statement.body.statements.forEach((inner) => {
117
+ declared(inner, globals, modules);
118
+ });
119
+ }
120
+ }
121
+ }
122
+
123
+ /** The entry declaration file of one installed type package's directory. */
124
+ function entryOf(host: Host, dir: string): string {
125
+ try {
126
+ const manifest: unknown = JSON.parse(host.readFile(joinPath(dir, "package.json")));
127
+ const named =
128
+ typeof manifest === "object" && manifest !== null
129
+ ? ((manifest as Record<string, unknown>)["types"] ??
130
+ (manifest as Record<string, unknown>)["typings"])
131
+ : undefined;
132
+ if (typeof named === "string" && named !== "") {
133
+ const path = resolvePath(dir, named);
134
+ return path.endsWith(".ts") ? path : `${path}.d.ts`;
135
+ }
136
+ } catch {
137
+ // A package with no readable manifest is read from its index, as the compiler reads it.
138
+ }
139
+ return joinPath(dir, "index.d.ts");
140
+ }
141
+
142
+ /**
143
+ * Every global name and ambient module one installed type package declares: in its entry
144
+ * file and each file a `/// <reference path>` of a file read reaches, inside the package.
145
+ */
146
+ function packageDeclarations(host: Host, dir: string, parse: ParseFile): PackageDeclarations {
147
+ const known = READ.get(host) ?? new Map<string, PackageDeclarations>();
148
+ READ.set(host, known);
149
+ const held = known.get(dir);
150
+ if (held !== undefined) {
151
+ return held;
152
+ }
153
+ const globals: string[] = [];
154
+ const modules: string[] = [];
155
+ const seen = new Set<string>();
156
+ const pending = [entryOf(host, dir)];
157
+ for (let path = pending.pop(); path !== undefined; path = pending.pop()) {
158
+ if (seen.has(path) || !path.startsWith(`${dir}/`)) {
159
+ continue;
160
+ }
161
+ seen.add(path);
162
+ let text: string;
163
+ try {
164
+ text = host.readFile(path);
165
+ } catch {
166
+ continue;
167
+ }
168
+ const file = parse(path, text);
169
+ fileDeclarations(file, globals, modules);
170
+ for (const reference of file.referencedFiles) {
171
+ pending.push(resolvePath(dirnamePath(path), reference.fileName));
172
+ }
173
+ }
174
+ const found = { globals, modules };
175
+ known.set(dir, found);
176
+ return found;
177
+ }
178
+
179
+ /**
180
+ * What the `@types` packages a compiler before version 6 would include for one
181
+ * configuration declare: none where its options list `types`, else every package of each
182
+ * `node_modules/@types` directory at or above the configuration's directory, the nearest
183
+ * holding a name winning.
184
+ */
185
+ export function automaticTypes(
186
+ host: Host,
187
+ configFile: string,
188
+ options: Readonly<Record<string, unknown>>,
189
+ parse: ParseFile,
190
+ ): AutomaticTypes {
191
+ if (options["types"] !== undefined) {
192
+ return NONE;
193
+ }
194
+ const globals = new Map<string, string[]>();
195
+ const modules = new Map<string, string[]>();
196
+ const add = (into: Map<string, string[]>, name: string, named: string): void => {
197
+ const held = into.get(name) ?? [];
198
+ if (!held.includes(named)) {
199
+ into.set(name, [...held, named]);
200
+ }
201
+ };
202
+ const read = new Set<string>();
203
+ for (const root of typeRoots(configFile, options)) {
204
+ let entries: readonly DirectoryEntry[];
205
+ try {
206
+ entries = host.readDirectory(root);
207
+ } catch {
208
+ entries = [];
209
+ }
210
+ for (const entry of entries) {
211
+ if (read.has(entry.name) || host.kindOf(joinPath(root, entry.name)) !== "directory") {
212
+ continue;
213
+ }
214
+ read.add(entry.name);
215
+ const named = `@types/${entry.name}`;
216
+ const found = packageDeclarations(host, joinPath(root, entry.name), parse);
217
+ found.globals.forEach((name) => {
218
+ add(globals, name, named);
219
+ });
220
+ found.modules.forEach((name) => {
221
+ add(modules, name, named);
222
+ });
223
+ }
224
+ }
225
+ return { globals, modules };
226
+ }
227
+
228
+ /**
229
+ * The type roots of one configuration that a package manager installs into: those its
230
+ * `typeRoots` lists, else each `node_modules/@types` directory at or above its directory.
231
+ */
232
+ function typeRoots(configFile: string, options: Readonly<Record<string, unknown>>): string[] {
233
+ const listed = options["typeRoots"];
234
+ if (Array.isArray(listed)) {
235
+ return listed.filter(
236
+ (root): root is string => typeof root === "string" && root.endsWith(`/${TYPE_ROOT}`),
237
+ );
238
+ }
239
+ const roots: string[] = [];
240
+ for (let dir = dirnamePath(configFile); ; dir = dirnamePath(dir)) {
241
+ roots.push(joinPath(dir, TYPE_ROOT));
242
+ if (dirnamePath(dir) === dir) {
243
+ return roots;
244
+ }
245
+ }
246
+ }
@@ -0,0 +1,24 @@
1
+ import { resolvePath } from "./paths.ts";
2
+ import type { Engine } from "./session.ts";
3
+
4
+ type CompilerOptions = ReturnType<Engine["parseConfigFile"]>["options"];
5
+
6
+ /**
7
+ * The `paths` one configuration's resolved options map under the compilers before version
8
+ * 7, each target absolute: a target is read against the effective `baseUrl`, and a specifier
9
+ * no other pattern maps is looked up below it. Undefined where no `baseUrl` is set, so the
10
+ * options' own `paths` stand. Version 7 reads no `baseUrl`:
11
+ * https://github.com/microsoft/TypeScript/issues/62207
12
+ */
13
+ export function baseUrlPaths(options: CompilerOptions): Record<string, string[]> | undefined {
14
+ const baseUrl: unknown = Reflect.get(options, "baseUrl");
15
+ if (typeof baseUrl !== "string") {
16
+ return undefined;
17
+ }
18
+ return Object.fromEntries(
19
+ Object.entries({ "*": ["*"], ...options.paths }).map(([key, targets]) => [
20
+ key,
21
+ targets.map((target) => resolvePath(baseUrl, target)),
22
+ ]),
23
+ );
24
+ }
package/src/components.ts CHANGED
@@ -208,12 +208,12 @@ function linesSpanned(symbols: readonly (InventorySymbol | undefined)[]): number
208
208
  }
209
209
 
210
210
  /**
211
- * The dead declarations by subgraph place (`at`, and `position` per graph declaration),
212
- * a test file's only where it tests dead code or nothing the tests reach. `adjacent` holds
213
- * each reference one makes to another, except an unreferenced test-file declaration's to
214
- * production code, which decides nothing of its target; an edge each way between a dead
215
- * member and its dead container; and one back from each production target of an admitted
216
- * test, which puts the test in its subject's component. `references` holds the first alone.
211
+ * The dead declarations by subgraph place (`at`, and `position` per graph declaration), a
212
+ * test file's only where it tests dead code or nothing the tests reach. `adjacent` holds each
213
+ * reference one makes to another, but an unreferenced test-file declaration's to production
214
+ * code, which decides nothing; an edge each way between a dead member and its dead container;
215
+ * and one back from each non-ambient production target of an admitted test, which puts the
216
+ * test in its subject's component. `references` holds the first alone.
217
217
  */
218
218
  function deadSubgraph(
219
219
  graph: Graph,
@@ -248,6 +248,7 @@ function deadSubgraph(
248
248
  if (
249
249
  testOfDeadCode[index] === true &&
250
250
  graph.test[edge.to] !== true &&
251
+ graph.ambient[edge.to] !== true &&
251
252
  graph.subject[edge.to] === true
252
253
  ) {
253
254
  adjacent[to]?.push(from);
package/src/config.ts CHANGED
@@ -175,6 +175,8 @@ export interface TSSection {
175
175
  readonly injectionRegistrations: readonly DeclarationEntry[];
176
176
  readonly lifecycleContracts: readonly LifecycleContract[];
177
177
  readonly serializers: readonly DeclarationEntry[];
178
+ /** Whether the maintainer declares the roots the analysis finds every root the target has. */
179
+ readonly rootsComplete: boolean;
178
180
  }
179
181
 
180
182
  /**
@@ -253,6 +255,7 @@ export function defaultConfig(): Config {
253
255
  injectionRegistrations: [],
254
256
  lifecycleContracts: [],
255
257
  serializers: [],
258
+ rootsComplete: false,
256
259
  },
257
260
  };
258
261
  }
@@ -8,10 +8,13 @@
8
8
  */
9
9
 
10
10
  import {
11
+ isCallExpression,
11
12
  isExportDeclaration,
13
+ isIdentifier,
12
14
  isImportDeclaration,
13
15
  isNoSubstitutionTemplateLiteral,
14
16
  isStringLiteral,
17
+ SyntaxKind,
15
18
  type Node,
16
19
  type SourceFile,
17
20
  } from "@typescript/native/ast";
@@ -110,7 +113,7 @@ interface ConfigurationDocument {
110
113
  export type ParseFile = (fileName: string, text: string) => SourceFile;
111
114
 
112
115
  /** One file no program holds that a configuration module outside the program leads to. */
113
- interface OutsideModule {
116
+ export interface OutsideModule {
114
117
  /** The file, parsed alone. */
115
118
  readonly file: SourceFile;
116
119
  /** The directories of the manifests of the configuration files the file was reached from. */
@@ -439,19 +442,70 @@ export function configurationFiles(
439
442
  };
440
443
  }
441
444
 
442
- /** The relative specifiers one file's imports and re-exports name. */
443
- function relativeSpecifiers(file: SourceFile): readonly string[] {
444
- return file.statements.flatMap((statement) => {
445
- const specifier =
445
+ /** The literal module specifier one `require(...)` or `import(...)` call names, if any. */
446
+ function calledSpecifier(node: Node): Node | undefined {
447
+ if (!isCallExpression(node) || node.arguments.length !== 1) {
448
+ return undefined;
449
+ }
450
+ const callee = node.expression;
451
+ const loads =
452
+ callee.kind === SyntaxKind.ImportKeyword || (isIdentifier(callee) && callee.text === "require");
453
+ return loads ? node.arguments[0] : undefined;
454
+ }
455
+
456
+ /**
457
+ * The relative specifiers one file's imports and re-exports name, and those its
458
+ * `require(...)` and `import(...)` calls name by a literal.
459
+ */
460
+ export function relativeSpecifiers(file: SourceFile): readonly string[] {
461
+ const known = specifiersOf.get(file);
462
+ if (known !== undefined) {
463
+ return known;
464
+ }
465
+ const found: string[] = [];
466
+ const named = (specifier: Node | undefined): void => {
467
+ if (
468
+ specifier !== undefined &&
469
+ (isStringLiteral(specifier) || isNoSubstitutionTemplateLiteral(specifier)) &&
470
+ RELATIVE_PREFIXES.some((prefix) => specifier.text.startsWith(prefix))
471
+ ) {
472
+ found.push(specifier.text);
473
+ }
474
+ };
475
+ const visit = (node: Node): void => {
476
+ named(calledSpecifier(node));
477
+ node.forEachChild(visit);
478
+ };
479
+ for (const statement of file.statements) {
480
+ named(
446
481
  isImportDeclaration(statement) || isExportDeclaration(statement)
447
482
  ? statement.moduleSpecifier
448
- : undefined;
449
- return specifier !== undefined &&
450
- isStringLiteral(specifier) &&
451
- RELATIVE_PREFIXES.some((prefix) => specifier.text.startsWith(prefix))
452
- ? [specifier.text]
453
- : [];
454
- });
483
+ : undefined,
484
+ );
485
+ visit(statement);
486
+ }
487
+ specifiersOf.set(file, found);
488
+ return found;
489
+ }
490
+
491
+ /** What {@link relativeSpecifiers} found in each file, which every project of a run reads alike. */
492
+ const specifiersOf = new WeakMap<SourceFile, readonly string[]>();
493
+
494
+ /**
495
+ * The files no program holds that a chain from `seeds` reads: each seed, and each file
496
+ * below the target root one imports, re-exports or loads by a relative specifier that no
497
+ * program holds either, until no further file joins, each read against the manifest
498
+ * directory its seed names. A file that cannot be read is skipped.
499
+ */
500
+ export function outsideChain(
501
+ host: Host,
502
+ targetRoot: string,
503
+ files: SourceFiles,
504
+ parse: ParseFile,
505
+ seeds: readonly { readonly path: string; readonly manifestDir: string }[],
506
+ ): readonly OutsideModule[] {
507
+ const root = resolvePath(host.workingDirectory(), targetRoot);
508
+ return outsideModules(host, root, [], files, () => false, parse, seeds);
455
509
  }
456
510
 
457
511
  /**
@@ -2,9 +2,9 @@
2
2
  // Regenerate it with `UPDATE_GOLDEN=1 npx vitest --run src/conformance.corpus.test.ts`.
3
3
  export const RECORD = {
4
4
  conformance: {
5
- corpusVersion: "5.2.0",
5
+ corpusVersion: "6.0.0",
6
6
  result: "pass",
7
- digest: "sha256:012872542679539ec105f49b5ee502b0a9fa35c6bbd9ef3983b5f6e53af83d8e",
7
+ digest: "sha256:76c07ac14bda6e302444918d832b01844bac6f29b6f22a742d538804480c0769",
8
8
  },
9
9
  gaps: [],
10
10
  } as const;
@@ -31,6 +31,22 @@ export interface ShortNameReading extends MoveReading {
31
31
  readonly package: string;
32
32
  }
33
33
 
34
+ /**
35
+ * One configuration property whose values name packages by the tool's prefix convention:
36
+ * `x` stands for `<prefix>-x`, `@s` for `@s/<prefix>` and `@s/x` for `@s/<prefix>-x`, and
37
+ * a name already holding the prefix for itself.
38
+ */
39
+ export interface PrefixedNameReading extends MoveReading {
40
+ /** The prefix the convention puts before a short name. */
41
+ readonly prefix: string;
42
+ /**
43
+ * The text a value starts with to name a package, the name ending at its last `/`.
44
+ * Absent, every value without a `:` names one, the name ending at its first `/` past
45
+ * a scope.
46
+ */
47
+ readonly marker?: string;
48
+ }
49
+
34
50
  /**
35
51
  * One configuration property whose value makes the tool's output import a runtime helper
36
52
  * package, so the package is used where the property holds that value.
@@ -63,6 +79,14 @@ export interface TestFileReading extends MoveReading {
63
79
  readonly roots: readonly string[];
64
80
  }
65
81
 
82
+ /** One member of the declaring `package.json` whose strings name the tool's entries. */
83
+ export interface ManifestEntryKey {
84
+ /** The member's path through nested objects, its keys joined by `.`, a `*` key standing for every member. */
85
+ readonly key: string;
86
+ /** Whether each string names a directory of entries, or one entry, its extension optional. */
87
+ readonly directory: boolean;
88
+ }
89
+
66
90
  /** One directory a row's globs name that a configuration property moves. */
67
91
  export interface DirectoryMove {
68
92
  /** The placeholder the row's globs and later moves write as `<id>`. */
@@ -96,6 +120,8 @@ export interface ConventionRow {
96
120
  readonly moves: readonly DirectoryMove[];
97
121
  /** The properties that name a package the configuration uses by a short name. */
98
122
  readonly shortNames?: readonly ShortNameReading[];
123
+ /** The properties that name a package the configuration uses by its prefix convention. */
124
+ readonly prefixedNames?: readonly PrefixedNameReading[];
99
125
  /**
100
126
  * The properties whose value only selects files to test, mutate, cover or ignore, so a
101
127
  * string there names no file the tool runs or loads.
@@ -110,6 +136,8 @@ export interface ConventionRow {
110
136
  readonly packageKeys?: readonly PackageKey[];
111
137
  /** The members of a `package.json` the tool reads its configuration from. */
112
138
  readonly manifestKeys?: readonly string[];
139
+ /** The members of the declaring `package.json` whose strings name entries, as an entry glob does. */
140
+ readonly manifestEntries?: readonly ManifestEntryKey[];
113
141
  /** The settings that make the tool's output import a runtime helper package. */
114
142
  readonly helpers?: readonly HelperReading[];
115
143
  }
@@ -118,6 +146,8 @@ const SCRIPT = "{js,jsx,ts,tsx}";
118
146
  const MODULE = "{js,mjs,ts,mts}";
119
147
  const ANY_MODULE = "{js,mjs,cjs,ts,mts,cts}";
120
148
  const VITE_CONFIG = `vite.config.${ANY_MODULE}`;
149
+ /** The legacy configuration files of ESLint that hold JSON or a CommonJS module. */
150
+ const ESLINTRC = [".eslintrc", ".eslintrc.json", ".eslintrc.{js,cjs}"];
121
151
 
122
152
  const SVELTEKIT_ROUTE_FILES = "+{page,layout,server,page.server,layout.server,error}";
123
153
 
@@ -231,6 +261,25 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
231
261
  package: "@babel/runtime",
232
262
  })),
233
263
  },
264
+ {
265
+ name: "docusaurus",
266
+ package: "@docusaurus/core",
267
+ range: ">=2.0.0 <4.0.0",
268
+ entries: [`src/pages/**/*.${SCRIPT}`, `src/theme/**/*.${SCRIPT}`],
269
+ moves: [],
270
+ },
271
+ {
272
+ name: "eslint",
273
+ package: "eslint",
274
+ range: ">=4.0.0 <10.0.0",
275
+ entries: [],
276
+ moves: [],
277
+ prefixedNames: [
278
+ { property: "plugins", files: ESLINTRC, prefix: "eslint-plugin" },
279
+ { property: "extends", files: ESLINTRC, prefix: "eslint-plugin", marker: "plugin:" },
280
+ { property: "extends", files: ESLINTRC, prefix: "eslint-config" },
281
+ ],
282
+ },
234
283
  {
235
284
  name: "eslint",
236
285
  package: "eslint",
@@ -238,6 +287,13 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
238
287
  entries: [`**/eslint.config.${ANY_MODULE}`],
239
288
  moves: [],
240
289
  },
290
+ {
291
+ name: "expo",
292
+ package: "expo",
293
+ range: ">=44.0.0 <60.0.0",
294
+ entries: [`{index,App}.${SCRIPT}`],
295
+ moves: [],
296
+ },
241
297
  {
242
298
  name: "expo-router",
243
299
  package: "expo-router",
@@ -322,6 +378,28 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
322
378
  },
323
379
  ],
324
380
  },
381
+ {
382
+ name: "oclif",
383
+ package: "@oclif/core",
384
+ range: ">=1.0.0 <5.0.0",
385
+ entries: [`src/{commands,hooks}/**/*.${SCRIPT}`],
386
+ moves: [],
387
+ manifestEntries: [
388
+ { key: "oclif.commands", directory: true },
389
+ { key: "oclif.hooks", directory: false },
390
+ ],
391
+ },
392
+ {
393
+ name: "parcel",
394
+ package: "parcel",
395
+ range: ">=2.0.0 <3.0.0",
396
+ entries: [],
397
+ moves: [],
398
+ manifestEntries: [
399
+ { key: "source", directory: false },
400
+ { key: "targets.*.source", directory: false },
401
+ ],
402
+ },
325
403
  {
326
404
  name: "prettier",
327
405
  package: "prettier",
@@ -613,6 +691,31 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
613
691
  },
614
692
  ],
615
693
  },
694
+ {
695
+ name: "tanstack-router",
696
+ package: "@tanstack/router-plugin",
697
+ range: ">=1.0.0 <2.0.0",
698
+ entries: [`<routes>/**/*.${SCRIPT}`],
699
+ moves: [
700
+ {
701
+ id: "routes",
702
+ defaults: ["src/routes"],
703
+ readings: [
704
+ { property: "routesDirectory", files: ["tsr.config.json"] },
705
+ {
706
+ property: "routesDirectory",
707
+ files: [VITE_CONFIG],
708
+ call: { module: "@tanstack/router-plugin/vite", export: "tanstackRouter" },
709
+ },
710
+ {
711
+ property: "routesDirectory",
712
+ files: [VITE_CONFIG],
713
+ call: { module: "@tanstack/router-plugin/vite", export: "TanStackRouterVite" },
714
+ },
715
+ ],
716
+ },
717
+ ],
718
+ },
616
719
  {
617
720
  name: "vite",
618
721
  package: "vite",