@cplieger/deadset-ts 3.12.1 → 4.0.0
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 +4 -4
- package/dist/bin/node-host.js +10 -7
- package/dist/src/alias-chain.js +59 -7
- package/dist/src/analysis.js +217 -48
- package/dist/src/calls.js +23 -22
- package/dist/src/class-members.js +10 -11
- package/dist/src/components.js +4 -2
- package/dist/src/configured-declarations.js +56 -7
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/dependencies.js +34 -17
- package/dist/src/deprecation.js +35 -24
- package/dist/src/destructuring.js +37 -29
- package/dist/src/discover.js +105 -28
- package/dist/src/edges.js +4 -1
- package/dist/src/entry-points.js +7 -0
- package/dist/src/enum-group.js +46 -40
- package/dist/src/exempt.js +42 -1
- package/dist/src/findings/completion.js +37 -8
- package/dist/src/findings/emitters.js +20 -6
- package/dist/src/findings/interfaces.js +7 -1
- package/dist/src/findings/intra-function.js +91 -2
- package/dist/src/findings/self-check.js +30 -7
- package/dist/src/findings/unused-declarations.js +15 -3
- package/dist/src/findings-pass.js +18 -11
- package/dist/src/framework-lifecycle.js +8 -6
- package/dist/src/implementations.js +45 -23
- package/dist/src/interface-satisfaction.js +30 -27
- package/dist/src/intra-function-parts.js +660 -0
- package/dist/src/inventory.js +85 -4
- package/dist/src/json-chunks.js +76 -0
- package/dist/src/kinds.js +337 -35
- package/dist/src/partial.js +23 -0
- package/dist/src/query.js +207 -0
- package/dist/src/references.js +92 -20
- package/dist/src/reflective-lookup.js +46 -4
- package/dist/src/report.js +14 -3
- package/dist/src/reporters/annotations.js +9 -5
- package/dist/src/reporters/json.js +3 -2
- package/dist/src/reporters/sarif.js +22 -16
- package/dist/src/reporters/template.js +112 -12
- package/dist/src/reporters/text.js +8 -6
- package/dist/src/roots.js +27 -11
- package/dist/src/serialization-contract.js +53 -32
- package/dist/src/session.js +79 -39
- package/dist/src/signatures.js +32 -0
- package/dist/src/suppress-file.js +29 -7
- package/dist/src/suppress-inline.js +8 -1
- package/dist/src/suppress.js +27 -0
- package/dist/src/sweep.js +4 -1
- package/dist/src/value-reach.js +44 -25
- package/dist/src/verbs/analyze.js +39 -14
- package/dist/src/verbs/describe.js +12 -8
- package/dist/src/verbs/explain.js +22 -9
- package/dist/src/verbs/print-projects.js +40 -7
- package/dist/src/verbs/print-retained.js +6 -2
- package/dist/src/verbs/print-roots.js +5 -1
- package/dist/src/version.js +1 -1
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +75 -11
- package/src/analysis.ts +385 -104
- package/src/calls.ts +10 -9
- package/src/class-members.ts +10 -11
- package/src/components.ts +4 -2
- package/src/configured-declarations.ts +80 -8
- package/src/conformance-record.ts +2 -2
- package/src/dependencies.ts +42 -20
- package/src/deprecation.ts +53 -27
- package/src/destructuring.ts +50 -39
- package/src/discover.ts +177 -33
- package/src/edges.ts +4 -1
- package/src/entry-points.ts +7 -0
- package/src/enum-group.ts +56 -42
- package/src/exempt.ts +50 -2
- package/src/finding.ts +5 -1
- package/src/findings/completion.ts +46 -10
- package/src/findings/emitter.ts +3 -0
- package/src/findings/emitters.ts +27 -6
- package/src/findings/interfaces.ts +7 -1
- package/src/findings/intra-function.ts +106 -3
- package/src/findings/self-check.ts +48 -7
- package/src/findings/unused-declarations.ts +18 -4
- package/src/findings-pass.ts +26 -11
- package/src/framework-lifecycle.ts +10 -6
- package/src/host.ts +5 -3
- package/src/implementations.ts +59 -23
- package/src/interface-satisfaction.ts +30 -27
- package/src/intra-function-parts.ts +889 -0
- package/src/inventory.ts +97 -4
- package/src/json-chunks.ts +87 -0
- package/src/kinds.ts +355 -41
- package/src/partial.ts +40 -0
- package/src/query.ts +522 -0
- package/src/references.ts +119 -38
- package/src/reflective-lookup.ts +56 -6
- package/src/report.ts +32 -4
- package/src/reporters/annotations.ts +23 -21
- package/src/reporters/json.ts +3 -2
- package/src/reporters/reporter.ts +2 -2
- package/src/reporters/sarif.ts +27 -18
- package/src/reporters/template.ts +119 -14
- package/src/reporters/text.ts +12 -12
- package/src/roots.ts +34 -12
- package/src/serialization-contract.ts +59 -40
- package/src/session.ts +146 -58
- package/src/signatures.ts +55 -0
- package/src/suppress-file.ts +40 -8
- package/src/suppress-inline.ts +8 -1
- package/src/suppress.ts +31 -0
- package/src/sweep.ts +9 -4
- package/src/value-reach.ts +66 -28
- package/src/verbs/analyze.ts +51 -14
- package/src/verbs/describe.ts +13 -9
- package/src/verbs/explain.ts +20 -9
- package/src/verbs/print-projects.ts +53 -7
- package/src/verbs/print-retained.ts +6 -2
- package/src/verbs/print-roots.ts +5 -1
- package/src/version.ts +1 -1
- package/dist/src/reporters/rules.js +0 -178
- package/src/reporters/rules.ts +0 -195
package/src/inventory.ts
CHANGED
|
@@ -33,6 +33,7 @@ import type { Host } from "./host.ts";
|
|
|
33
33
|
import { dirnamePath, joinPath, relativePath } from "./paths.ts";
|
|
34
34
|
import { byPosition, positionKey, renderPosition, type Position } from "./position.ts";
|
|
35
35
|
import { computedComponent, nameComponent, renderRef, type Component, type Module } from "./ref.ts";
|
|
36
|
+
import { isAnswered, UNANSWERED } from "./query.ts";
|
|
36
37
|
import type { Handle, ProjectView } from "./session.ts";
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -160,6 +161,17 @@ export interface Inventory {
|
|
|
160
161
|
* that the omission is a number rather than a silence.
|
|
161
162
|
*/
|
|
162
163
|
readonly outsideOwnFiles: number;
|
|
164
|
+
/**
|
|
165
|
+
* The declarations whose module, namespace or container table the checker did not
|
|
166
|
+
* answer, in position order: whether each is exported, and which members a table
|
|
167
|
+
* would have added, are unknown.
|
|
168
|
+
*/
|
|
169
|
+
readonly unanswered: readonly string[];
|
|
170
|
+
/**
|
|
171
|
+
* Every declaration other than a file by each name a use may spell it with: its
|
|
172
|
+
* component, its declared name where that differs, and a type parameter's own name.
|
|
173
|
+
*/
|
|
174
|
+
readonly byName: ReadonlyMap<string, readonly string[]>;
|
|
163
175
|
}
|
|
164
176
|
|
|
165
177
|
/** A declaration under construction, before its reference is rendered. */
|
|
@@ -758,12 +770,28 @@ export function inventory<Brand>(
|
|
|
758
770
|
const handles: Handle<Brand>[] = batched.map((node) => project.handle(node));
|
|
759
771
|
const resolved = project.symbolsAt(handles);
|
|
760
772
|
const symbolOf = new Map<Node, TSSymbol>();
|
|
773
|
+
// A scope or a container whose symbol or table went unanswered keeps what its syntax
|
|
774
|
+
// declares, and everything under it is recorded unanswered.
|
|
775
|
+
const unansweredOwners = new Set<Building>();
|
|
761
776
|
batched.forEach((node, index) => {
|
|
762
777
|
const symbol = resolved[index];
|
|
763
|
-
if (symbol
|
|
778
|
+
if (typeof symbol === "object") {
|
|
764
779
|
symbolOf.set(node, symbol);
|
|
765
780
|
}
|
|
766
781
|
});
|
|
782
|
+
resolved.slice(0, exportScopes.length).forEach((symbol, index) => {
|
|
783
|
+
const scope = exportScopes[index];
|
|
784
|
+
if (symbol === UNANSWERED && scope !== undefined) {
|
|
785
|
+
unansweredOwners.add(scope.owner);
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
containers
|
|
789
|
+
.filter((container) => container.nameNode !== undefined)
|
|
790
|
+
.forEach((container, index) => {
|
|
791
|
+
if (resolved[exportScopes.length + index] === UNANSWERED) {
|
|
792
|
+
unansweredOwners.add(container.owner);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
767
795
|
|
|
768
796
|
let exportTables = 0;
|
|
769
797
|
for (const scope of exportScopes) {
|
|
@@ -772,7 +800,11 @@ export function inventory<Brand>(
|
|
|
772
800
|
continue;
|
|
773
801
|
}
|
|
774
802
|
exportTables += 1;
|
|
775
|
-
|
|
803
|
+
const table = project.queries.exportsOf(symbol);
|
|
804
|
+
if (!isAnswered(table)) {
|
|
805
|
+
unansweredOwners.add(scope.owner);
|
|
806
|
+
}
|
|
807
|
+
for (const [, exported] of isAnswered(table) ? table : []) {
|
|
776
808
|
for (const declaration of exported.declarations) {
|
|
777
809
|
const held = project.declarationAt(declaration);
|
|
778
810
|
if (held === undefined) {
|
|
@@ -794,11 +826,21 @@ export function inventory<Brand>(
|
|
|
794
826
|
if (symbol !== undefined) {
|
|
795
827
|
if (!container.membersAreExports) {
|
|
796
828
|
memberTables += 1;
|
|
797
|
-
|
|
829
|
+
const table = project.queries.membersOf(symbol);
|
|
830
|
+
if (isAnswered(table)) {
|
|
831
|
+
outsideOwnFiles += readTable(container, table, seen, false);
|
|
832
|
+
} else {
|
|
833
|
+
unansweredOwners.add(container.owner);
|
|
834
|
+
}
|
|
798
835
|
}
|
|
799
836
|
if (container.membersAreExports || container.isClass) {
|
|
800
837
|
memberTables += 1;
|
|
801
|
-
|
|
838
|
+
const table = project.queries.exportsOf(symbol);
|
|
839
|
+
if (isAnswered(table)) {
|
|
840
|
+
outsideOwnFiles += readTable(container, table, seen, container.isClass);
|
|
841
|
+
} else {
|
|
842
|
+
unansweredOwners.add(container.owner);
|
|
843
|
+
}
|
|
802
844
|
}
|
|
803
845
|
}
|
|
804
846
|
completeFromTree(container, seen);
|
|
@@ -919,6 +961,7 @@ export function inventory<Brand>(
|
|
|
919
961
|
return undefined;
|
|
920
962
|
}
|
|
921
963
|
|
|
964
|
+
const byId = new Map(building.map((record) => [positionKey(record.position), record]));
|
|
922
965
|
const symbols = building
|
|
923
966
|
.map(render)
|
|
924
967
|
.sort((a, b) => byPosition(a.position, b.position) || (a.ref < b.ref ? -1 : 1));
|
|
@@ -932,9 +975,50 @@ export function inventory<Brand>(
|
|
|
932
975
|
cost: { batches: 1, exportTables, memberTables },
|
|
933
976
|
starReExports,
|
|
934
977
|
outsideOwnFiles,
|
|
978
|
+
unanswered: symbols
|
|
979
|
+
.filter((symbol) => underUnanswered(byId.get(symbol.id), unansweredOwners))
|
|
980
|
+
.map((symbol) => symbol.id),
|
|
981
|
+
byName: namesOf(building),
|
|
935
982
|
};
|
|
936
983
|
}
|
|
937
984
|
|
|
985
|
+
/** Whether one declaration is, or is declared under, a scope or container left unanswered. */
|
|
986
|
+
function underUnanswered(record: Building | undefined, owners: ReadonlySet<Building>): boolean {
|
|
987
|
+
for (let at = record; at !== undefined; at = at.parent) {
|
|
988
|
+
if (owners.has(at)) {
|
|
989
|
+
return true;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/** Every declaration but a file, by each name a use may spell it with. */
|
|
996
|
+
function namesOf(building: readonly Building[]): ReadonlyMap<string, readonly string[]> {
|
|
997
|
+
const byName = new Map<string, string[]>();
|
|
998
|
+
for (const record of building) {
|
|
999
|
+
if (record.kind === "file") {
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const id = positionKey(record.position);
|
|
1003
|
+
const names =
|
|
1004
|
+
record.typeParameter === undefined
|
|
1005
|
+
? [record.component.text, record.localName]
|
|
1006
|
+
: [record.typeParameter];
|
|
1007
|
+
for (const name of new Set(names)) {
|
|
1008
|
+
if (name === undefined) {
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
const ids = byName.get(name);
|
|
1012
|
+
if (ids === undefined) {
|
|
1013
|
+
byName.set(name, [id]);
|
|
1014
|
+
} else if (!ids.includes(id)) {
|
|
1015
|
+
ids.push(id);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return byName;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
938
1022
|
/** The chain of components that names one declaration, read from the module inward. */
|
|
939
1023
|
function chainOf(record: Building): readonly Component[] {
|
|
940
1024
|
return [...record.parentChain, record.component];
|
|
@@ -984,3 +1068,12 @@ function refOf(record: Building, chain: readonly Component[]): string {
|
|
|
984
1068
|
...(record.typeParameter === undefined ? {} : { typeParameter: record.typeParameter }),
|
|
985
1069
|
});
|
|
986
1070
|
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Every declaration of an inventory other than a file, by each name a use may spell it
|
|
1074
|
+
* with. It is how a reference the checker could not resolve is still counted, as a
|
|
1075
|
+
* reference to every declaration that bears the name it spells.
|
|
1076
|
+
*/
|
|
1077
|
+
export function declarationsByName(held: Inventory): ReadonlyMap<string, readonly string[]> {
|
|
1078
|
+
return held.byName;
|
|
1079
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A document written in pieces, so no stage of writing it holds the whole document as
|
|
3
|
+
* one string: a string has a length limit far below what a large report reaches.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** The length a piece grows to before it is handed on. */
|
|
7
|
+
export const CHUNK_LENGTH = 1 << 16;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The pieces of `pieces` joined into runs of about {@link CHUNK_LENGTH}, the last one
|
|
11
|
+
* shorter. A piece longer than that is handed on whole.
|
|
12
|
+
*/
|
|
13
|
+
export function* coalesced(pieces: Iterable<string>): Generator<string, void, undefined> {
|
|
14
|
+
let held: string[] = [];
|
|
15
|
+
let length = 0;
|
|
16
|
+
for (const piece of pieces) {
|
|
17
|
+
held.push(piece);
|
|
18
|
+
length += piece.length;
|
|
19
|
+
if (length >= CHUNK_LENGTH) {
|
|
20
|
+
yield held.join("");
|
|
21
|
+
held = [];
|
|
22
|
+
length = 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (length > 0) {
|
|
26
|
+
yield held.join("");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Whether `JSON.stringify` writes nothing for a value where an object member holds it. */
|
|
31
|
+
function omitted(value: unknown): boolean {
|
|
32
|
+
return value === undefined || typeof value === "function" || typeof value === "symbol";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function* valueOf(
|
|
36
|
+
value: unknown,
|
|
37
|
+
prefix: string,
|
|
38
|
+
step: string,
|
|
39
|
+
): Generator<string, void, undefined> {
|
|
40
|
+
// With no indentation the document is compact: no line breaks, and no space after a key.
|
|
41
|
+
const nl = step === "" ? "" : "\n";
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
if (value.length === 0) {
|
|
44
|
+
yield "[]";
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const inner = prefix + step;
|
|
48
|
+
yield "[";
|
|
49
|
+
for (let at = 0; at < value.length; at += 1) {
|
|
50
|
+
yield at === 0 ? `${nl}${inner}` : `,${nl}${inner}`;
|
|
51
|
+
const element: unknown = value[at];
|
|
52
|
+
yield* omitted(element) ? ["null"] : valueOf(element, inner, step);
|
|
53
|
+
}
|
|
54
|
+
yield `${nl}${prefix}]`;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === "object" && value !== null) {
|
|
58
|
+
const inner = prefix + step;
|
|
59
|
+
let first = true;
|
|
60
|
+
for (const [key, member] of Object.entries(value)) {
|
|
61
|
+
if (omitted(member)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
yield `${first ? "{" : ","}${nl}${inner}${JSON.stringify(key)}:${step === "" ? "" : " "}`;
|
|
65
|
+
first = false;
|
|
66
|
+
yield* valueOf(member, inner, step);
|
|
67
|
+
}
|
|
68
|
+
yield first ? "{}" : `${nl}${prefix}}`;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
yield JSON.stringify(value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* One JSON document in pieces whose concatenation is the value as `JSON.stringify`
|
|
76
|
+
* writes it at `indent` spaces, followed by a newline. The value is plain data: objects,
|
|
77
|
+
* arrays, strings, finite numbers, booleans and null, an object member holding nothing
|
|
78
|
+
* being left out as `JSON.stringify` leaves it out.
|
|
79
|
+
*/
|
|
80
|
+
export function jsonChunks(value: unknown, indent: number): Generator<string, void, undefined> {
|
|
81
|
+
return coalesced(
|
|
82
|
+
(function* document(): Generator<string, void, undefined> {
|
|
83
|
+
yield* valueOf(value, "", " ".repeat(indent));
|
|
84
|
+
yield "\n";
|
|
85
|
+
})(),
|
|
86
|
+
);
|
|
87
|
+
}
|