@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.
- package/dist/src/analysis.js +106 -10
- package/dist/src/augmentations.js +21 -8
- package/dist/src/automatic-types.js +189 -0
- package/dist/src/base-url.js +18 -0
- package/dist/src/components.js +7 -6
- package/dist/src/config.js +1 -0
- package/dist/src/configuration-files.js +50 -12
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/convention-rows.js +75 -0
- package/dist/src/conventions.js +101 -5
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +183 -28
- package/dist/src/discover.js +132 -11
- package/dist/src/entry-point-gaps.js +7 -10
- package/dist/src/entry-points.js +112 -11
- package/dist/src/findings/completion.js +116 -21
- package/dist/src/findings/dependencies-and-module-machinery.js +71 -25
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +44 -13
- package/dist/src/findings/non-code-artifacts.js +66 -33
- package/dist/src/findings/unused-declarations.js +21 -1
- package/dist/src/findings/visibility-narrowing.js +22 -3
- package/dist/src/generated-file.js +11 -0
- package/dist/src/git-ignore.js +86 -0
- package/dist/src/graph.js +4 -1
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +297 -45
- package/dist/src/inventory.js +52 -8
- package/dist/src/kinds.js +13 -13
- package/dist/src/matrix.js +18 -9
- package/dist/src/mdx-entries.js +197 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/overrides.js +41 -10
- package/dist/src/print.js +1 -0
- package/dist/src/ref.js +10 -0
- package/dist/src/references.js +4 -1
- package/dist/src/report.js +4 -2
- package/dist/src/resolve.js +2 -0
- package/dist/src/roots.js +110 -38
- package/dist/src/schema.js +1 -0
- package/dist/src/serialization-contract.js +54 -8
- package/dist/src/session.js +7 -0
- package/dist/src/source-files.js +39 -14
- package/dist/src/suppress-file.js +10 -2
- package/dist/src/suppress-inline.js +18 -2
- package/dist/src/suppress.js +7 -5
- package/dist/src/sweep.js +27 -10
- package/dist/src/symbol-keys.js +103 -2
- package/dist/src/test-support.js +121 -17
- package/dist/src/type-errors.js +34 -7
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/verbs/analyze.js +16 -0
- package/dist/src/verbs/explain.js +4 -2
- package/dist/src/version.js +1 -1
- package/dist/src/workflow-steps.js +119 -22
- package/dist/src/workspace-programs.js +7 -3
- package/dist/src/workspace-resolution.js +53 -10
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/analysis.ts +143 -10
- package/src/augmentations.ts +21 -10
- package/src/automatic-types.ts +246 -0
- package/src/base-url.ts +24 -0
- package/src/components.ts +7 -6
- package/src/config.ts +3 -0
- package/src/configuration-files.ts +66 -12
- package/src/conformance-record.ts +2 -2
- package/src/convention-rows.ts +103 -0
- package/src/conventions.ts +121 -7
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +247 -41
- package/src/discover.ts +156 -13
- package/src/entry-point-gaps.ts +8 -11
- package/src/entry-points.ts +133 -11
- package/src/finding.ts +10 -0
- package/src/findings/completion.ts +147 -24
- package/src/findings/dependencies-and-module-machinery.ts +90 -28
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +56 -14
- package/src/findings/non-code-artifacts.ts +81 -41
- package/src/findings/unused-declarations.ts +25 -1
- package/src/findings/visibility-narrowing.ts +32 -3
- package/src/generated-file.ts +16 -0
- package/src/git-ignore.ts +98 -0
- package/src/graph.ts +6 -0
- package/src/host.ts +5 -9
- package/src/import-bindings.ts +128 -0
- package/src/indexed-access.ts +152 -30
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +394 -59
- package/src/inventory.ts +71 -7
- package/src/kinds.ts +13 -13
- package/src/matrix.ts +32 -14
- package/src/mdx-entries.ts +241 -0
- package/src/module-declarations.ts +11 -13
- package/src/overrides.ts +51 -12
- package/src/print.ts +1 -0
- package/src/ref.ts +12 -0
- package/src/references.ts +10 -1
- package/src/report.ts +16 -3
- package/src/resolve.ts +3 -0
- package/src/roots.ts +163 -45
- package/src/schema.ts +1 -0
- package/src/serialization-contract.ts +55 -4
- package/src/session.ts +7 -0
- package/src/source-files.ts +66 -16
- package/src/suppress-file.ts +12 -1
- package/src/suppress-inline.ts +18 -1
- package/src/suppress.ts +9 -5
- package/src/sweep.ts +32 -11
- package/src/symbol-keys.ts +129 -2
- package/src/test-support.ts +149 -16
- package/src/type-errors.ts +38 -6
- package/src/type-parameter-reads.ts +227 -0
- package/src/verbs/analyze.ts +22 -0
- package/src/verbs/explain.ts +5 -2
- package/src/version.ts +1 -1
- package/src/workflow-steps.ts +127 -22
- package/src/workspace-programs.ts +7 -3
- package/src/workspace-resolution.ts +62 -10
package/src/augmentations.ts
CHANGED
|
@@ -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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
+
}
|
package/src/base-url.ts
ADDED
|
@@ -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
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* test
|
|
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
|
|
443
|
-
function
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
+
corpusVersion: "6.0.0",
|
|
6
6
|
result: "pass",
|
|
7
|
-
digest: "sha256:
|
|
7
|
+
digest: "sha256:76c07ac14bda6e302444918d832b01844bac6f29b6f22a742d538804480c0769",
|
|
8
8
|
},
|
|
9
9
|
gaps: [],
|
|
10
10
|
} as const;
|
package/src/convention-rows.ts
CHANGED
|
@@ -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",
|