@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
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
* it. A rest element names no property: what it copies is read where the copy is.
|
|
7
7
|
*/
|
|
8
8
|
import { isArrayLiteralExpression, isBinaryExpression, isForOfStatement, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, isParameterDeclaration, isParenthesizedExpression, isPropertyAssignment, isShorthandPropertyAssignment, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
9
|
-
import { TypeFlags } from "@typescript/native/sync";
|
|
9
|
+
import { SignatureKind, TypeFlags, } from "@typescript/native/sync";
|
|
10
10
|
import { isAnswered, propertyTables, UNANSWERED, } from "./query.js";
|
|
11
|
+
/** The types of an iteration result's `value` that carry no element of the iterable. */
|
|
12
|
+
const NO_ELEMENT = TypeFlags.AnyOrUnknown | TypeFlags.Undefined | TypeFlags.Void | TypeFlags.Null | TypeFlags.Never;
|
|
11
13
|
/** The text a property name spells, or `undefined` for a computed name. */
|
|
12
14
|
function nameText(node) {
|
|
13
15
|
if (node === undefined) {
|
|
@@ -207,11 +209,84 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
207
209
|
const elements = queries.typeArguments(type);
|
|
208
210
|
return isAnswered(elements) ? elements[0] : UNANSWERED;
|
|
209
211
|
};
|
|
212
|
+
/** The type of the one property of a type that `matches` accepts, unanswered where two do. */
|
|
213
|
+
const propertyTypeOf = (owner, matches) => {
|
|
214
|
+
const table = tableOf(owner);
|
|
215
|
+
if (!isAnswered(table)) {
|
|
216
|
+
return UNANSWERED;
|
|
217
|
+
}
|
|
218
|
+
const [property, ...more] = [...table.values()].filter(matches);
|
|
219
|
+
if (property === undefined) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
if (more.length > 0) {
|
|
223
|
+
return UNANSWERED;
|
|
224
|
+
}
|
|
225
|
+
patternLookups += 1;
|
|
226
|
+
return queries.typeOfSymbol(property);
|
|
227
|
+
};
|
|
228
|
+
const resultOf = (callee) => {
|
|
229
|
+
if (callee === undefined || callee === UNANSWERED) {
|
|
230
|
+
return callee;
|
|
231
|
+
}
|
|
232
|
+
patternLookups += 1;
|
|
233
|
+
const signatures = queries.signaturesOf(callee, SignatureKind.Call);
|
|
234
|
+
if (!isAnswered(signatures)) {
|
|
235
|
+
return UNANSWERED;
|
|
236
|
+
}
|
|
237
|
+
const [signature] = signatures;
|
|
238
|
+
if (signature === undefined) {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
patternLookups += 1;
|
|
242
|
+
return queries.returnTypeOf(signature);
|
|
243
|
+
};
|
|
244
|
+
const iterations = new Map();
|
|
245
|
+
/**
|
|
246
|
+
* The type a `for…of` takes from one iterable that is no array, read through the
|
|
247
|
+
* iteration protocol: the `value` of what the `next()` of its `[Symbol.iterator]()`
|
|
248
|
+
* answers. A constituent of that answer whose value is {@link NO_ELEMENT} carries no
|
|
249
|
+
* element; where more than one carries one, the element is left unanswered, so a read
|
|
250
|
+
* through it is resolved by name.
|
|
251
|
+
*/
|
|
252
|
+
const iteratedOf = (type) => {
|
|
253
|
+
if (iterations.has(type.id)) {
|
|
254
|
+
return iterations.get(type.id);
|
|
255
|
+
}
|
|
256
|
+
// The checker keys a member a unique symbol names `__@<name>@<symbol id>`, and escapes
|
|
257
|
+
// a written key that starts with `__` with one more `_`, so no string key matches; a
|
|
258
|
+
// unique symbol of the program's own named `iterator` does, which leaves two matches:
|
|
259
|
+
// https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/checker/checker.go#L23096-L23108
|
|
260
|
+
// https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/ast/symbol.go#L91-L103
|
|
261
|
+
const iterator = resultOf(propertyTypeOf(type, (one) => one.escapedName.startsWith("__@iterator@")));
|
|
262
|
+
const result = iterator === undefined || iterator === UNANSWERED
|
|
263
|
+
? iterator
|
|
264
|
+
: resultOf(propertyTypeOf(iterator, (one) => one.name === "next"));
|
|
265
|
+
let found = result;
|
|
266
|
+
if (result !== undefined && result !== UNANSWERED) {
|
|
267
|
+
let parts = [result];
|
|
268
|
+
if (result.isUnionType()) {
|
|
269
|
+
patternLookups += 1;
|
|
270
|
+
parts = queries.constituents(result);
|
|
271
|
+
}
|
|
272
|
+
const values = isAnswered(parts)
|
|
273
|
+
? parts.map((part) => propertyTypeOf(part, (one) => one.name === "value"))
|
|
274
|
+
: [UNANSWERED];
|
|
275
|
+
const carried = values.filter((value) => value === UNANSWERED || (value !== undefined && (value.flags & NO_ELEMENT) === 0));
|
|
276
|
+
const [only, ...more] = carried;
|
|
277
|
+
found = more.length > 0 ? UNANSWERED : only;
|
|
278
|
+
}
|
|
279
|
+
iterations.set(type.id, found);
|
|
280
|
+
return found;
|
|
281
|
+
};
|
|
210
282
|
const step = (type, next) => {
|
|
211
283
|
const defined = definedOf(type);
|
|
212
284
|
if (defined === undefined || defined === UNANSWERED) {
|
|
213
285
|
return defined;
|
|
214
286
|
}
|
|
287
|
+
if (next.kind === "iterated") {
|
|
288
|
+
return elementOf(defined, undefined) ?? iteratedOf(defined);
|
|
289
|
+
}
|
|
215
290
|
if (next.kind === "property") {
|
|
216
291
|
const table = tableOf(defined);
|
|
217
292
|
if (!isAnswered(table)) {
|
|
@@ -224,7 +299,7 @@ export function destructuring(project, cap, properties = propertyTables(project.
|
|
|
224
299
|
patternLookups += 1;
|
|
225
300
|
return queries.typeOfSymbol(property);
|
|
226
301
|
}
|
|
227
|
-
return elementOf(defined, next.
|
|
302
|
+
return elementOf(defined, next.index);
|
|
228
303
|
};
|
|
229
304
|
const resolve = (reads) => {
|
|
230
305
|
const patterns = [...new Set(reads.map((read) => read.pattern))];
|
package/dist/src/discover.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DiagnosticCategory } from "@typescript/native/sync";
|
|
2
|
+
import { baseUrlPaths } from "./base-url.js";
|
|
2
3
|
import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
3
4
|
import { SetupError } from "./setup-failure.js";
|
|
5
|
+
import { writtenPath } from "./workspace-programs.js";
|
|
4
6
|
/** Directory names discovery does not descend into. */
|
|
5
7
|
export const IGNORED_DIRECTORIES = new Set(["node_modules", ".git"]);
|
|
6
8
|
/** A compiler configuration file discovery recognises under a root. */
|
|
@@ -60,12 +62,35 @@ function locatedIn(diagnostics, locate) {
|
|
|
60
62
|
export function diagnosticErrors(sets) {
|
|
61
63
|
return [...sets.configParsing, ...sets.syntactic, ...sets.semantic].filter(isError);
|
|
62
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The codes of a type library and a lib the configuration names that the compiler
|
|
67
|
+
* cannot find, which it reports while it gathers the program's files,
|
|
68
|
+
* https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/compiler/processingDiagnostic.go,
|
|
69
|
+
* and {@link MISSING_GLOBAL_TYPE}, which the checker reports.
|
|
70
|
+
*/
|
|
71
|
+
const UNFOUND_LIBRARY = new Set([2318, 2688, 2726, 2727]);
|
|
72
|
+
/**
|
|
73
|
+
* The code of a global type the checker finds in no library the program holds. The checker
|
|
74
|
+
* types every use of one against nothing and places no error in the file, so the analysis
|
|
75
|
+
* skips each file of such a project whole.
|
|
76
|
+
*/
|
|
77
|
+
export const MISSING_GLOBAL_TYPE = 2318;
|
|
63
78
|
/** The errors one open project carries, each located in the file it names. */
|
|
64
79
|
export function projectErrors(sets) {
|
|
65
|
-
|
|
80
|
+
// The compiler numbers its semantic messages from 2000 and its option messages from
|
|
81
|
+
// 5000. A file-less option error, a removed option or an output layout, leaves the
|
|
82
|
+
// program whole, and the compiler checks it all the same:
|
|
83
|
+
// https://github.com/microsoft/TypeScript/wiki/Coding-guidelines#diagnostic-message-codes
|
|
84
|
+
const semantic = sets.unplaced.filter((diagnostic) => isError(diagnostic) && diagnostic.code >= 2000 && diagnostic.code < 3000);
|
|
85
|
+
const unfound = semantic.filter(({ code }) => UNFOUND_LIBRARY.has(code));
|
|
86
|
+
const load = [
|
|
87
|
+
...sets.configParsing.filter(isError),
|
|
88
|
+
...semantic.filter(({ code }) => !UNFOUND_LIBRARY.has(code)),
|
|
89
|
+
];
|
|
66
90
|
return {
|
|
67
91
|
load: locatedIn(load, sets.locate),
|
|
68
92
|
check: load.length > 0 ? [] : locatedIn(diagnosticErrors(sets), sets.locate),
|
|
93
|
+
unfound: load.length > 0 ? [] : locatedIn(unfound, sets.locate),
|
|
69
94
|
};
|
|
70
95
|
}
|
|
71
96
|
/**
|
|
@@ -86,9 +111,6 @@ export function targetRelative(text, targetRoot) {
|
|
|
86
111
|
export function isGuess(discovery, configFile) {
|
|
87
112
|
return discovery.derived && !discovery.fromScope.includes(configFile);
|
|
88
113
|
}
|
|
89
|
-
function isRecord(value) {
|
|
90
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
91
|
-
}
|
|
92
114
|
/**
|
|
93
115
|
* The configuration files below `root`, in ascending path order, skipping an
|
|
94
116
|
* ignored directory. A directory that cannot be read is skipped: a tree the
|
|
@@ -124,46 +146,6 @@ function configFilesUnder(host, root) {
|
|
|
124
146
|
}
|
|
125
147
|
return found.sort();
|
|
126
148
|
}
|
|
127
|
-
/**
|
|
128
|
-
* The configuration files one configuration's `references` name, resolved against
|
|
129
|
-
* that configuration's own directory. A reference naming a directory names the
|
|
130
|
-
* `tsconfig.json` in it, which is the compiler's own rule.
|
|
131
|
-
*/
|
|
132
|
-
function referencesOf(host, configFile) {
|
|
133
|
-
let text;
|
|
134
|
-
try {
|
|
135
|
-
text = host.readFile(configFile);
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
let value;
|
|
141
|
-
try {
|
|
142
|
-
value = JSON.parse(text);
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
// What a configuration's contents say is refused by the compiler's own
|
|
146
|
-
// diagnostics, which name the position; nothing is inferred from it here.
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
if (!isRecord(value) || !Array.isArray(value["references"])) {
|
|
150
|
-
return [];
|
|
151
|
-
}
|
|
152
|
-
const dir = dirnamePath(configFile);
|
|
153
|
-
const found = [];
|
|
154
|
-
for (const entry of value["references"]) {
|
|
155
|
-
if (!isRecord(entry)) {
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
const path = entry["path"];
|
|
159
|
-
if (typeof path !== "string" || path === "") {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
const resolved = isAbsolutePath(path) ? path : joinPath(dir, path);
|
|
163
|
-
found.push(resolved.endsWith(".json") ? resolved : joinPath(resolved, "tsconfig.json"));
|
|
164
|
-
}
|
|
165
|
-
return found;
|
|
166
|
-
}
|
|
167
149
|
/**
|
|
168
150
|
* The configuration file the scope names for the target directly, where its path
|
|
169
151
|
* names one rather than a directory. A target naming a directory is reached by the
|
|
@@ -177,12 +159,12 @@ function scopeConfigFiles(host, scope) {
|
|
|
177
159
|
}
|
|
178
160
|
/**
|
|
179
161
|
* Every configuration file reached from `pending`, in the order discovery reads them:
|
|
180
|
-
* each once, each followed by the configurations it references
|
|
181
|
-
* configuration the compiler cannot read ends the run naming it, unless
|
|
182
|
-
* is given, which is then told the configuration and why, and the walk
|
|
183
|
-
* without it.
|
|
162
|
+
* each once, each followed by the configurations it references that `follows` admits,
|
|
163
|
+
* transitively. A configuration the compiler cannot read ends the run naming it, unless
|
|
164
|
+
* `unreadable` is given, which is then told the configuration and why, and the walk
|
|
165
|
+
* goes on without it.
|
|
184
166
|
*/
|
|
185
|
-
function derivedFrom(engine, host, pending, unreadable) {
|
|
167
|
+
function derivedFrom(engine, host, pending, follows, unreadable) {
|
|
186
168
|
const seen = new Set();
|
|
187
169
|
const found = [];
|
|
188
170
|
while (pending.length > 0) {
|
|
@@ -195,16 +177,16 @@ function derivedFrom(engine, host, pending, unreadable) {
|
|
|
195
177
|
continue;
|
|
196
178
|
}
|
|
197
179
|
seen.add(path);
|
|
198
|
-
const
|
|
199
|
-
if (reason
|
|
180
|
+
const read = readConfiguration(engine, path);
|
|
181
|
+
if ("reason" in read) {
|
|
200
182
|
if (unreadable === undefined) {
|
|
201
|
-
throw new DiscoveryError(`${path} cannot be read as a compiler configuration: ${reason}`, []);
|
|
183
|
+
throw new DiscoveryError(`${path} cannot be read as a compiler configuration: ${read.reason}`, []);
|
|
202
184
|
}
|
|
203
|
-
unreadable(path, reason);
|
|
185
|
+
unreadable(path, read.reason);
|
|
204
186
|
continue;
|
|
205
187
|
}
|
|
206
188
|
found.push(path);
|
|
207
|
-
pending.push(...
|
|
189
|
+
pending.push(...read.references.filter(follows));
|
|
208
190
|
}
|
|
209
191
|
return found;
|
|
210
192
|
}
|
|
@@ -229,7 +211,7 @@ export function consumerProjects(engine, host, root) {
|
|
|
229
211
|
if (kind !== "directory") {
|
|
230
212
|
throw new DiscoveryError(`the consumer ${root} is not a directory`, []);
|
|
231
213
|
}
|
|
232
|
-
const found = derivedFrom(engine, host, configFilesUnder(host, root));
|
|
214
|
+
const found = derivedFrom(engine, host, configFilesUnder(host, root), () => true);
|
|
233
215
|
if (found.length === 0) {
|
|
234
216
|
throw new DiscoveryError(`the consumer ${root} holds no compiler configuration, so none of its references can be read`, []);
|
|
235
217
|
}
|
|
@@ -242,42 +224,130 @@ export function consumerProjects(engine, host, root) {
|
|
|
242
224
|
function derivedId(targetRoot, configFile) {
|
|
243
225
|
return relativePath(targetRoot, configFile) ?? configFile;
|
|
244
226
|
}
|
|
245
|
-
/**
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
227
|
+
/**
|
|
228
|
+
* The code of a configuration whose `extends` chain returns to a configuration it already
|
|
229
|
+
* passed. The parse reports it and returns, and a snapshot opened from it never answers:
|
|
230
|
+
* https://github.com/microsoft/TypeScript/issues/64754
|
|
231
|
+
*/
|
|
232
|
+
const CIRCULAR_EXTENDS = 18000;
|
|
233
|
+
/** The configuration files one parsed configuration's own `extends` names by path. */
|
|
234
|
+
function extendedPaths(parsed, configFile) {
|
|
235
|
+
const { raw } = parsed;
|
|
236
|
+
const named = typeof raw === "object" && raw !== null ? Reflect.get(raw, "extends") : [];
|
|
237
|
+
return (Array.isArray(named) ? named : [named])
|
|
238
|
+
.filter((one) => typeof one === "string")
|
|
239
|
+
.filter((one) => one.startsWith("./") || one.startsWith("../") || isAbsolutePath(one))
|
|
240
|
+
.map((one) => resolvePath(dirnamePath(configFile), one));
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* The `extends` chain from `configFile` to the first configuration it reaches twice, each
|
|
244
|
+
* link by path, or undefined where no chain written by path returns.
|
|
245
|
+
*/
|
|
246
|
+
function extendsCycle(engine, configFile, parsed) {
|
|
247
|
+
const walk = (chain, at) => {
|
|
248
|
+
for (const next of extendedPaths(at, chain.at(-1) ?? configFile)) {
|
|
249
|
+
if (chain.includes(next)) {
|
|
250
|
+
return [...chain, next];
|
|
251
|
+
}
|
|
252
|
+
let read;
|
|
253
|
+
try {
|
|
254
|
+
read = engine.parseConfigFile(next);
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const cycle = walk([...chain, next], read);
|
|
260
|
+
if (cycle !== undefined) {
|
|
261
|
+
return cycle;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
249
264
|
return undefined;
|
|
265
|
+
};
|
|
266
|
+
return walk([configFile], parsed);
|
|
267
|
+
}
|
|
268
|
+
/** The configuration files one parsed configuration's `references` name. */
|
|
269
|
+
function referencesOf(parsed) {
|
|
270
|
+
// The parse answers a directory reference as the directory; the compiler reads a
|
|
271
|
+
// path not ending `.json` as its `tsconfig.json`:
|
|
272
|
+
// https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/core/projectreference.go#L18
|
|
273
|
+
return (parsed.projectReferences ?? []).map(({ path }) => path.endsWith(".json") ? path : joinPath(path, "tsconfig.json"));
|
|
274
|
+
}
|
|
275
|
+
/** Why one parsed configuration's own `extends` chain cannot be opened, where it is circular. */
|
|
276
|
+
function circularity(engine, configFile, parsed) {
|
|
277
|
+
const chain = extendsCycle(engine, configFile, parsed);
|
|
278
|
+
if (chain === undefined && !parsed.errors.some(({ code }) => code === CIRCULAR_EXTENDS)) {
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
return `TS${String(CIRCULAR_EXTENDS)}: Circularity detected while resolving configuration: ${(chain ?? [configFile]).join(" -> ")}`;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Why a configuration the `references` reach, transitively, cannot be opened, where one's
|
|
285
|
+
* `extends` chain is circular: the compiler opens every referenced project with the one
|
|
286
|
+
* that references it.
|
|
287
|
+
*/
|
|
288
|
+
function referencedCircularity(engine, references, visited) {
|
|
289
|
+
for (const reference of references) {
|
|
290
|
+
if (visited.has(reference)) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
visited.add(reference);
|
|
294
|
+
let parsed;
|
|
295
|
+
try {
|
|
296
|
+
parsed = engine.parseConfigFile(reference);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const reason = circularity(engine, reference, parsed) ??
|
|
302
|
+
referencedCircularity(engine, referencesOf(parsed), visited);
|
|
303
|
+
if (reason !== undefined) {
|
|
304
|
+
return reason;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* The configuration files one configuration's `references` name, or why the compiler
|
|
311
|
+
* cannot read the configuration: it throws, or its `extends` chain or that of a
|
|
312
|
+
* configuration it references is circular, which the analysis refuses before any
|
|
313
|
+
* snapshot is asked to open it.
|
|
314
|
+
*/
|
|
315
|
+
function readConfiguration(engine, configFile) {
|
|
316
|
+
try {
|
|
317
|
+
const parsed = engine.parseConfigFile(configFile);
|
|
318
|
+
const references = referencesOf(parsed);
|
|
319
|
+
const reason = circularity(engine, configFile, parsed) ??
|
|
320
|
+
referencedCircularity(engine, references, new Set([configFile]));
|
|
321
|
+
return reason === undefined ? { references } : { reason };
|
|
250
322
|
}
|
|
251
323
|
catch (error) {
|
|
252
|
-
return error instanceof Error ? error.message : String(error);
|
|
324
|
+
return { reason: error instanceof Error ? error.message : String(error) };
|
|
253
325
|
}
|
|
254
326
|
}
|
|
255
327
|
/**
|
|
256
|
-
*
|
|
257
|
-
* opened from a configuration the compiler cannot read would be analyzed with
|
|
258
|
-
* type information.
|
|
328
|
+
* The references of one compiler configuration file, or ends the run naming it: a
|
|
329
|
+
* project opened from a configuration the compiler cannot read would be analyzed with
|
|
330
|
+
* no type information.
|
|
259
331
|
*/
|
|
260
332
|
function parseOrRefuse(engine, configFile, named) {
|
|
261
|
-
const
|
|
262
|
-
if (reason
|
|
263
|
-
throw new DiscoveryError(`${named} cannot be read as a compiler configuration: ${reason}`, []);
|
|
333
|
+
const read = readConfiguration(engine, configFile);
|
|
334
|
+
if ("reason" in read) {
|
|
335
|
+
throw new DiscoveryError(`${named} cannot be read as a compiler configuration: ${read.reason}`, []);
|
|
264
336
|
}
|
|
337
|
+
return read.references;
|
|
265
338
|
}
|
|
266
339
|
/**
|
|
267
|
-
* The projects a build matrix declares, in
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* naming both: the compiler builds a referenced project's sources into the program
|
|
274
|
-
* that references it without reporting their diagnostics, so the project the matrix
|
|
275
|
-
* left out would be analyzed with its errors unread. An entry naming a file an
|
|
276
|
-
* earlier entry named adds nothing, so the earlier identifier stands.
|
|
340
|
+
* The projects a build matrix declares, in its order, each entry a file below the target
|
|
341
|
+
* root: an entry naming an absent file, a directory or a file the compiler cannot read
|
|
342
|
+
* ends the run naming it, and one naming an earlier entry's file adds nothing. A
|
|
343
|
+
* reference below the root the matrix does not name ends the run naming both, because the
|
|
344
|
+
* compiler builds a referenced project's sources into the referencing program without
|
|
345
|
+
* reporting their diagnostics. A reference outside the root is an outside project.
|
|
277
346
|
*/
|
|
278
347
|
function declaredProjects(engine, host, targetRoot, entries) {
|
|
279
348
|
const seen = new Set();
|
|
280
349
|
const projects = [];
|
|
350
|
+
const references = new Map();
|
|
281
351
|
for (const entry of entries) {
|
|
282
352
|
const configFile = joinPath(targetRoot, entry.project);
|
|
283
353
|
const named = `the project ${JSON.stringify(entry.id)} (${configFile})`;
|
|
@@ -291,11 +361,13 @@ function declaredProjects(engine, host, targetRoot, entries) {
|
|
|
291
361
|
continue;
|
|
292
362
|
}
|
|
293
363
|
seen.add(configFile);
|
|
294
|
-
parseOrRefuse(engine, configFile, named);
|
|
364
|
+
references.set(configFile, parseOrRefuse(engine, configFile, named));
|
|
295
365
|
projects.push({ id: entry.id, configFile });
|
|
296
366
|
}
|
|
297
367
|
for (const project of projects) {
|
|
298
|
-
const unnamed =
|
|
368
|
+
const unnamed = references
|
|
369
|
+
.get(project.configFile)
|
|
370
|
+
?.find((path) => !seen.has(path) && relativePath(targetRoot, path) !== undefined);
|
|
299
371
|
if (unnamed !== undefined) {
|
|
300
372
|
throw new DiscoveryError(`the project ${JSON.stringify(project.id)} (${project.configFile}) references ` +
|
|
301
373
|
`${unnamed}, which the build matrix does not name. A referenced project is built ` +
|
|
@@ -311,18 +383,19 @@ function declaredProjects(engine, host, targetRoot, entries) {
|
|
|
311
383
|
};
|
|
312
384
|
}
|
|
313
385
|
/**
|
|
314
|
-
* The projects a run derives where the matrix declares none, in
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
* each
|
|
318
|
-
*
|
|
319
|
-
* it cannot be read; one the walk found is dropped and named.
|
|
386
|
+
* The projects a run derives where the matrix declares none, in discovery order: the
|
|
387
|
+
* paths the scope names, every compiler configuration under the target root outside an
|
|
388
|
+
* ignored directory, then each configuration below the root those reference,
|
|
389
|
+
* transitively, each once. A reference outside the root is an outside project, whose
|
|
390
|
+
* sources the compiler builds into the referencing program. A configuration the scope
|
|
391
|
+
* names ends the run where it cannot be read; one the walk found is dropped and named.
|
|
320
392
|
*/
|
|
321
393
|
function derivedProjects(engine, host, scope) {
|
|
322
394
|
const targetRoot = scope.target.path;
|
|
323
395
|
const fromScope = scopeConfigFiles(host, scope);
|
|
324
396
|
const notBuilt = [];
|
|
325
|
-
const
|
|
397
|
+
const rootDir = resolvePath(host.workingDirectory(), fromScope.length > 0 ? dirnamePath(targetRoot) : targetRoot);
|
|
398
|
+
const configFiles = derivedFrom(engine, host, [...fromScope, ...configFilesUnder(host, targetRoot)], (reference) => relativePath(rootDir, reference) !== undefined, (configFile, reason) => {
|
|
326
399
|
if (fromScope.includes(configFile)) {
|
|
327
400
|
throw new DiscoveryError(`${configFile} cannot be read as a compiler configuration: ${reason}`, []);
|
|
328
401
|
}
|
|
@@ -363,3 +436,33 @@ export function discoverProjects(engine, host, scope, configurations = []) {
|
|
|
363
436
|
}
|
|
364
437
|
return derivedProjects(engine, host, scope);
|
|
365
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* How the snapshot reads each configuration of `configFiles` that sets `baseUrl` and
|
|
441
|
+
* `programs` does not already describe: a configuration only the snapshot holds, which
|
|
442
|
+
* extends it and states the `paths` {@link baseUrlPaths} gives, so its imports resolve as
|
|
443
|
+
* the compilers before version 7 resolve them. A configuration the compiler cannot read is
|
|
444
|
+
* left for its project to refuse.
|
|
445
|
+
*/
|
|
446
|
+
export function withBaseUrlPaths(engine, host, configFiles, programs) {
|
|
447
|
+
const found = new Map(programs);
|
|
448
|
+
for (const configFile of configFiles) {
|
|
449
|
+
if (found.has(configFile)) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
let paths;
|
|
453
|
+
try {
|
|
454
|
+
paths = baseUrlPaths(engine.parseConfigFile(configFile).options);
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (paths !== undefined) {
|
|
460
|
+
found.set(configFile, {
|
|
461
|
+
kind: "configuration",
|
|
462
|
+
file: writtenPath(host, configFile),
|
|
463
|
+
text: `${JSON.stringify({ extends: configFile, compilerOptions: { paths } }, null, 2)}\n`,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return found;
|
|
468
|
+
}
|
package/dist/src/edges.js
CHANGED
|
@@ -130,7 +130,7 @@ export function readEdgeSides(host, targetRoot) {
|
|
|
130
130
|
}
|
|
131
131
|
const sides = [];
|
|
132
132
|
edges.forEach((value, index) => {
|
|
133
|
-
const offset = walk.
|
|
133
|
+
const offset = walk.starts[index];
|
|
134
134
|
const at = offset === undefined ? EDGES_FILE : positionKey(positionAt(text, EDGES_FILE, offset));
|
|
135
135
|
const edge = readEdge(value, at);
|
|
136
136
|
const named = [
|
|
@@ -43,7 +43,6 @@ const UNREAD_TOOLS = [
|
|
|
43
43
|
"cypress",
|
|
44
44
|
"danger",
|
|
45
45
|
"dependency-cruiser",
|
|
46
|
-
"docusaurus",
|
|
47
46
|
"dotenv",
|
|
48
47
|
"drizzle",
|
|
49
48
|
"electron-vite",
|
|
@@ -97,14 +96,12 @@ const UNREAD_TOOLS = [
|
|
|
97
96
|
"nuxtjs-i18n",
|
|
98
97
|
"nx",
|
|
99
98
|
"nyc",
|
|
100
|
-
"oclif",
|
|
101
99
|
"openapi-ts",
|
|
102
100
|
"openclaw",
|
|
103
101
|
"orval",
|
|
104
102
|
"oxfmt",
|
|
105
103
|
"oxlint",
|
|
106
104
|
"panda-css",
|
|
107
|
-
"parcel",
|
|
108
105
|
"payload",
|
|
109
106
|
"pino",
|
|
110
107
|
"playwright-ct",
|
|
@@ -149,7 +146,6 @@ const UNREAD_TOOLS = [
|
|
|
149
146
|
"swc",
|
|
150
147
|
"syncpack",
|
|
151
148
|
"tailwind",
|
|
152
|
-
"tanstack-router",
|
|
153
149
|
"taskfile",
|
|
154
150
|
"tauri",
|
|
155
151
|
"temporal",
|
|
@@ -193,18 +189,19 @@ const UNREAD_TOOLS = [
|
|
|
193
189
|
];
|
|
194
190
|
/** The conventions declined for a tool some of whose conventions are entered. */
|
|
195
191
|
const PARTLY_ENTERED = [
|
|
192
|
+
{
|
|
193
|
+
tool: "docusaurus",
|
|
194
|
+
convention: "a page or theme directory a plugin's configuration moves, and the docs",
|
|
195
|
+
reason: "The convention row reads src/pages and src/theme only, and a document's imports are " +
|
|
196
|
+
"read from every Markdown and MDX file, so a module only a moved directory holds is " +
|
|
197
|
+
"reported as never imported unless ts.entry_files names it.",
|
|
198
|
+
},
|
|
196
199
|
{
|
|
197
200
|
tool: "eslint",
|
|
198
201
|
convention: "a legacy configuration written in YAML or in JSON with comments",
|
|
199
202
|
reason: "Only a configuration module and a strict JSON configuration file are read, so a " +
|
|
200
203
|
"dependency only such a file names is reported as unused.",
|
|
201
204
|
},
|
|
202
|
-
{
|
|
203
|
-
tool: "eslint",
|
|
204
|
-
convention: "a plugin or shareable configuration named without its package's prefix",
|
|
205
|
-
reason: "A string uses a dependency only where it spells the dependency's name, so a " +
|
|
206
|
-
"dependency only a shortened name loads is reported as unused.",
|
|
207
|
-
},
|
|
208
205
|
{
|
|
209
206
|
tool: "playwright",
|
|
210
207
|
convention: "a testMatch written as a regular expression",
|