@cplieger/deadset-ts 7.0.0-dev.2 → 7.0.0-dev.4
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/README.md +1 -1
- package/dist/src/alias-chain.js +2 -2
- package/dist/src/analysis.js +89 -44
- package/dist/src/augmentations.js +14 -12
- package/dist/src/calls.js +2 -2
- package/dist/src/class-members.js +1 -1
- package/dist/src/components.js +62 -20
- package/dist/src/configuration-files.js +6 -6
- package/dist/src/configured-declarations.js +2 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +71 -14
- package/dist/src/convention-rows.js +6 -2
- package/dist/src/conventions.js +4 -2
- package/dist/src/dependencies.js +81 -27
- package/dist/src/deprecation.js +1 -1
- package/dist/src/destructuring.js +2 -2
- package/dist/src/discover.js +1 -1
- package/dist/src/element-access.js +341 -0
- package/dist/src/entry-points.js +2 -2
- package/dist/src/enum-group.js +2 -2
- package/dist/src/erased-stores.js +317 -0
- package/dist/src/findings/completion.js +46 -15
- package/dist/src/findings/dependencies-and-module-machinery.js +1 -1
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/self-check.js +17 -4
- package/dist/src/findings-pass.js +6 -5
- package/dist/src/framework-lifecycle.js +1 -1
- package/dist/src/html-entries.js +1 -1
- package/dist/src/implementations.js +2 -2
- package/dist/src/in-operands.js +30 -0
- package/dist/src/indexed-access.js +264 -125
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +2 -2
- package/dist/src/intra-function-parts.js +26 -12
- package/dist/src/inventory.js +26 -1
- package/dist/src/keyof-comparisons.js +170 -0
- package/dist/src/kinds.js +17 -7
- package/dist/src/manifest.js +6 -16
- package/dist/src/matrix.js +20 -2
- package/dist/src/member-names.js +1 -1
- package/dist/src/module-declarations.js +1 -1
- package/dist/src/namespace-objects.js +1 -1
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +2 -2
- package/dist/src/query.js +4 -1
- package/dist/src/references.js +13 -2
- package/dist/src/reflective-lookup.js +2 -2
- package/dist/src/report.js +1 -1
- package/dist/src/reporters/sarif.js +4 -0
- package/dist/src/roots.js +1 -1
- package/dist/src/serialization-contract.js +45 -16
- package/dist/src/session.js +15 -8
- package/dist/src/signatures.js +1 -1
- package/dist/src/stores.js +1 -1
- package/dist/src/suppress-file.js +58 -6
- package/dist/src/suppress-inline.js +14 -5
- package/dist/src/suppress.js +54 -24
- package/dist/src/symbol-keys.js +1 -1
- package/dist/src/type-errors.js +1 -1
- package/dist/src/type-query-alias.js +1 -1
- package/dist/src/unimplemented-callees.js +312 -0
- package/dist/src/value-flow.js +1 -1
- package/dist/src/value-reach.js +8 -8
- package/dist/src/verbs/analyze.js +11 -9
- package/dist/src/workspace-programs.js +2 -2
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +2 -2
- package/src/analysis.ts +135 -52
- package/src/augmentations.ts +14 -11
- package/src/calls.ts +2 -2
- package/src/class-members.ts +2 -2
- package/src/component-layer.ts +1 -1
- package/src/components.ts +103 -21
- package/src/configuration-files.ts +6 -5
- package/src/configured-declarations.ts +2 -6
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +82 -18
- package/src/convention-rows.ts +6 -2
- package/src/conventions.ts +11 -2
- package/src/decorator.ts +1 -1
- package/src/dependencies.ts +108 -31
- package/src/deprecation.ts +1 -1
- package/src/destructuring.ts +2 -2
- package/src/discover.ts +2 -2
- package/src/element-access.ts +446 -0
- package/src/entry-points.ts +2 -2
- package/src/enum-group.ts +2 -2
- package/src/erased-stores.ts +413 -0
- package/src/file-facts.ts +3 -1
- package/src/findings/completion.ts +54 -20
- package/src/findings/dependencies-and-module-machinery.ts +1 -1
- package/src/findings/emitters.ts +1 -1
- package/src/findings/self-check.ts +18 -4
- package/src/findings-pass.ts +12 -5
- package/src/framework-lifecycle.ts +2 -2
- package/src/html-entries.ts +1 -1
- package/src/implementations.ts +2 -2
- package/src/in-operands.ts +45 -0
- package/src/indexed-access.ts +304 -146
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +2 -2
- package/src/intra-function-parts.ts +42 -17
- package/src/inventory.ts +34 -2
- package/src/keyof-comparisons.ts +211 -0
- package/src/kinds.ts +20 -7
- package/src/manifest.ts +6 -21
- package/src/matrix.ts +34 -2
- package/src/member-names.ts +1 -1
- package/src/module-declarations.ts +1 -1
- package/src/namespace-objects.ts +1 -1
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +2 -2
- package/src/position.ts +1 -1
- package/src/query.ts +12 -2
- package/src/references.ts +25 -3
- package/src/reflective-lookup.ts +2 -2
- package/src/report.ts +3 -1
- package/src/reporters/sarif.ts +4 -0
- package/src/roots.ts +2 -2
- package/src/serialization-contract.ts +55 -16
- package/src/session.ts +39 -32
- package/src/signatures.ts +1 -1
- package/src/source-files.ts +1 -1
- package/src/stores.ts +1 -1
- package/src/suppress-file.ts +68 -5
- package/src/suppress-inline.ts +15 -5
- package/src/suppress.ts +63 -28
- package/src/symbol-keys.ts +2 -2
- package/src/type-errors.ts +2 -2
- package/src/type-query-alias.ts +1 -1
- package/src/unimplemented-callees.ts +411 -0
- package/src/value-flow.ts +1 -1
- package/src/value-reach.ts +12 -8
- package/src/verbs/analyze.ts +15 -9
- package/src/workflow-steps.ts +1 -1
- package/src/workspace-programs.ts +4 -4
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
isStringLiteral,
|
|
15
15
|
type Node,
|
|
16
16
|
type SourceFile,
|
|
17
|
-
} from "@typescript/native/
|
|
17
|
+
} from "@typescript/native/ast";
|
|
18
18
|
import type { Host } from "./host.ts";
|
|
19
19
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
20
20
|
import type { SourceFiles } from "./source-files.ts";
|
|
@@ -426,7 +426,7 @@ export function configurationFiles(
|
|
|
426
426
|
outside:
|
|
427
427
|
parse === undefined
|
|
428
428
|
? []
|
|
429
|
-
: outsideModules(host, [...dirs], files, holdsManifest, parse, named),
|
|
429
|
+
: outsideModules(host, root, [...dirs], files, holdsManifest, parse, named),
|
|
430
430
|
};
|
|
431
431
|
}
|
|
432
432
|
|
|
@@ -480,11 +480,12 @@ export function resolveRelativeSpecifier(
|
|
|
480
480
|
|
|
481
481
|
/**
|
|
482
482
|
* The configuration modules of the directories that hold a manifest which the program
|
|
483
|
-
* does not hold, and the files they import by a relative specifier,
|
|
484
|
-
* file joins. A file that cannot be read is skipped.
|
|
483
|
+
* does not hold, and the project files below `root` they import by a relative specifier,
|
|
484
|
+
* until no further file joins. A file that cannot be read is skipped.
|
|
485
485
|
*/
|
|
486
486
|
function outsideModules(
|
|
487
487
|
host: Host,
|
|
488
|
+
root: string,
|
|
488
489
|
dirs: readonly string[],
|
|
489
490
|
files: SourceFiles,
|
|
490
491
|
holdsManifest: (dir: string) => boolean,
|
|
@@ -533,7 +534,7 @@ function outsideModules(
|
|
|
533
534
|
outside.manifestDirs.push(next.manifestDir);
|
|
534
535
|
for (const specifier of relativeSpecifiers(outside.file)) {
|
|
535
536
|
const reached = resolveRelativeSpecifier(host, dirnamePath(next.path), specifier);
|
|
536
|
-
if (reached !== undefined) {
|
|
537
|
+
if (reached !== undefined && isProjectPath(root, reached)) {
|
|
537
538
|
pending.push({ path: reached, manifestDir: next.manifestDir });
|
|
538
539
|
}
|
|
539
540
|
}
|
|
@@ -15,12 +15,8 @@ import {
|
|
|
15
15
|
isStringLiteral,
|
|
16
16
|
SyntaxKind,
|
|
17
17
|
type Node,
|
|
18
|
-
} from "@typescript/native/
|
|
19
|
-
import {
|
|
20
|
-
SymbolFlags,
|
|
21
|
-
type NodeHandle,
|
|
22
|
-
type Symbol as TSSymbol,
|
|
23
|
-
} from "@typescript/native/unstable/sync";
|
|
18
|
+
} from "@typescript/native/ast";
|
|
19
|
+
import { SymbolFlags, type NodeHandle, type Symbol as TSSymbol } from "@typescript/native/sync";
|
|
24
20
|
import type { AliasChains } from "./alias-chain.ts";
|
|
25
21
|
import { resolvedTargets } from "./calls.ts";
|
|
26
22
|
import type { Config, DeclarationEntry } from "./config.ts";
|
|
@@ -4,7 +4,7 @@ export const RECORD = {
|
|
|
4
4
|
conformance: {
|
|
5
5
|
corpusVersion: "5.0.0",
|
|
6
6
|
result: "pass",
|
|
7
|
-
digest: "sha256:
|
|
7
|
+
digest: "sha256:c46c7d06b6f0c58504c5f311870efefc0b85f18c2c46ba7d7b722081cb370c0c",
|
|
8
8
|
},
|
|
9
9
|
gaps: [],
|
|
10
10
|
} as const;
|
package/src/contextual-uses.ts
CHANGED
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
+
isClassDeclaration,
|
|
12
|
+
isClassExpression,
|
|
11
13
|
isComputedPropertyName,
|
|
12
14
|
isGetAccessorDeclaration,
|
|
13
15
|
isIdentifier,
|
|
14
16
|
isMethodDeclaration,
|
|
17
|
+
isMethodSignatureDeclaration,
|
|
15
18
|
isNoSubstitutionTemplateLiteral,
|
|
16
19
|
isNumericLiteral,
|
|
17
20
|
isObjectLiteralExpression,
|
|
@@ -23,8 +26,8 @@ import {
|
|
|
23
26
|
type Expression,
|
|
24
27
|
type Node,
|
|
25
28
|
type ObjectLiteralExpression,
|
|
26
|
-
} from "@typescript/native/
|
|
27
|
-
import { SignatureKind,
|
|
29
|
+
} from "@typescript/native/ast";
|
|
30
|
+
import { SignatureKind, type NodeHandle, type Type } from "@typescript/native/sync";
|
|
28
31
|
import type { AliasChains } from "./alias-chain.ts";
|
|
29
32
|
import type { Inventory, SymbolKind } from "./inventory.ts";
|
|
30
33
|
import { isAnswered, type PropertyTables } from "./query.ts";
|
|
@@ -115,11 +118,30 @@ interface Checked {
|
|
|
115
118
|
/** One member of an object type, by the inventory's declarations and the checker's declaration handles. */
|
|
116
119
|
interface Member {
|
|
117
120
|
readonly ids: readonly string[];
|
|
121
|
+
/** The inventory's interface methods it is, which a position reads but no literal writes. */
|
|
122
|
+
readonly methods: readonly string[];
|
|
118
123
|
readonly handles: ReadonlySet<string>;
|
|
119
124
|
/** Whether a declaration of it is in one of the target's own files. */
|
|
120
125
|
readonly own: boolean;
|
|
121
|
-
/**
|
|
122
|
-
readonly
|
|
126
|
+
/** The checker's declarations of it in the target's own files. */
|
|
127
|
+
readonly declarations: readonly NodeHandle[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether an object spread copies a member a declaration declares: a method, a method
|
|
132
|
+
* signature and a class's accessor are no own data property of the value, while an
|
|
133
|
+
* accessor of an interface or a type literal describes one. It is decided on the
|
|
134
|
+
* declaration, because a mapped type over a class keeps the class's declarations on
|
|
135
|
+
* plain property symbols.
|
|
136
|
+
*/
|
|
137
|
+
function copiedBySpread(node: Node): boolean {
|
|
138
|
+
if (isMethodDeclaration(node) || isMethodSignatureDeclaration(node)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
return !(
|
|
142
|
+
(isGetAccessorDeclaration(node) || isSetAccessorDeclaration(node)) &&
|
|
143
|
+
(isClassDeclaration(node.parent) || isClassExpression(node.parent))
|
|
144
|
+
);
|
|
123
145
|
}
|
|
124
146
|
|
|
125
147
|
/**
|
|
@@ -182,11 +204,14 @@ export function contextualUses<Brand>(
|
|
|
182
204
|
ids: chains
|
|
183
205
|
.declarationsOf(property)
|
|
184
206
|
.filter((id) => MEMBER_KINDS.has(kinds.get(id) ?? "file")),
|
|
207
|
+
methods: chains
|
|
208
|
+
.declarationsOf(property)
|
|
209
|
+
.filter((id) => kinds.get(id) === "interface-method"),
|
|
185
210
|
handles: new Set(
|
|
186
211
|
property.declarations.map((handle) => `${handle.path}#${String(handle.index)}`),
|
|
187
212
|
),
|
|
188
213
|
own: property.declarations.some((handle) => ownPaths.has(handle.path)),
|
|
189
|
-
|
|
214
|
+
declarations: property.declarations.filter((handle) => ownPaths.has(handle.path)),
|
|
190
215
|
});
|
|
191
216
|
}
|
|
192
217
|
tables.set(type.id, table);
|
|
@@ -250,8 +275,10 @@ export function contextualUses<Brand>(
|
|
|
250
275
|
cap,
|
|
251
276
|
);
|
|
252
277
|
/** The members a position may read structurally: those of object types, not of classes. */
|
|
253
|
-
const readable = (member: Member): readonly string[] =>
|
|
254
|
-
member.ids.filter((id) => kinds.get(id) === "type-member")
|
|
278
|
+
const readable = (member: Member): readonly string[] => [
|
|
279
|
+
...member.ids.filter((id) => kinds.get(id) === "type-member"),
|
|
280
|
+
...member.methods,
|
|
281
|
+
];
|
|
255
282
|
const carries = (part: Type): boolean =>
|
|
256
283
|
[...membersOf(part).values()].some((member) => readable(member).length > 0);
|
|
257
284
|
/** Whether an object type has members and declares none of them in the target. */
|
|
@@ -278,6 +305,20 @@ export function contextualUses<Brand>(
|
|
|
278
305
|
return element;
|
|
279
306
|
};
|
|
280
307
|
|
|
308
|
+
const argumentsOf = new Map<number, readonly Type[]>();
|
|
309
|
+
/** The type arguments of an instantiation of a generic type, read once per type. */
|
|
310
|
+
const typeArgumentsOf = (type: Type): readonly Type[] => {
|
|
311
|
+
const known = argumentsOf.get(type.id);
|
|
312
|
+
if (known !== undefined) {
|
|
313
|
+
return known;
|
|
314
|
+
}
|
|
315
|
+
lookups += type.isTypeReference() ? 1 : 0;
|
|
316
|
+
const held = queries.typeArguments(type);
|
|
317
|
+
const found = isAnswered(held) ? held : [];
|
|
318
|
+
argumentsOf.set(type.id, found);
|
|
319
|
+
return found;
|
|
320
|
+
};
|
|
321
|
+
|
|
281
322
|
const calls = new Map<number, Called | undefined>();
|
|
282
323
|
/**
|
|
283
324
|
* The parameter and return types of a type's one call signature, or of its one construct
|
|
@@ -315,10 +356,11 @@ export function contextualUses<Brand>(
|
|
|
315
356
|
|
|
316
357
|
/**
|
|
317
358
|
* Each object type a value of `source` is checked as against the object types of
|
|
318
|
-
* `target`: the value's own, and through array elements
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
359
|
+
* `target`: the value's own, and through array elements, a function's parameters,
|
|
360
|
+
* checked the other way, and result, and the type arguments two instantiations share
|
|
361
|
+
* by place, an array among them, as an array reaches `Iterable<T>`.
|
|
362
|
+
* A source that is one of the target's own object types matches it as it stands. Each
|
|
363
|
+
* pair is compared once, which ends the walk over a recursive type.
|
|
322
364
|
*/
|
|
323
365
|
const compared = (
|
|
324
366
|
source: Type | undefined,
|
|
@@ -344,9 +386,20 @@ export function contextualUses<Brand>(
|
|
|
344
386
|
}
|
|
345
387
|
const element = elementOf(one);
|
|
346
388
|
const called = element === undefined ? callOf(one) : undefined;
|
|
389
|
+
const sourceArguments = typeArgumentsOf(one);
|
|
347
390
|
for (const part of targets) {
|
|
391
|
+
const partElement = elementOf(part);
|
|
392
|
+
if (element !== undefined && partElement !== undefined) {
|
|
393
|
+
compared(element, partElement, into, seen);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const targetArguments = typeArgumentsOf(part);
|
|
397
|
+
// A defaulted trailing parameter, as `Iterable<T, TReturn, TNext>` has, leaves
|
|
398
|
+
// the places two instantiations share comparable.
|
|
399
|
+
sourceArguments.slice(0, targetArguments.length).forEach((argument, index) => {
|
|
400
|
+
compared(argument, targetArguments[index], into, seen);
|
|
401
|
+
});
|
|
348
402
|
if (element !== undefined) {
|
|
349
|
-
compared(element, elementOf(part), into, seen);
|
|
350
403
|
continue;
|
|
351
404
|
}
|
|
352
405
|
const other = called === undefined ? undefined : callOf(part);
|
|
@@ -378,7 +431,7 @@ export function contextualUses<Brand>(
|
|
|
378
431
|
if (element !== undefined) {
|
|
379
432
|
return mayCarry(element, seen);
|
|
380
433
|
}
|
|
381
|
-
return callOf(part) !== undefined;
|
|
434
|
+
return callOf(part) !== undefined || typeArgumentsOf(part).some((one) => mayCarry(one, seen));
|
|
382
435
|
});
|
|
383
436
|
};
|
|
384
437
|
|
|
@@ -433,6 +486,19 @@ export function contextualUses<Brand>(
|
|
|
433
486
|
reads.push({ at: flow.value, from: flow.from, ids: [...ids] });
|
|
434
487
|
}
|
|
435
488
|
});
|
|
489
|
+
const copies = new Map<Member, boolean>();
|
|
490
|
+
/** Whether a spread copies the member: where any declaration of it declares data. */
|
|
491
|
+
const copied = (member: Member): boolean => {
|
|
492
|
+
let known = copies.get(member);
|
|
493
|
+
if (known === undefined) {
|
|
494
|
+
known = member.declarations.some((handle) => {
|
|
495
|
+
const node = project.declarationAt(handle)?.node;
|
|
496
|
+
return node === undefined || copiedBySpread(node);
|
|
497
|
+
});
|
|
498
|
+
copies.set(member, known);
|
|
499
|
+
}
|
|
500
|
+
return known;
|
|
501
|
+
};
|
|
436
502
|
const spreads = literals.flatMap(({ literal, from }) =>
|
|
437
503
|
literal.properties.flatMap((property) =>
|
|
438
504
|
isSpreadAssignment(property) ? [{ value: property.expression, from }] : [],
|
|
@@ -448,12 +514,10 @@ export function contextualUses<Brand>(
|
|
|
448
514
|
if (type === undefined || !isAnswered(type)) {
|
|
449
515
|
return;
|
|
450
516
|
}
|
|
451
|
-
// A spread gets every own enumerable property, an overwritten key's getter included
|
|
452
|
-
//
|
|
453
|
-
// the class's methods (never data members), and a private name, which is no property
|
|
517
|
+
// A spread gets every own enumerable property, an overwritten key's getter included,
|
|
518
|
+
// and no private name, which is no property
|
|
454
519
|
// (https://tc39.es/ecma262/#sec-copydataproperties).
|
|
455
|
-
const own = (member: Member, id: string): boolean =>
|
|
456
|
-
!privateNames.has(id) && !(member.accessor && kinds.get(id) === "class-member");
|
|
520
|
+
const own = (member: Member, id: string): boolean => !privateNames.has(id) && copied(member);
|
|
457
521
|
const ids = [
|
|
458
522
|
...new Set(
|
|
459
523
|
partsOf(type).flatMap((part) =>
|
package/src/convention-rows.ts
CHANGED
|
@@ -158,7 +158,8 @@ const VITEST_CONFIG = `{vitest,vite}.config.${ANY_MODULE}`;
|
|
|
158
158
|
|
|
159
159
|
/**
|
|
160
160
|
* The properties Vitest takes its project root from, `test.root` before Vite's `root`; it
|
|
161
|
-
* resolves `setupFiles` against that root
|
|
161
|
+
* resolves `setupFiles` and `globalSetup` against that root
|
|
162
|
+
* (https://vitest.dev/config/setupfiles, https://vitest.dev/config/globalsetup).
|
|
162
163
|
*/
|
|
163
164
|
const VITEST_ROOTS = ["test.root", "root"] as const;
|
|
164
165
|
|
|
@@ -660,6 +661,9 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
|
|
|
660
661
|
{ property: "test.coverage.include", files: [VITEST_CONFIG] },
|
|
661
662
|
{ property: "test.coverage.exclude", files: [VITEST_CONFIG] },
|
|
662
663
|
],
|
|
663
|
-
testFiles: [
|
|
664
|
+
testFiles: [
|
|
665
|
+
{ property: "test.setupFiles", files: [VITEST_CONFIG], roots: VITEST_ROOTS },
|
|
666
|
+
{ property: "test.globalSetup", files: [VITEST_CONFIG], roots: VITEST_ROOTS },
|
|
667
|
+
],
|
|
664
668
|
},
|
|
665
669
|
];
|
package/src/conventions.ts
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
type ParenthesizedExpression,
|
|
45
45
|
type SatisfiesExpression,
|
|
46
46
|
type SourceFile,
|
|
47
|
-
} from "@typescript/native/
|
|
47
|
+
} from "@typescript/native/ast";
|
|
48
48
|
import { lineOf } from "./component-files.ts";
|
|
49
49
|
import {
|
|
50
50
|
CONVENTION_ROWS,
|
|
@@ -71,6 +71,11 @@ export interface ConventionManifest {
|
|
|
71
71
|
readonly dir: string;
|
|
72
72
|
/** The manifest's path below the target root, `/`-separated. */
|
|
73
73
|
readonly path: string;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the manifest is a nested package's, whose install no run requires: an
|
|
76
|
+
* enabling package it declares and nothing installs applies no row and fails nothing.
|
|
77
|
+
*/
|
|
78
|
+
readonly nested?: boolean;
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
/** One row a run applied, as the report lists it. */
|
|
@@ -1007,7 +1012,11 @@ export function readConventions(
|
|
|
1007
1012
|
}
|
|
1008
1013
|
const version = versions.get(row.package);
|
|
1009
1014
|
if (version === undefined) {
|
|
1010
|
-
if (
|
|
1015
|
+
if (
|
|
1016
|
+
missing.has(row.package) ||
|
|
1017
|
+
manifest.nested === true ||
|
|
1018
|
+
!declares(declared, row.package, REQUIRED_SECTIONS)
|
|
1019
|
+
) {
|
|
1011
1020
|
continue;
|
|
1012
1021
|
}
|
|
1013
1022
|
missing.add(row.package);
|
package/src/decorator.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* the inventory holds for the class, static members included.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type { Node } from "@typescript/native/
|
|
14
|
+
import type { Node } from "@typescript/native/ast";
|
|
15
15
|
import { decoratorsOf, spelledDecorator } from "./calls.ts";
|
|
16
16
|
import type { DetectorInput, Evidence } from "./exempt.ts";
|
|
17
17
|
import { nodeKey, type InventorySymbol, type SymbolKind } from "./inventory.ts";
|
package/src/dependencies.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
type Identifier,
|
|
25
25
|
type Node,
|
|
26
26
|
type SourceFile,
|
|
27
|
-
} from "@typescript/native/
|
|
27
|
+
} from "@typescript/native/ast";
|
|
28
28
|
import {
|
|
29
29
|
configurationFiles,
|
|
30
30
|
declaredIn as declaredBeside,
|
|
@@ -71,28 +71,37 @@ interface DeclaredDependency {
|
|
|
71
71
|
/** What a configuration's own reading needs: packages no deletion of a declaration removes. */
|
|
72
72
|
export function configurationNeeds(packages: readonly string[]): ProjectNeeds {
|
|
73
73
|
return {
|
|
74
|
-
packages: new Set(packages),
|
|
75
74
|
uses: new Map([[PROJECT_OWNER, new Set(packages)]]),
|
|
75
|
+
at: new Map([[ANYWHERE, new Set(packages)]]),
|
|
76
76
|
unanswered: [],
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/** The key of {@link ProjectNeeds.at} for a use no file of the target governs. */
|
|
81
|
+
const ANYWHERE = "";
|
|
82
|
+
|
|
80
83
|
/** What the installed copy of one declared dependency states about itself. */
|
|
81
84
|
interface InstalledDependency {
|
|
82
85
|
/** The commands its manifest's `bin` declares, by name. */
|
|
83
86
|
readonly commands: readonly string[];
|
|
87
|
+
/** The packages its manifest's `peerDependencies` names, optional ones included. */
|
|
88
|
+
readonly peers: readonly string[];
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
/** What one project's own files and configuration need from outside the target. */
|
|
87
92
|
export interface ProjectNeeds {
|
|
88
|
-
/** Every package a use names or resolves to. */
|
|
89
|
-
readonly packages: ReadonlySet<string>;
|
|
90
93
|
/**
|
|
91
94
|
* Per declaration of the inventory, the packages the uses written inside it name; a
|
|
92
95
|
* use is held by the innermost declaration around it. The empty key holds what the
|
|
93
96
|
* compiler configuration names, which no deletion removes.
|
|
94
97
|
*/
|
|
95
98
|
readonly uses: ReadonlyMap<string, ReadonlySet<string>>;
|
|
99
|
+
/**
|
|
100
|
+
* Per absolute path of the file a use is written in, the packages it names, which a
|
|
101
|
+
* dependency only a manifest at or above that file declares serves. The empty key holds
|
|
102
|
+
* the uses no one file holds, which every manifest's dependency of that name serves.
|
|
103
|
+
*/
|
|
104
|
+
readonly at: ReadonlyMap<string, ReadonlySet<string>>;
|
|
96
105
|
/**
|
|
97
106
|
* The files writing a module specifier the checker left unanswered, each of which may
|
|
98
107
|
* use any dependency a manifest at or above it declares.
|
|
@@ -104,11 +113,14 @@ export interface ProjectNeeds {
|
|
|
104
113
|
export interface Dependencies {
|
|
105
114
|
/** Every declared dependency, manifest by manifest, in the order each writes them. */
|
|
106
115
|
readonly declared: readonly DeclaredDependency[];
|
|
107
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* The declared dependencies, by {@link declarationKey}, a use written at or below their
|
|
118
|
+
* manifest's directory needs, and each declared peer of a needed one.
|
|
119
|
+
*/
|
|
108
120
|
readonly needed: ReadonlySet<string>;
|
|
109
121
|
/**
|
|
110
122
|
* The declared dependencies whose command a script entry of the manifest declaring them,
|
|
111
|
-
* or a workflow step
|
|
123
|
+
* or a workflow step of the target, runs, each by {@link declarationKey}.
|
|
112
124
|
*/
|
|
113
125
|
readonly ran: ReadonlySet<string>;
|
|
114
126
|
/**
|
|
@@ -322,9 +334,10 @@ export function projectNeeds<Brand>(
|
|
|
322
334
|
programPackages,
|
|
323
335
|
);
|
|
324
336
|
const uses = new Map<string, Set<string>>();
|
|
325
|
-
const
|
|
337
|
+
const at = new Map<string, Set<string>>();
|
|
338
|
+
const use = (owners: readonly string[], named: Iterable<string>, path: string): void => {
|
|
326
339
|
for (const one of named) {
|
|
327
|
-
|
|
340
|
+
at.set(path, (at.get(path) ?? new Set()).add(one));
|
|
328
341
|
for (const owner of owners) {
|
|
329
342
|
let set = uses.get(owner);
|
|
330
343
|
if (set === undefined) {
|
|
@@ -335,11 +348,14 @@ export function projectNeeds<Brand>(
|
|
|
335
348
|
}
|
|
336
349
|
}
|
|
337
350
|
};
|
|
338
|
-
use([PROJECT_OWNER], packages);
|
|
351
|
+
use([PROJECT_OWNER], packages, project.configFile);
|
|
339
352
|
|
|
340
353
|
const unanswered: string[] = [];
|
|
341
354
|
for (const file of project.ownSourceFiles()) {
|
|
342
|
-
|
|
355
|
+
const inFile = (owners: readonly string[], named: Iterable<string>): void => {
|
|
356
|
+
use(owners, named, file.fileName);
|
|
357
|
+
};
|
|
358
|
+
if (fileNeeds(project, held, file, programPackages, inFile)) {
|
|
343
359
|
unanswered.push(file.fileName);
|
|
344
360
|
}
|
|
345
361
|
}
|
|
@@ -363,14 +379,14 @@ export function projectNeeds<Brand>(
|
|
|
363
379
|
for (const { file } of configuration.modules) {
|
|
364
380
|
const names = declaredAt(file.fileName);
|
|
365
381
|
for (const { node, text } of moduleStrings(file)) {
|
|
366
|
-
use(ownersOf(file, held, node), dependenciesNamed(text, names));
|
|
382
|
+
use(ownersOf(file, held, node), dependenciesNamed(text, names), file.fileName);
|
|
367
383
|
}
|
|
368
384
|
}
|
|
369
385
|
for (const { strings, manifestDirs } of configuration.documents) {
|
|
370
386
|
for (const manifestDir of manifestDirs) {
|
|
371
387
|
const names = declaredAt(joinPath(manifestDir, "package.json"));
|
|
372
388
|
for (const text of strings) {
|
|
373
|
-
use([PROJECT_OWNER], dependenciesNamed(text, names));
|
|
389
|
+
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
374
390
|
}
|
|
375
391
|
}
|
|
376
392
|
}
|
|
@@ -378,11 +394,11 @@ export function projectNeeds<Brand>(
|
|
|
378
394
|
for (const manifestDir of manifestDirs) {
|
|
379
395
|
const names = declaredAt(joinPath(manifestDir, "package.json"));
|
|
380
396
|
for (const { text } of moduleStrings(file)) {
|
|
381
|
-
use([PROJECT_OWNER], dependenciesNamed(text, names));
|
|
397
|
+
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
382
398
|
}
|
|
383
399
|
}
|
|
384
400
|
}
|
|
385
|
-
return {
|
|
401
|
+
return { uses, at, unanswered };
|
|
386
402
|
}
|
|
387
403
|
|
|
388
404
|
/** The declarations of the inventory a use written at one node belongs to. */
|
|
@@ -638,8 +654,10 @@ function installedOf(host: Host, root: string, name: string): InstalledDependenc
|
|
|
638
654
|
const read = readJSON(host, joinPath(dir, MODULES_DIR, name, MANIFEST));
|
|
639
655
|
if (read !== undefined && isRecord(read.value)) {
|
|
640
656
|
const bin = read.value["bin"];
|
|
657
|
+
const peers = read.value["peerDependencies"];
|
|
641
658
|
// A string `bin` names one command, the package's name without its scope.
|
|
642
659
|
return {
|
|
660
|
+
peers: isRecord(peers) ? Object.keys(peers) : [],
|
|
643
661
|
commands:
|
|
644
662
|
typeof bin === "string"
|
|
645
663
|
? bin === ""
|
|
@@ -711,13 +729,53 @@ function isInside(path: string, dir: string): boolean {
|
|
|
711
729
|
return dir === "" || path.startsWith(`${dir}/`);
|
|
712
730
|
}
|
|
713
731
|
|
|
732
|
+
/** The directory below the target root a declared dependency's manifest is in, `""` being the root. */
|
|
733
|
+
function manifestDir(dependency: DeclaredDependency): string {
|
|
734
|
+
const dir = dirnamePath(dependency.position.path);
|
|
735
|
+
return dir === "." ? "" : dir;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Adds to `needed` each declared dependency that the installed manifest of a needed or
|
|
740
|
+
* run declared dependency names as a peer, until no further one joins: the declaration of
|
|
741
|
+
* that name in the needing dependency's manifest or in a manifest above it, where the
|
|
742
|
+
* installed peer is resolved from. `peers` holds each declaration's installed peers by
|
|
743
|
+
* {@link declarationKey}.
|
|
744
|
+
*/
|
|
745
|
+
function addNeededPeers(
|
|
746
|
+
declared: readonly DeclaredDependency[],
|
|
747
|
+
peers: ReadonlyMap<string, readonly string[]>,
|
|
748
|
+
needed: Set<string>,
|
|
749
|
+
ran: ReadonlySet<string>,
|
|
750
|
+
): void {
|
|
751
|
+
const pending = declared.filter(
|
|
752
|
+
(dependency) => needed.has(declarationKey(dependency)) || ran.has(declarationKey(dependency)),
|
|
753
|
+
);
|
|
754
|
+
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
|
755
|
+
const below = manifestDir(next);
|
|
756
|
+
for (const peer of peers.get(declarationKey(next)) ?? []) {
|
|
757
|
+
for (const dependency of declared) {
|
|
758
|
+
const key = declarationKey(dependency);
|
|
759
|
+
if (
|
|
760
|
+
dependency.name === peer &&
|
|
761
|
+
!needed.has(key) &&
|
|
762
|
+
(below === manifestDir(dependency) || isInside(below, manifestDir(dependency)))
|
|
763
|
+
) {
|
|
764
|
+
needed.add(key);
|
|
765
|
+
pending.push(dependency);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
714
772
|
/**
|
|
715
|
-
* The run's dependency evidence
|
|
716
|
-
* `
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
773
|
+
* The run's dependency evidence over the target's manifest and those of `members` and
|
|
774
|
+
* `nested` packages below the root: each declared dependency's position, what the projects,
|
|
775
|
+
* scripts and workflow steps need, and each deletion candidate's last uses. A member in
|
|
776
|
+
* `unbuilt` holds source only in configurations the run could not build. An unreadable
|
|
777
|
+
* manifest declares nothing, nor does a nested package's uninstalled dependency, because
|
|
778
|
+
* no run requires a nested package to be installed.
|
|
721
779
|
*/
|
|
722
780
|
export function dependenciesOf(
|
|
723
781
|
host: Host,
|
|
@@ -726,12 +784,13 @@ export function dependenciesOf(
|
|
|
726
784
|
candidates: readonly string[],
|
|
727
785
|
members: readonly string[] = [],
|
|
728
786
|
unbuiltMembers: readonly string[] = [],
|
|
787
|
+
nested: readonly string[] = [],
|
|
729
788
|
): Dependencies {
|
|
730
789
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
731
790
|
const scopeOf = packageScope(host, targetRoot);
|
|
732
791
|
const manifests = new Map<string, string>([[root, MANIFEST]]);
|
|
733
792
|
const unbuilt = new Set<string>();
|
|
734
|
-
for (const dir of [...members, ...unbuiltMembers]) {
|
|
793
|
+
for (const dir of [...members, ...unbuiltMembers, ...nested]) {
|
|
735
794
|
const below = relativePath(root, dir);
|
|
736
795
|
if (below !== undefined && !manifests.has(dir)) {
|
|
737
796
|
manifests.set(dir, `${below}/${MANIFEST}`);
|
|
@@ -746,6 +805,8 @@ export function dependenciesOf(
|
|
|
746
805
|
const workflow = new Set(workflowTokens(host, targetRoot));
|
|
747
806
|
const declared: DeclaredDependency[] = [];
|
|
748
807
|
const ran = new Set<string>();
|
|
808
|
+
const peers = new Map<string, readonly string[]>();
|
|
809
|
+
const uninstalledHere = new Set(nested);
|
|
749
810
|
for (const [dir, path] of manifests) {
|
|
750
811
|
const read = readJSON(host, joinPath(dir, MANIFEST));
|
|
751
812
|
if (read === undefined || !isRecord(read.value)) {
|
|
@@ -753,13 +814,16 @@ export function dependenciesOf(
|
|
|
753
814
|
}
|
|
754
815
|
const tokens = scriptTokensOf(read.value);
|
|
755
816
|
for (const dependency of declaredIn(read.text, read.value, path, scopeOf(path))) {
|
|
756
|
-
declared.push(dependency);
|
|
757
817
|
const found = installedOf(host, dir, dependency.name);
|
|
818
|
+
if (found === undefined && uninstalledHere.has(dir)) {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
declared.push(dependency);
|
|
758
822
|
if (found === undefined) {
|
|
759
823
|
continue;
|
|
760
824
|
}
|
|
761
|
-
|
|
762
|
-
|
|
825
|
+
peers.set(declarationKey(dependency), found.peers);
|
|
826
|
+
const runs = (command: string): boolean => tokens.has(command) || workflow.has(command);
|
|
763
827
|
if (found.commands.some(runs)) {
|
|
764
828
|
ran.add(declarationKey(dependency));
|
|
765
829
|
}
|
|
@@ -769,25 +833,38 @@ export function dependenciesOf(
|
|
|
769
833
|
// A declaration several projects compile is one user, and a use any project makes is
|
|
770
834
|
// a use, so the uses are merged by declaration before anything is counted.
|
|
771
835
|
const needed = new Set<string>();
|
|
836
|
+
const byName = new Map<string, DeclaredDependency[]>();
|
|
837
|
+
for (const dependency of declared) {
|
|
838
|
+
byName.set(dependency.name, [...(byName.get(dependency.name) ?? []), dependency]);
|
|
839
|
+
}
|
|
772
840
|
const uses = new Map<string, Set<string>>();
|
|
773
841
|
for (const project of perProject) {
|
|
774
842
|
// A specifier the checker left unanswered may resolve to any dependency a manifest at
|
|
775
843
|
// or above its file declares, and what the configuration needs is never deleted.
|
|
776
844
|
const unanswered = project.unanswered.flatMap((file) => relativePath(root, file) ?? []);
|
|
777
|
-
const
|
|
778
|
-
.
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
.map((dependency) => dependency.name);
|
|
845
|
+
const unknownDeclared = declared.filter((dependency) =>
|
|
846
|
+
unanswered.some((file) => isInside(file, manifestDir(dependency))),
|
|
847
|
+
);
|
|
848
|
+
unknownDeclared.forEach((dependency) => needed.add(declarationKey(dependency)));
|
|
849
|
+
const unknown = unknownDeclared.map((dependency) => dependency.name);
|
|
783
850
|
const held = unknown.length === 0 ? [] : [[PROJECT_OWNER, new Set(unknown)] as const];
|
|
784
851
|
for (const [id, used] of [...project.uses, ...held]) {
|
|
785
852
|
const merged = uses.get(id) ?? new Set<string>();
|
|
786
853
|
used.forEach((one) => merged.add(one));
|
|
787
854
|
uses.set(id, merged);
|
|
788
855
|
}
|
|
789
|
-
[
|
|
856
|
+
for (const [path, named] of project.at) {
|
|
857
|
+
const below = path === ANYWHERE ? undefined : relativePath(root, path);
|
|
858
|
+
for (const one of named) {
|
|
859
|
+
for (const dependency of byName.get(one) ?? []) {
|
|
860
|
+
if (below === undefined || isInside(below, manifestDir(dependency))) {
|
|
861
|
+
needed.add(declarationKey(dependency));
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
790
866
|
}
|
|
867
|
+
addNeededPeers(declared, peers, needed, ran);
|
|
791
868
|
const users = new Map<string, number>();
|
|
792
869
|
for (const used of uses.values()) {
|
|
793
870
|
used.forEach((one) => users.set(one, (users.get(one) ?? 0) + 1));
|
package/src/deprecation.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the list's own tags from the tree.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { isVariableStatement, SyntaxKind, type Node } from "@typescript/native/
|
|
10
|
+
import { isVariableStatement, SyntaxKind, type Node } from "@typescript/native/ast";
|
|
11
11
|
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
12
12
|
import { isAnswered, UNANSWERED } from "./query.ts";
|
|
13
13
|
import type { Handle, ProjectView } from "./session.ts";
|
package/src/destructuring.ts
CHANGED
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
SyntaxKind,
|
|
24
24
|
type BinaryExpression,
|
|
25
25
|
type Node,
|
|
26
|
-
} from "@typescript/native/
|
|
27
|
-
import { TypeFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/
|
|
26
|
+
} from "@typescript/native/ast";
|
|
27
|
+
import { TypeFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/sync";
|
|
28
28
|
import {
|
|
29
29
|
isAnswered,
|
|
30
30
|
propertyTables,
|
package/src/discover.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DiagnosticCategory } from "@typescript/native/
|
|
2
|
-
import type { Diagnostic } from "@typescript/native/
|
|
1
|
+
import { DiagnosticCategory } from "@typescript/native/sync";
|
|
2
|
+
import type { Diagnostic } from "@typescript/native/sync";
|
|
3
3
|
import type { BuildConfiguration, ProjectConfiguration } from "./config.ts";
|
|
4
4
|
import type { Host } from "./host.ts";
|
|
5
5
|
import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.ts";
|