@cplieger/deadset-ts 7.2.0-dev.3 → 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/bin/mapper-connection.js +7 -4
- package/dist/src/analysis.js +116 -16
- 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/component-files.js +96 -31
- package/dist/src/components.js +7 -6
- package/dist/src/config.js +1 -0
- package/dist/src/configuration-files.js +54 -15
- package/dist/src/configured-declarations.js +24 -2
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/contextual-uses.js +26 -26
- 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 +185 -30
- package/dist/src/destructuring.js +77 -2
- package/dist/src/discover.js +188 -85
- package/dist/src/edges.js +1 -1
- 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 +313 -50
- package/dist/src/inventory.js +63 -8
- package/dist/src/json-document.js +5 -5
- 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/namespace-objects.js +28 -18
- package/dist/src/overrides.js +45 -9
- package/dist/src/paths.js +9 -0
- package/dist/src/print.js +1 -0
- package/dist/src/ref.js +10 -0
- package/dist/src/references.js +29 -13
- package/dist/src/reflective-lookup.js +8 -6
- package/dist/src/report.js +4 -2
- package/dist/src/reporters/template.js +22 -13
- 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 +97 -20
- package/dist/src/session.js +26 -1
- package/dist/src/source-files.js +39 -14
- package/dist/src/spelled-names.js +58 -0
- package/dist/src/suppress-file.js +12 -4
- package/dist/src/suppress-inline.js +21 -3
- 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/template-field.js +9 -6
- package/dist/src/test-support.js +121 -17
- package/dist/src/type-errors.js +116 -35
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/value-reach.js +17 -5
- package/dist/src/verbs/analyze.js +19 -1
- package/dist/src/verbs/explain.js +4 -2
- package/dist/src/version-range.js +10 -5
- 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 +72 -13
- package/dist/src/workspace.js +1 -1
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/analysis.ts +175 -16
- package/src/augmentations.ts +21 -10
- package/src/automatic-types.ts +246 -0
- package/src/base-url.ts +24 -0
- package/src/component-files.ts +116 -31
- package/src/components.ts +7 -6
- package/src/config.ts +3 -0
- package/src/configuration-files.ts +80 -18
- package/src/configured-declarations.ts +33 -4
- package/src/conformance-record.ts +2 -2
- package/src/contextual-uses.ts +27 -27
- package/src/convention-rows.ts +103 -0
- package/src/conventions.ts +121 -7
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +249 -43
- package/src/destructuring.ts +93 -2
- package/src/discover.ts +234 -86
- package/src/edges.ts +1 -1
- 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 +415 -64
- package/src/inventory.ts +83 -7
- package/src/json-document.ts +7 -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/namespace-objects.ts +34 -21
- package/src/overrides.ts +56 -9
- package/src/paths.ts +10 -0
- package/src/print.ts +1 -0
- package/src/ref.ts +12 -0
- package/src/references.ts +38 -17
- package/src/reflective-lookup.ts +9 -7
- package/src/report.ts +16 -3
- package/src/reporters/template.ts +21 -13
- package/src/resolve.ts +3 -0
- package/src/roots.ts +163 -45
- package/src/schema.ts +1 -0
- package/src/serialization-contract.ts +108 -17
- package/src/session.ts +29 -1
- package/src/source-files.ts +66 -16
- package/src/spelled-names.ts +102 -0
- package/src/suppress-file.ts +14 -3
- package/src/suppress-inline.ts +21 -2
- package/src/suppress.ts +9 -5
- package/src/sweep.ts +32 -11
- package/src/symbol-keys.ts +129 -2
- package/src/template-field.ts +9 -6
- package/src/test-support.ts +149 -16
- package/src/type-errors.ts +132 -41
- package/src/type-parameter-reads.ts +227 -0
- package/src/value-reach.ts +19 -6
- package/src/verbs/analyze.ts +28 -1
- package/src/verbs/explain.ts +5 -2
- package/src/version-range.ts +10 -5
- 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 +86 -14
- package/src/workspace.ts +1 -1
package/src/discover.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { DiagnosticCategory } from "@typescript/native/sync";
|
|
2
2
|
import type { Diagnostic } from "@typescript/native/sync";
|
|
3
|
+
import { baseUrlPaths } from "./base-url.ts";
|
|
3
4
|
import type { BuildConfiguration, ProjectConfiguration } from "./config.ts";
|
|
4
5
|
import type { Host } from "./host.ts";
|
|
5
6
|
import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
6
7
|
import type { Scope } from "./scope.ts";
|
|
7
8
|
import { SetupError } from "./setup-failure.ts";
|
|
8
9
|
import type { Engine, ProjectDiagnostics } from "./session.ts";
|
|
10
|
+
import { writtenPath, type WorkspaceProgram } from "./workspace-programs.ts";
|
|
9
11
|
|
|
10
12
|
/** Directory names discovery does not descend into. */
|
|
11
13
|
export const IGNORED_DIRECTORIES: ReadonlySet<string> = new Set(["node_modules", ".git"]);
|
|
@@ -95,20 +97,54 @@ export function diagnosticErrors(sets: {
|
|
|
95
97
|
/** Why one open project cannot be analyzed, split by whether it loaded at all. */
|
|
96
98
|
interface ProjectErrors {
|
|
97
99
|
/**
|
|
98
|
-
* The errors in the configuration itself
|
|
99
|
-
*
|
|
100
|
+
* The errors in the configuration itself, and the errors the compiler places in no file
|
|
101
|
+
* that leave its checker without a type it needs: no input matched, an option that
|
|
102
|
+
* does not parse, a missing global type. A project carrying one did not load, whatever
|
|
103
|
+
* its files say.
|
|
100
104
|
*/
|
|
101
105
|
readonly load: readonly LocatedDiagnostic[];
|
|
102
106
|
/** The errors in the files of a project that loaded: their syntax and their types. */
|
|
103
107
|
readonly check: readonly LocatedDiagnostic[];
|
|
108
|
+
/**
|
|
109
|
+
* The libraries the configuration names that the compiler cannot find, which leave it
|
|
110
|
+
* loaded: a use of a name only such a library declares is an error in its file.
|
|
111
|
+
*/
|
|
112
|
+
readonly unfound: readonly LocatedDiagnostic[];
|
|
104
113
|
}
|
|
105
114
|
|
|
115
|
+
/**
|
|
116
|
+
* The codes of a type library and a lib the configuration names that the compiler
|
|
117
|
+
* cannot find, which it reports while it gathers the program's files,
|
|
118
|
+
* https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/compiler/processingDiagnostic.go,
|
|
119
|
+
* and {@link MISSING_GLOBAL_TYPE}, which the checker reports.
|
|
120
|
+
*/
|
|
121
|
+
const UNFOUND_LIBRARY: ReadonlySet<number> = new Set([2318, 2688, 2726, 2727]);
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The code of a global type the checker finds in no library the program holds. The checker
|
|
125
|
+
* types every use of one against nothing and places no error in the file, so the analysis
|
|
126
|
+
* skips each file of such a project whole.
|
|
127
|
+
*/
|
|
128
|
+
export const MISSING_GLOBAL_TYPE = 2318;
|
|
129
|
+
|
|
106
130
|
/** The errors one open project carries, each located in the file it names. */
|
|
107
131
|
export function projectErrors(sets: ProjectDiagnostics): ProjectErrors {
|
|
108
|
-
|
|
132
|
+
// The compiler numbers its semantic messages from 2000 and its option messages from
|
|
133
|
+
// 5000. A file-less option error, a removed option or an output layout, leaves the
|
|
134
|
+
// program whole, and the compiler checks it all the same:
|
|
135
|
+
// https://github.com/microsoft/TypeScript/wiki/Coding-guidelines#diagnostic-message-codes
|
|
136
|
+
const semantic = sets.unplaced.filter(
|
|
137
|
+
(diagnostic) => isError(diagnostic) && diagnostic.code >= 2000 && diagnostic.code < 3000,
|
|
138
|
+
);
|
|
139
|
+
const unfound = semantic.filter(({ code }) => UNFOUND_LIBRARY.has(code));
|
|
140
|
+
const load = [
|
|
141
|
+
...sets.configParsing.filter(isError),
|
|
142
|
+
...semantic.filter(({ code }) => !UNFOUND_LIBRARY.has(code)),
|
|
143
|
+
];
|
|
109
144
|
return {
|
|
110
145
|
load: locatedIn(load, sets.locate),
|
|
111
146
|
check: load.length > 0 ? [] : locatedIn(diagnosticErrors(sets), sets.locate),
|
|
147
|
+
unfound: load.length > 0 ? [] : locatedIn(unfound, sets.locate),
|
|
112
148
|
};
|
|
113
149
|
}
|
|
114
150
|
|
|
@@ -182,10 +218,6 @@ interface Discovery {
|
|
|
182
218
|
readonly fromScope: readonly string[];
|
|
183
219
|
}
|
|
184
220
|
|
|
185
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
186
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
221
|
/**
|
|
190
222
|
* The configuration files below `root`, in ascending path order, skipping an
|
|
191
223
|
* ignored directory. A directory that cannot be read is skipped: a tree the
|
|
@@ -221,45 +253,6 @@ function configFilesUnder(host: Host, root: string): string[] {
|
|
|
221
253
|
return found.sort();
|
|
222
254
|
}
|
|
223
255
|
|
|
224
|
-
/**
|
|
225
|
-
* The configuration files one configuration's `references` name, resolved against
|
|
226
|
-
* that configuration's own directory. A reference naming a directory names the
|
|
227
|
-
* `tsconfig.json` in it, which is the compiler's own rule.
|
|
228
|
-
*/
|
|
229
|
-
function referencesOf(host: Host, configFile: string): string[] {
|
|
230
|
-
let text: string;
|
|
231
|
-
try {
|
|
232
|
-
text = host.readFile(configFile);
|
|
233
|
-
} catch {
|
|
234
|
-
return [];
|
|
235
|
-
}
|
|
236
|
-
let value: unknown;
|
|
237
|
-
try {
|
|
238
|
-
value = JSON.parse(text);
|
|
239
|
-
} catch {
|
|
240
|
-
// What a configuration's contents say is refused by the compiler's own
|
|
241
|
-
// diagnostics, which name the position; nothing is inferred from it here.
|
|
242
|
-
return [];
|
|
243
|
-
}
|
|
244
|
-
if (!isRecord(value) || !Array.isArray(value["references"])) {
|
|
245
|
-
return [];
|
|
246
|
-
}
|
|
247
|
-
const dir = dirnamePath(configFile);
|
|
248
|
-
const found: string[] = [];
|
|
249
|
-
for (const entry of value["references"] as unknown[]) {
|
|
250
|
-
if (!isRecord(entry)) {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
const path = entry["path"];
|
|
254
|
-
if (typeof path !== "string" || path === "") {
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
const resolved = isAbsolutePath(path) ? path : joinPath(dir, path);
|
|
258
|
-
found.push(resolved.endsWith(".json") ? resolved : joinPath(resolved, "tsconfig.json"));
|
|
259
|
-
}
|
|
260
|
-
return found;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
256
|
/**
|
|
264
257
|
* The configuration file the scope names for the target directly, where its path
|
|
265
258
|
* names one rather than a directory. A target naming a directory is reached by the
|
|
@@ -274,15 +267,16 @@ function scopeConfigFiles(host: Host, scope: Scope): string[] {
|
|
|
274
267
|
|
|
275
268
|
/**
|
|
276
269
|
* Every configuration file reached from `pending`, in the order discovery reads them:
|
|
277
|
-
* each once, each followed by the configurations it references
|
|
278
|
-
* configuration the compiler cannot read ends the run naming it, unless
|
|
279
|
-
* is given, which is then told the configuration and why, and the walk
|
|
280
|
-
* without it.
|
|
270
|
+
* each once, each followed by the configurations it references that `follows` admits,
|
|
271
|
+
* transitively. A configuration the compiler cannot read ends the run naming it, unless
|
|
272
|
+
* `unreadable` is given, which is then told the configuration and why, and the walk
|
|
273
|
+
* goes on without it.
|
|
281
274
|
*/
|
|
282
275
|
function derivedFrom(
|
|
283
276
|
engine: Engine,
|
|
284
277
|
host: Host,
|
|
285
278
|
pending: string[],
|
|
279
|
+
follows: (reference: string) => boolean,
|
|
286
280
|
unreadable?: (configFile: string, reason: string) => void,
|
|
287
281
|
): string[] {
|
|
288
282
|
const seen = new Set<string>();
|
|
@@ -297,19 +291,19 @@ function derivedFrom(
|
|
|
297
291
|
continue;
|
|
298
292
|
}
|
|
299
293
|
seen.add(path);
|
|
300
|
-
const
|
|
301
|
-
if (reason
|
|
294
|
+
const read = readConfiguration(engine, path);
|
|
295
|
+
if ("reason" in read) {
|
|
302
296
|
if (unreadable === undefined) {
|
|
303
297
|
throw new DiscoveryError(
|
|
304
|
-
`${path} cannot be read as a compiler configuration: ${reason}`,
|
|
298
|
+
`${path} cannot be read as a compiler configuration: ${read.reason}`,
|
|
305
299
|
[],
|
|
306
300
|
);
|
|
307
301
|
}
|
|
308
|
-
unreadable(path, reason);
|
|
302
|
+
unreadable(path, read.reason);
|
|
309
303
|
continue;
|
|
310
304
|
}
|
|
311
305
|
found.push(path);
|
|
312
|
-
pending.push(...
|
|
306
|
+
pending.push(...read.references.filter(follows));
|
|
313
307
|
}
|
|
314
308
|
return found;
|
|
315
309
|
}
|
|
@@ -335,7 +329,7 @@ export function consumerProjects(engine: Engine, host: Host, root: string): read
|
|
|
335
329
|
if (kind !== "directory") {
|
|
336
330
|
throw new DiscoveryError(`the consumer ${root} is not a directory`, []);
|
|
337
331
|
}
|
|
338
|
-
const found = derivedFrom(engine, host, configFilesUnder(host, root));
|
|
332
|
+
const found = derivedFrom(engine, host, configFilesUnder(host, root), () => true);
|
|
339
333
|
if (found.length === 0) {
|
|
340
334
|
throw new DiscoveryError(
|
|
341
335
|
`the consumer ${root} holds no compiler configuration, so none of its references can be read`,
|
|
@@ -353,39 +347,150 @@ function derivedId(targetRoot: string, configFile: string): string {
|
|
|
353
347
|
return relativePath(targetRoot, configFile) ?? configFile;
|
|
354
348
|
}
|
|
355
349
|
|
|
356
|
-
/**
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
350
|
+
/**
|
|
351
|
+
* The code of a configuration whose `extends` chain returns to a configuration it already
|
|
352
|
+
* passed. The parse reports it and returns, and a snapshot opened from it never answers:
|
|
353
|
+
* https://github.com/microsoft/TypeScript/issues/64754
|
|
354
|
+
*/
|
|
355
|
+
const CIRCULAR_EXTENDS = 18000;
|
|
356
|
+
|
|
357
|
+
type Parsed = ReturnType<Engine["parseConfigFile"]>;
|
|
358
|
+
|
|
359
|
+
/** The configuration files one parsed configuration's own `extends` names by path. */
|
|
360
|
+
function extendedPaths(parsed: Parsed, configFile: string): readonly string[] {
|
|
361
|
+
const { raw } = parsed as { readonly raw?: unknown };
|
|
362
|
+
const named: unknown = typeof raw === "object" && raw !== null ? Reflect.get(raw, "extends") : [];
|
|
363
|
+
return (Array.isArray(named) ? named : [named])
|
|
364
|
+
.filter((one): one is string => typeof one === "string")
|
|
365
|
+
.filter((one) => one.startsWith("./") || one.startsWith("../") || isAbsolutePath(one))
|
|
366
|
+
.map((one) => resolvePath(dirnamePath(configFile), one));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* The `extends` chain from `configFile` to the first configuration it reaches twice, each
|
|
371
|
+
* link by path, or undefined where no chain written by path returns.
|
|
372
|
+
*/
|
|
373
|
+
function extendsCycle(
|
|
374
|
+
engine: Engine,
|
|
375
|
+
configFile: string,
|
|
376
|
+
parsed: Parsed,
|
|
377
|
+
): readonly string[] | undefined {
|
|
378
|
+
const walk = (chain: readonly string[], at: Parsed): readonly string[] | undefined => {
|
|
379
|
+
for (const next of extendedPaths(at, chain.at(-1) ?? configFile)) {
|
|
380
|
+
if (chain.includes(next)) {
|
|
381
|
+
return [...chain, next];
|
|
382
|
+
}
|
|
383
|
+
let read: Parsed;
|
|
384
|
+
try {
|
|
385
|
+
read = engine.parseConfigFile(next);
|
|
386
|
+
} catch {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const cycle = walk([...chain, next], read);
|
|
390
|
+
if (cycle !== undefined) {
|
|
391
|
+
return cycle;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return undefined;
|
|
395
|
+
};
|
|
396
|
+
return walk([configFile], parsed);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** The configuration files one parsed configuration's `references` name. */
|
|
400
|
+
function referencesOf(parsed: Parsed): readonly string[] {
|
|
401
|
+
// The parse answers a directory reference as the directory; the compiler reads a
|
|
402
|
+
// path not ending `.json` as its `tsconfig.json`:
|
|
403
|
+
// https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/core/projectreference.go#L18
|
|
404
|
+
return (parsed.projectReferences ?? []).map(({ path }) =>
|
|
405
|
+
path.endsWith(".json") ? path : joinPath(path, "tsconfig.json"),
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** Why one parsed configuration's own `extends` chain cannot be opened, where it is circular. */
|
|
410
|
+
function circularity(engine: Engine, configFile: string, parsed: Parsed): string | undefined {
|
|
411
|
+
const chain = extendsCycle(engine, configFile, parsed);
|
|
412
|
+
if (chain === undefined && !parsed.errors.some(({ code }) => code === CIRCULAR_EXTENDS)) {
|
|
360
413
|
return undefined;
|
|
414
|
+
}
|
|
415
|
+
return `TS${String(CIRCULAR_EXTENDS)}: Circularity detected while resolving configuration: ${(chain ?? [configFile]).join(" -> ")}`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Why a configuration the `references` reach, transitively, cannot be opened, where one's
|
|
420
|
+
* `extends` chain is circular: the compiler opens every referenced project with the one
|
|
421
|
+
* that references it.
|
|
422
|
+
*/
|
|
423
|
+
function referencedCircularity(
|
|
424
|
+
engine: Engine,
|
|
425
|
+
references: readonly string[],
|
|
426
|
+
visited: Set<string>,
|
|
427
|
+
): string | undefined {
|
|
428
|
+
for (const reference of references) {
|
|
429
|
+
if (visited.has(reference)) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
visited.add(reference);
|
|
433
|
+
let parsed: Parsed;
|
|
434
|
+
try {
|
|
435
|
+
parsed = engine.parseConfigFile(reference);
|
|
436
|
+
} catch {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const reason =
|
|
440
|
+
circularity(engine, reference, parsed) ??
|
|
441
|
+
referencedCircularity(engine, referencesOf(parsed), visited);
|
|
442
|
+
if (reason !== undefined) {
|
|
443
|
+
return reason;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return undefined;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* The configuration files one configuration's `references` name, or why the compiler
|
|
451
|
+
* cannot read the configuration: it throws, or its `extends` chain or that of a
|
|
452
|
+
* configuration it references is circular, which the analysis refuses before any
|
|
453
|
+
* snapshot is asked to open it.
|
|
454
|
+
*/
|
|
455
|
+
function readConfiguration(
|
|
456
|
+
engine: Engine,
|
|
457
|
+
configFile: string,
|
|
458
|
+
): { readonly references: readonly string[] } | { readonly reason: string } {
|
|
459
|
+
try {
|
|
460
|
+
const parsed = engine.parseConfigFile(configFile);
|
|
461
|
+
const references = referencesOf(parsed);
|
|
462
|
+
const reason =
|
|
463
|
+
circularity(engine, configFile, parsed) ??
|
|
464
|
+
referencedCircularity(engine, references, new Set([configFile]));
|
|
465
|
+
return reason === undefined ? { references } : { reason };
|
|
361
466
|
} catch (error: unknown) {
|
|
362
|
-
return error instanceof Error ? error.message : String(error);
|
|
467
|
+
return { reason: error instanceof Error ? error.message : String(error) };
|
|
363
468
|
}
|
|
364
469
|
}
|
|
365
470
|
|
|
366
471
|
/**
|
|
367
|
-
*
|
|
368
|
-
* opened from a configuration the compiler cannot read would be analyzed with
|
|
369
|
-
* type information.
|
|
472
|
+
* The references of one compiler configuration file, or ends the run naming it: a
|
|
473
|
+
* project opened from a configuration the compiler cannot read would be analyzed with
|
|
474
|
+
* no type information.
|
|
370
475
|
*/
|
|
371
|
-
function parseOrRefuse(engine: Engine, configFile: string, named: string):
|
|
372
|
-
const
|
|
373
|
-
if (reason
|
|
374
|
-
throw new DiscoveryError(
|
|
476
|
+
function parseOrRefuse(engine: Engine, configFile: string, named: string): readonly string[] {
|
|
477
|
+
const read = readConfiguration(engine, configFile);
|
|
478
|
+
if ("reason" in read) {
|
|
479
|
+
throw new DiscoveryError(
|
|
480
|
+
`${named} cannot be read as a compiler configuration: ${read.reason}`,
|
|
481
|
+
[],
|
|
482
|
+
);
|
|
375
483
|
}
|
|
484
|
+
return read.references;
|
|
376
485
|
}
|
|
377
486
|
|
|
378
487
|
/**
|
|
379
|
-
* The projects a build matrix declares, in
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
* naming both: the compiler builds a referenced project's sources into the program
|
|
386
|
-
* that references it without reporting their diagnostics, so the project the matrix
|
|
387
|
-
* left out would be analyzed with its errors unread. An entry naming a file an
|
|
388
|
-
* earlier entry named adds nothing, so the earlier identifier stands.
|
|
488
|
+
* The projects a build matrix declares, in its order, each entry a file below the target
|
|
489
|
+
* root: an entry naming an absent file, a directory or a file the compiler cannot read
|
|
490
|
+
* ends the run naming it, and one naming an earlier entry's file adds nothing. A
|
|
491
|
+
* reference below the root the matrix does not name ends the run naming both, because the
|
|
492
|
+
* compiler builds a referenced project's sources into the referencing program without
|
|
493
|
+
* reporting their diagnostics. A reference outside the root is an outside project.
|
|
389
494
|
*/
|
|
390
495
|
function declaredProjects(
|
|
391
496
|
engine: Engine,
|
|
@@ -395,6 +500,7 @@ function declaredProjects(
|
|
|
395
500
|
): Discovery {
|
|
396
501
|
const seen = new Set<string>();
|
|
397
502
|
const projects: DiscoveredProject[] = [];
|
|
503
|
+
const references = new Map<string, readonly string[]>();
|
|
398
504
|
for (const entry of entries) {
|
|
399
505
|
const configFile = joinPath(targetRoot, entry.project);
|
|
400
506
|
const named = `the project ${JSON.stringify(entry.id)} (${configFile})`;
|
|
@@ -411,11 +517,13 @@ function declaredProjects(
|
|
|
411
517
|
continue;
|
|
412
518
|
}
|
|
413
519
|
seen.add(configFile);
|
|
414
|
-
parseOrRefuse(engine, configFile, named);
|
|
520
|
+
references.set(configFile, parseOrRefuse(engine, configFile, named));
|
|
415
521
|
projects.push({ id: entry.id, configFile });
|
|
416
522
|
}
|
|
417
523
|
for (const project of projects) {
|
|
418
|
-
const unnamed =
|
|
524
|
+
const unnamed = references
|
|
525
|
+
.get(project.configFile)
|
|
526
|
+
?.find((path) => !seen.has(path) && relativePath(targetRoot, path) !== undefined);
|
|
419
527
|
if (unnamed !== undefined) {
|
|
420
528
|
throw new DiscoveryError(
|
|
421
529
|
`the project ${JSON.stringify(project.id)} (${project.configFile}) references ` +
|
|
@@ -435,21 +543,26 @@ function declaredProjects(
|
|
|
435
543
|
}
|
|
436
544
|
|
|
437
545
|
/**
|
|
438
|
-
* The projects a run derives where the matrix declares none, in
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
* each
|
|
442
|
-
*
|
|
443
|
-
* it cannot be read; one the walk found is dropped and named.
|
|
546
|
+
* The projects a run derives where the matrix declares none, in discovery order: the
|
|
547
|
+
* paths the scope names, every compiler configuration under the target root outside an
|
|
548
|
+
* ignored directory, then each configuration below the root those reference,
|
|
549
|
+
* transitively, each once. A reference outside the root is an outside project, whose
|
|
550
|
+
* sources the compiler builds into the referencing program. A configuration the scope
|
|
551
|
+
* names ends the run where it cannot be read; one the walk found is dropped and named.
|
|
444
552
|
*/
|
|
445
553
|
function derivedProjects(engine: Engine, host: Host, scope: Scope): Discovery {
|
|
446
554
|
const targetRoot = scope.target.path;
|
|
447
555
|
const fromScope = scopeConfigFiles(host, scope);
|
|
448
556
|
const notBuilt: NotBuilt[] = [];
|
|
557
|
+
const rootDir = resolvePath(
|
|
558
|
+
host.workingDirectory(),
|
|
559
|
+
fromScope.length > 0 ? dirnamePath(targetRoot) : targetRoot,
|
|
560
|
+
);
|
|
449
561
|
const configFiles = derivedFrom(
|
|
450
562
|
engine,
|
|
451
563
|
host,
|
|
452
564
|
[...fromScope, ...configFilesUnder(host, targetRoot)],
|
|
565
|
+
(reference) => relativePath(rootDir, reference) !== undefined,
|
|
453
566
|
(configFile, reason) => {
|
|
454
567
|
if (fromScope.includes(configFile)) {
|
|
455
568
|
throw new DiscoveryError(
|
|
@@ -503,3 +616,38 @@ export function discoverProjects(
|
|
|
503
616
|
}
|
|
504
617
|
return derivedProjects(engine, host, scope);
|
|
505
618
|
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* How the snapshot reads each configuration of `configFiles` that sets `baseUrl` and
|
|
622
|
+
* `programs` does not already describe: a configuration only the snapshot holds, which
|
|
623
|
+
* extends it and states the `paths` {@link baseUrlPaths} gives, so its imports resolve as
|
|
624
|
+
* the compilers before version 7 resolve them. A configuration the compiler cannot read is
|
|
625
|
+
* left for its project to refuse.
|
|
626
|
+
*/
|
|
627
|
+
export function withBaseUrlPaths(
|
|
628
|
+
engine: Engine,
|
|
629
|
+
host: Host,
|
|
630
|
+
configFiles: readonly string[],
|
|
631
|
+
programs: ReadonlyMap<string, WorkspaceProgram>,
|
|
632
|
+
): ReadonlyMap<string, WorkspaceProgram> {
|
|
633
|
+
const found = new Map(programs);
|
|
634
|
+
for (const configFile of configFiles) {
|
|
635
|
+
if (found.has(configFile)) {
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
let paths: Record<string, string[]> | undefined;
|
|
639
|
+
try {
|
|
640
|
+
paths = baseUrlPaths(engine.parseConfigFile(configFile).options);
|
|
641
|
+
} catch {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (paths !== undefined) {
|
|
645
|
+
found.set(configFile, {
|
|
646
|
+
kind: "configuration",
|
|
647
|
+
file: writtenPath(host, configFile),
|
|
648
|
+
text: `${JSON.stringify({ extends: configFile, compilerOptions: { paths } }, null, 2)}\n`,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return found;
|
|
653
|
+
}
|
package/src/edges.ts
CHANGED
|
@@ -156,7 +156,7 @@ export function readEdgeSides(host: Host, targetRoot: string): readonly EdgeSide
|
|
|
156
156
|
|
|
157
157
|
const sides: EdgeSide[] = [];
|
|
158
158
|
(edges as readonly unknown[]).forEach((value, index) => {
|
|
159
|
-
const offset = walk.
|
|
159
|
+
const offset = walk.starts[index];
|
|
160
160
|
const at =
|
|
161
161
|
offset === undefined ? EDGES_FILE : positionKey(positionAt(text, EDGES_FILE, offset));
|
|
162
162
|
const edge = readEdge(value, at);
|
package/src/entry-point-gaps.ts
CHANGED
|
@@ -58,7 +58,6 @@ const UNREAD_TOOLS: readonly string[] = [
|
|
|
58
58
|
"cypress",
|
|
59
59
|
"danger",
|
|
60
60
|
"dependency-cruiser",
|
|
61
|
-
"docusaurus",
|
|
62
61
|
"dotenv",
|
|
63
62
|
"drizzle",
|
|
64
63
|
"electron-vite",
|
|
@@ -112,14 +111,12 @@ const UNREAD_TOOLS: readonly string[] = [
|
|
|
112
111
|
"nuxtjs-i18n",
|
|
113
112
|
"nx",
|
|
114
113
|
"nyc",
|
|
115
|
-
"oclif",
|
|
116
114
|
"openapi-ts",
|
|
117
115
|
"openclaw",
|
|
118
116
|
"orval",
|
|
119
117
|
"oxfmt",
|
|
120
118
|
"oxlint",
|
|
121
119
|
"panda-css",
|
|
122
|
-
"parcel",
|
|
123
120
|
"payload",
|
|
124
121
|
"pino",
|
|
125
122
|
"playwright-ct",
|
|
@@ -164,7 +161,6 @@ const UNREAD_TOOLS: readonly string[] = [
|
|
|
164
161
|
"swc",
|
|
165
162
|
"syncpack",
|
|
166
163
|
"tailwind",
|
|
167
|
-
"tanstack-router",
|
|
168
164
|
"taskfile",
|
|
169
165
|
"tauri",
|
|
170
166
|
"temporal",
|
|
@@ -210,18 +206,19 @@ const UNREAD_TOOLS: readonly string[] = [
|
|
|
210
206
|
/** The conventions declined for a tool some of whose conventions are entered. */
|
|
211
207
|
const PARTLY_ENTERED: readonly DeclinedConvention[] = [
|
|
212
208
|
{
|
|
213
|
-
tool: "
|
|
214
|
-
convention: "a
|
|
209
|
+
tool: "docusaurus",
|
|
210
|
+
convention: "a page or theme directory a plugin's configuration moves, and the docs",
|
|
215
211
|
reason:
|
|
216
|
-
"
|
|
217
|
-
"
|
|
212
|
+
"The convention row reads src/pages and src/theme only, and a document's imports are " +
|
|
213
|
+
"read from every Markdown and MDX file, so a module only a moved directory holds is " +
|
|
214
|
+
"reported as never imported unless ts.entry_files names it.",
|
|
218
215
|
},
|
|
219
216
|
{
|
|
220
217
|
tool: "eslint",
|
|
221
|
-
convention: "a
|
|
218
|
+
convention: "a legacy configuration written in YAML or in JSON with comments",
|
|
222
219
|
reason:
|
|
223
|
-
"
|
|
224
|
-
"dependency only a
|
|
220
|
+
"Only a configuration module and a strict JSON configuration file are read, so a " +
|
|
221
|
+
"dependency only such a file names is reported as unused.",
|
|
225
222
|
},
|
|
226
223
|
{
|
|
227
224
|
tool: "playwright",
|