@cplieger/deadset-ts 8.0.0-dev.2 → 8.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/analysis.js +61 -10
- package/dist/src/augmentations.js +63 -1
- package/dist/src/build-scripts.js +437 -0
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/convention-rows.js +39 -2
- package/dist/src/conventions.js +15 -0
- package/dist/src/dependencies.js +98 -1
- package/dist/src/discover.js +14 -82
- package/dist/src/entry-points.js +9 -18
- package/dist/src/exempt.js +8 -0
- package/dist/src/findings/completion.js +66 -28
- package/dist/src/findings/intra-function.js +4 -2
- package/dist/src/findings/reads-and-writes.js +7 -6
- package/dist/src/findings/unused-declarations.js +4 -1
- package/dist/src/graph.js +5 -3
- package/dist/src/intra-function-parts.js +45 -17
- package/dist/src/kinds.js +8 -8
- package/dist/src/manifest.js +18 -0
- package/dist/src/matrix.js +38 -4
- package/dist/src/query.js +1 -0
- package/dist/src/report.js +23 -4
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +12 -8
- package/dist/src/roots.js +197 -42
- package/dist/src/serialization-contract.js +590 -12
- package/dist/src/sweep.js +7 -9
- package/dist/src/test-globals.js +234 -0
- package/dist/src/type-errors.js +11 -3
- package/dist/src/unseen-loads.js +107 -0
- package/dist/src/verbs/analyze.js +24 -3
- package/dist/src/workflow-steps.js +32 -15
- package/dist/src/yaml-document.js +355 -0
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/analysis.ts +109 -12
- package/src/augmentations.ts +77 -2
- package/src/build-scripts.ts +564 -0
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +64 -12
- package/src/convention-rows.ts +47 -2
- package/src/conventions.ts +16 -0
- package/src/dependencies.ts +110 -1
- package/src/discover.ts +14 -94
- package/src/entry-points.ts +10 -25
- package/src/exempt.ts +16 -0
- package/src/file-facts.ts +3 -0
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +76 -30
- package/src/findings/intra-function.ts +4 -2
- package/src/findings/reads-and-writes.ts +7 -6
- package/src/findings/unused-declarations.ts +6 -1
- package/src/graph.ts +5 -3
- package/src/intra-function-parts.ts +55 -16
- package/src/kinds.ts +8 -8
- package/src/manifest.ts +21 -3
- package/src/matrix.ts +51 -4
- package/src/query.ts +11 -0
- package/src/report.ts +56 -16
- package/src/reporters/annotations.ts +21 -10
- package/src/reporters/reporter.ts +1 -3
- package/src/reporters/sarif.ts +16 -8
- package/src/roots.ts +226 -46
- package/src/serialization-contract.ts +758 -12
- package/src/sweep.ts +23 -9
- package/src/test-globals.ts +275 -0
- package/src/type-errors.ts +14 -3
- package/src/unseen-loads.ts +145 -0
- package/src/verbs/analyze.ts +28 -3
- package/src/workflow-steps.ts +35 -17
- package/src/yaml-document.ts +377 -0
package/dist/src/roots.js
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* decides; the published API only supposes a consumer, so a published export is live
|
|
8
8
|
* under reachability and still a candidate under reference counting.
|
|
9
9
|
*/
|
|
10
|
-
import { isExpressionWithTypeArguments, isFunctionLikeDeclaration, isPropertyAccessExpression, isQualifiedName, isTypeQueryNode, isTypeReferenceNode, isPropertyDeclaration, isVariableDeclaration, isVariableStatement, } from "@typescript/native/ast";
|
|
10
|
+
import { isArrowFunction, isAssertionExpression, isExpressionWithTypeArguments, isFunctionLikeDeclaration, isIdentifier, isMethodDeclaration, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAssignment, isReturnStatement, isSatisfiesExpression, isPropertyAccessExpression, isQualifiedName, isTypeQueryNode, isTypeReferenceNode, isPropertyDeclaration, isVariableDeclaration, isVariableStatement, SyntaxKind, } from "@typescript/native/ast";
|
|
11
11
|
import { aliasChains } from "./alias-chain.js";
|
|
12
|
+
import { isGlobalBlock } from "./augmentations.js";
|
|
12
13
|
import { filesNamedBy, outsideChain, relativeSpecifiers, } from "./configuration-files.js";
|
|
13
14
|
import { conventionEntries } from "./conventions.js";
|
|
14
15
|
import { entryPoints } from "./entry-points.js";
|
|
@@ -260,6 +261,10 @@ export function roots(project, held, targetRoot, options) {
|
|
|
260
261
|
published.push(...entryFiles(entry));
|
|
261
262
|
}
|
|
262
263
|
}
|
|
264
|
+
if (options.publishedAPI) {
|
|
265
|
+
// A library's bundle hands what its entry exports to every program that loads it.
|
|
266
|
+
published.push(...points.filter((point) => point.rule === "bundler-entry").map((point) => point.file));
|
|
267
|
+
}
|
|
263
268
|
const publishedNames = new Set(published.map((file) => file.fileName));
|
|
264
269
|
const patterned = [];
|
|
265
270
|
for (const pattern of options.entryFiles) {
|
|
@@ -439,9 +444,11 @@ const EXPORTING_CONTAINERS = new Set(["file", "namespace"]);
|
|
|
439
444
|
* Every declaration a consumer of the library can name: what the published files export,
|
|
440
445
|
* and below each the members a consumer writes, a namespace's exported ones and any other
|
|
441
446
|
* declaration's public and protected ones. A declaration a published one's type names is
|
|
442
|
-
* published too, and so is an exported declaration a release tag marks
|
|
443
|
-
*
|
|
444
|
-
*
|
|
447
|
+
* published too, and so is an exported declaration a release tag marks, and every
|
|
448
|
+
* declaration a `declare global` block of a published file, or of a file holding a published
|
|
449
|
+
* declaration, holds: the consumer's compiler loads that file's typings and their global
|
|
450
|
+
* scope with it. A type name the checker leaves unanswered publishes every declaration
|
|
451
|
+
* spelled as it is, as `guessed`. A published file is not itself published.
|
|
445
452
|
*/
|
|
446
453
|
function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
|
|
447
454
|
const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
|
|
@@ -467,46 +474,80 @@ function publishedAPI(project, published, exportsOf, held, byPath, tagged) {
|
|
|
467
474
|
reach(id);
|
|
468
475
|
}
|
|
469
476
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
477
|
+
const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
|
|
478
|
+
const blocks = held.symbols.filter((one) => isGlobalBlock(one, held, files));
|
|
479
|
+
const fileOf = (id) => {
|
|
480
|
+
let at = byId.get(id);
|
|
481
|
+
while (at !== undefined && at.kind !== "file") {
|
|
482
|
+
at = byId.get(at.parent);
|
|
483
|
+
}
|
|
484
|
+
return at?.id;
|
|
485
|
+
};
|
|
486
|
+
const publishedFiles = published.flatMap((file) => held.declarations.get(nodeKey(file, file)) ?? []);
|
|
487
|
+
const globalsReached = () => {
|
|
488
|
+
const typed = new Set([...publishedFiles, ...[...reached].flatMap((id) => fileOf(id) ?? [])]);
|
|
489
|
+
const before = pending.length;
|
|
490
|
+
for (const block of blocks.filter((one) => typed.has(one.parent))) {
|
|
491
|
+
reach(block.id);
|
|
492
|
+
for (const member of children.get(block.id) ?? []) {
|
|
493
|
+
if (member.kind !== "type-parameter") {
|
|
480
494
|
reach(member.id);
|
|
481
495
|
}
|
|
482
496
|
}
|
|
483
497
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
guessed.add(id);
|
|
500
|
-
reach(id);
|
|
498
|
+
return pending.length > before;
|
|
499
|
+
};
|
|
500
|
+
do {
|
|
501
|
+
while (pending.length > 0) {
|
|
502
|
+
const round = [];
|
|
503
|
+
for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
|
|
504
|
+
round.push(next);
|
|
505
|
+
const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
|
|
506
|
+
for (const member of children.get(next) ?? []) {
|
|
507
|
+
const reachable = exporting
|
|
508
|
+
? member.exported
|
|
509
|
+
: member.visibility !== "private" && member.visibility !== "private-name";
|
|
510
|
+
if (member.kind !== "type-parameter" && reachable) {
|
|
511
|
+
reach(member.id);
|
|
512
|
+
}
|
|
501
513
|
}
|
|
502
514
|
}
|
|
503
|
-
|
|
515
|
+
const named = round.flatMap((id) => {
|
|
516
|
+
const symbol = byId.get(id);
|
|
517
|
+
return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
|
|
518
|
+
});
|
|
519
|
+
const inferred = inferredTypeSymbols(project, round.flatMap((id) => {
|
|
520
|
+
const symbol = byId.get(id);
|
|
521
|
+
return symbol === undefined ? [] : nodes.nodesOf(symbol);
|
|
522
|
+
}));
|
|
523
|
+
for (const symbol of inferred.types) {
|
|
504
524
|
for (const target of chains.chainOf(symbol)) {
|
|
505
525
|
reach(target.id);
|
|
506
526
|
}
|
|
507
527
|
}
|
|
508
|
-
|
|
509
|
-
|
|
528
|
+
for (const symbol of inferred.members) {
|
|
529
|
+
for (const target of chains.chainOf(symbol)) {
|
|
530
|
+
const member = byId.get(target.id);
|
|
531
|
+
if (member?.visibility === "public" || member?.visibility === "protected") {
|
|
532
|
+
reach(target.id);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
|
|
537
|
+
if (symbol === UNANSWERED) {
|
|
538
|
+
for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
|
|
539
|
+
guessed.add(id);
|
|
540
|
+
reach(id);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
else if (symbol !== undefined) {
|
|
544
|
+
for (const target of chains.chainOf(symbol)) {
|
|
545
|
+
reach(target.id);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
} while (globalsReached());
|
|
510
551
|
return { ids: [...reached], guessed: [...guessed] };
|
|
511
552
|
}
|
|
512
553
|
/** The type names declarations of the inventory write, each file walked once, when first asked. */
|
|
@@ -544,11 +585,101 @@ function declarationNodes(held, byPath) {
|
|
|
544
585
|
/** How deep the type arguments and constituents of an inferred type are followed. */
|
|
545
586
|
const INFERRED_DEPTH = 4;
|
|
546
587
|
/**
|
|
547
|
-
* The
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
|
|
551
|
-
|
|
588
|
+
* The values one function body returns, each through the type assertions around it: the
|
|
589
|
+
* value a caller receives carries the operand's type whatever type the assertion or the
|
|
590
|
+
* signature names. A nested function's returns are its own.
|
|
591
|
+
*/
|
|
592
|
+
function returnedValues(body) {
|
|
593
|
+
const found = [];
|
|
594
|
+
const add = (value) => {
|
|
595
|
+
for (let at = value; at !== undefined;) {
|
|
596
|
+
found.push(at);
|
|
597
|
+
at =
|
|
598
|
+
isAssertionExpression(at) || isSatisfiesExpression(at) || isParenthesizedExpression(at)
|
|
599
|
+
? at.expression
|
|
600
|
+
: undefined;
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
if (!isFunctionLikeDeclaration(body.parent) || body.parent.body !== body) {
|
|
604
|
+
return found;
|
|
605
|
+
}
|
|
606
|
+
if (isArrowFunction(body.parent) && body.kind !== SyntaxKind.Block) {
|
|
607
|
+
add(body);
|
|
608
|
+
return found;
|
|
609
|
+
}
|
|
610
|
+
const visit = (node) => {
|
|
611
|
+
if (isFunctionLikeDeclaration(node)) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (isReturnStatement(node) && node.expression !== undefined) {
|
|
615
|
+
add(node.expression);
|
|
616
|
+
}
|
|
617
|
+
node.forEachChild(visit);
|
|
618
|
+
};
|
|
619
|
+
body.forEachChild(visit);
|
|
620
|
+
return found;
|
|
621
|
+
}
|
|
622
|
+
/** The object literal one returned value is, written there or as the initializer of a variable `body` declares. */
|
|
623
|
+
function returnedLiteral(value, body) {
|
|
624
|
+
if (isObjectLiteralExpression(value)) {
|
|
625
|
+
return value;
|
|
626
|
+
}
|
|
627
|
+
if (!isIdentifier(value)) {
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
let found;
|
|
631
|
+
const visit = (node) => {
|
|
632
|
+
if (isVariableDeclaration(node) &&
|
|
633
|
+
isIdentifier(node.name) &&
|
|
634
|
+
node.name.text === value.text &&
|
|
635
|
+
node.initializer !== undefined) {
|
|
636
|
+
let at = node.initializer;
|
|
637
|
+
while (isAssertionExpression(at) ||
|
|
638
|
+
isSatisfiesExpression(at) ||
|
|
639
|
+
isParenthesizedExpression(at)) {
|
|
640
|
+
at = at.expression;
|
|
641
|
+
}
|
|
642
|
+
found = isObjectLiteralExpression(at) ? at : found;
|
|
643
|
+
}
|
|
644
|
+
if (!isFunctionLikeDeclaration(node)) {
|
|
645
|
+
node.forEachChild(visit);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
body.forEachChild(visit);
|
|
649
|
+
return found;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* The values one function body returns, and those each method of an object literal it
|
|
653
|
+
* returns returns, to a fixed depth: a caller holding the literal calls its methods, and
|
|
654
|
+
* receives what they return.
|
|
655
|
+
*/
|
|
656
|
+
function returnedThroughLiterals(body, depth = 0) {
|
|
657
|
+
const values = returnedValues(body);
|
|
658
|
+
if (depth >= INFERRED_DEPTH) {
|
|
659
|
+
return values;
|
|
660
|
+
}
|
|
661
|
+
const found = [...values];
|
|
662
|
+
for (const value of values) {
|
|
663
|
+
for (const property of returnedLiteral(value, body)?.properties ?? []) {
|
|
664
|
+
const fn = isMethodDeclaration(property)
|
|
665
|
+
? property
|
|
666
|
+
: isPropertyAssignment(property) && isFunctionLikeDeclaration(property.initializer)
|
|
667
|
+
? property.initializer
|
|
668
|
+
: undefined;
|
|
669
|
+
if (fn?.body !== undefined) {
|
|
670
|
+
found.push(...returnedThroughLiterals(fn.body, depth + 1));
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return found;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* The declarations of the types the compiler infers where a declaration writes none, and
|
|
678
|
+
* of the values a function returns: the type of a variable or a property with no
|
|
679
|
+
* annotation, the result of a function or a method with no result annotation, and the type
|
|
680
|
+
* of each value its body returns, each with the types its type arguments and its union or
|
|
681
|
+
* intersection constituents name, to a fixed depth; and as `members` the properties of each
|
|
682
|
+
* returned value's type. A question the checker leaves unanswered names nothing.
|
|
552
683
|
*/
|
|
553
684
|
function inferredTypeSymbols(project, declarations) {
|
|
554
685
|
const { queries } = project;
|
|
@@ -557,12 +688,24 @@ function inferredTypeSymbols(project, declarations) {
|
|
|
557
688
|
!isFunctionLikeDeclaration(node.initializer ?? node)
|
|
558
689
|
? [node.name]
|
|
559
690
|
: []);
|
|
691
|
+
const returned = declarations.flatMap((node) => {
|
|
692
|
+
const signature = isFunctionLikeDeclaration(node)
|
|
693
|
+
? node
|
|
694
|
+
: isVariableDeclaration(node) &&
|
|
695
|
+
node.initializer !== undefined &&
|
|
696
|
+
isFunctionLikeDeclaration(node.initializer)
|
|
697
|
+
? node.initializer
|
|
698
|
+
: undefined;
|
|
699
|
+
return signature?.body === undefined ? [] : returnedThroughLiterals(signature.body);
|
|
700
|
+
});
|
|
560
701
|
const types = [];
|
|
561
|
-
|
|
702
|
+
const returnedTypes = [];
|
|
703
|
+
queries.typesAt([...valued, ...returned]).forEach((type, at) => {
|
|
562
704
|
if (type !== UNANSWERED && type !== undefined) {
|
|
563
|
-
types.push(type);
|
|
705
|
+
(at < valued.length ? types : returnedTypes).push(type);
|
|
564
706
|
}
|
|
565
707
|
});
|
|
708
|
+
types.push(...returnedTypes);
|
|
566
709
|
for (const node of declarations) {
|
|
567
710
|
const signature = isFunctionLikeDeclaration(node)
|
|
568
711
|
? node
|
|
@@ -604,7 +747,19 @@ function inferredTypeSymbols(project, declarations) {
|
|
|
604
747
|
types.forEach((type) => {
|
|
605
748
|
visit(type, 0);
|
|
606
749
|
});
|
|
607
|
-
|
|
750
|
+
// A returned value carries each property of its type, one an anonymous type declares or
|
|
751
|
+
// an intersection merges included, which no type name reaches.
|
|
752
|
+
const members = new Map();
|
|
753
|
+
for (const type of returnedTypes) {
|
|
754
|
+
const parts = queries.constituents(type);
|
|
755
|
+
for (const one of [type, ...(parts === UNANSWERED ? [] : parts)]) {
|
|
756
|
+
const properties = queries.propertiesOf(one);
|
|
757
|
+
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
758
|
+
members.set(property.id, property);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return { types: [...found.values()], members: [...members.values()] };
|
|
608
763
|
}
|
|
609
764
|
/**
|
|
610
765
|
* The names of types one declaration's own syntax writes: in a type reference, a
|