@cplieger/deadset-ts 3.0.0 → 3.1.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 +1 -1
- package/dist/src/alias-chain.js +96 -0
- package/dist/src/analysis.js +14 -13
- package/dist/src/graph.js +5 -1
- package/dist/src/matrix.js +138 -0
- package/dist/src/references.js +78 -100
- package/dist/src/roots.js +14 -37
- package/dist/src/session.js +16 -5
- package/dist/src/sweep.js +13 -17
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alias-chain.ts +135 -0
- package/src/analysis.ts +21 -27
- package/src/components.ts +1 -1
- package/src/graph.ts +12 -3
- package/src/matrix.ts +212 -0
- package/src/references.ts +108 -113
- package/src/roots.ts +15 -46
- package/src/session.ts +18 -5
- package/src/sweep.ts +16 -22
package/dist/src/roots.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* The root set of one project: every declaration the analysis keeps live without a
|
|
3
3
|
* reference, and why.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* something outside the program reaches is live under
|
|
9
|
-
* candidate under
|
|
5
|
+
* Every root seeds REACHABILITY, and no root counts as a reference. A root of a kind
|
|
6
|
+
* that names a caller is live under reference counting as well, which the sweep
|
|
7
|
+
* decides; a root that only supposes a consumer is not, so an exported declaration
|
|
8
|
+
* something outside the program reaches is live under reachability and still a
|
|
9
|
+
* candidate under reference counting.
|
|
10
10
|
*/
|
|
11
|
+
import { aliasChains } from "./alias-chain.js";
|
|
11
12
|
import { REPOSITORY_DOCUMENT } from "./config.js";
|
|
12
13
|
import { entryPoints } from "./entry-points.js";
|
|
13
14
|
import { globExpression } from "./glob.js";
|
|
@@ -143,7 +144,10 @@ export function roots(project, held, targetRoot, options) {
|
|
|
143
144
|
for (const point of entryPoints(project, files, options.testFiles)) {
|
|
144
145
|
enter(point.file, point.rule, point.source, point.exports);
|
|
145
146
|
}
|
|
146
|
-
const exportsOf = exportTables(project, held, [
|
|
147
|
+
const exportsOf = exportTables(project, held, [
|
|
148
|
+
...entered.filter((entry) => entry.exports).map((entry) => entry.file),
|
|
149
|
+
...published,
|
|
150
|
+
]);
|
|
147
151
|
for (const entry of entered) {
|
|
148
152
|
const id = fileIds.get(entry.file.fileName);
|
|
149
153
|
if (id === undefined) {
|
|
@@ -181,41 +185,14 @@ export function roots(project, held, targetRoot, options) {
|
|
|
181
185
|
* its export table, star re-exports resolved into it. A file that is no module
|
|
182
186
|
* exports nothing.
|
|
183
187
|
*/
|
|
184
|
-
function exportTables(project, held, wanted
|
|
188
|
+
function exportTables(project, held, wanted) {
|
|
185
189
|
const unique = [...new Map(wanted.map((file) => [file.fileName, file])).values()];
|
|
186
190
|
const tables = new Map();
|
|
187
191
|
if (unique.length === 0) {
|
|
188
192
|
return tables;
|
|
189
193
|
}
|
|
190
194
|
const modules = project.symbolsAt(unique.map((file) => project.handle(file)));
|
|
191
|
-
const chains =
|
|
192
|
-
const chainOf = (symbol) => {
|
|
193
|
-
const cached = chains.get(symbol.id);
|
|
194
|
-
if (cached !== undefined) {
|
|
195
|
-
return cached;
|
|
196
|
-
}
|
|
197
|
-
const ids = [];
|
|
198
|
-
const walked = new Set();
|
|
199
|
-
let at = symbol;
|
|
200
|
-
while (at !== undefined && !walked.has(at.id)) {
|
|
201
|
-
walked.add(at.id);
|
|
202
|
-
for (const handle of at.declarations) {
|
|
203
|
-
if (!ownFiles.has(handle.path)) {
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
const node = project.declarationAt(handle)?.node;
|
|
207
|
-
const id = node === undefined
|
|
208
|
-
? undefined
|
|
209
|
-
: held.declarations.get(nodeKey(node.getSourceFile(), node));
|
|
210
|
-
if (id !== undefined && !ids.includes(id)) {
|
|
211
|
-
ids.push(id);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
at = project.aliasStepOf(at);
|
|
215
|
-
}
|
|
216
|
-
chains.set(symbol.id, ids);
|
|
217
|
-
return ids;
|
|
218
|
-
};
|
|
195
|
+
const chains = aliasChains(project, held);
|
|
219
196
|
unique.forEach((file, index) => {
|
|
220
197
|
const module = modules[index];
|
|
221
198
|
if (module === undefined) {
|
|
@@ -224,8 +201,8 @@ function exportTables(project, held, wanted, ownFiles) {
|
|
|
224
201
|
}
|
|
225
202
|
const ids = new Set();
|
|
226
203
|
for (const exported of project.checker.getExportsOfModule(module)) {
|
|
227
|
-
for (const
|
|
228
|
-
ids.add(id);
|
|
204
|
+
for (const target of chains.chainOf(exported)) {
|
|
205
|
+
ids.add(target.id);
|
|
229
206
|
}
|
|
230
207
|
}
|
|
231
208
|
tables.set(file.fileName, [...ids]);
|
package/dist/src/session.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { API, SymbolFlags } from "@typescript/native/unstable/sync";
|
|
2
2
|
import { isIdentifier } from "@typescript/native/unstable/ast";
|
|
3
|
+
import { DiscoveryError } from "./discover.js";
|
|
3
4
|
/**
|
|
4
5
|
* Opens the compiler client the analysis reads through.
|
|
5
6
|
*
|
|
@@ -81,19 +82,29 @@ export function diagnosticsOf(project) {
|
|
|
81
82
|
}
|
|
82
83
|
/**
|
|
83
84
|
* Runs `visit` over every project the compiler configurations name, inside one
|
|
84
|
-
* snapshot of one client.
|
|
85
|
+
* snapshot of one client, in the order they are named, and answers in that order.
|
|
85
86
|
*
|
|
86
87
|
* The snapshot is opened once with every configuration, so each becomes a project
|
|
87
88
|
* with its own program and checker; it is never updated, because the analysis
|
|
88
|
-
* reads and the tree does not change under it.
|
|
89
|
-
*
|
|
90
|
-
*
|
|
89
|
+
* reads and the tree does not change under it. The order the snapshot lists its
|
|
90
|
+
* projects in is the compiler's own, so each named configuration is looked up in it
|
|
91
|
+
* instead, and a configuration the snapshot opened no project for ends the run: the
|
|
92
|
+
* run would otherwise answer for fewer projects than it was asked to analyze. The
|
|
93
|
+
* snapshot is disposed once when the last project has been visited, and the client
|
|
94
|
+
* is released after it, whether the visit completed or threw.
|
|
91
95
|
*/
|
|
92
96
|
export function runSession(engine, configFiles, visit) {
|
|
93
97
|
try {
|
|
94
98
|
const snapshot = engine.updateSnapshot(configFiles);
|
|
95
99
|
try {
|
|
96
|
-
const
|
|
100
|
+
const opened = new Map(snapshot.getProjects().map((project) => [project.configFileName, project]));
|
|
101
|
+
const projects = configFiles.map((configFile) => {
|
|
102
|
+
const project = opened.get(configFile);
|
|
103
|
+
if (project === undefined) {
|
|
104
|
+
throw new DiscoveryError(`${configFile} opened no project`, []);
|
|
105
|
+
}
|
|
106
|
+
return visit(viewOf(project));
|
|
107
|
+
});
|
|
97
108
|
return { projects, timing: engine.getTimingInfo() };
|
|
98
109
|
}
|
|
99
110
|
finally {
|
package/dist/src/sweep.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Which declarations of one graph are dead, under which liveness relation
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Which declarations of one graph are dead, and under which liveness relation.
|
|
3
|
+
* Reference counting holds a declaration live when any declaration references it,
|
|
4
|
+
* whatever that one's own liveness, or when it is a root of a kind that names a caller;
|
|
5
5
|
* reachability holds it live when a path of references reaches it from a root. So a
|
|
6
6
|
* declaration only unreachable ones reference is live under the first, dead under the second.
|
|
7
7
|
*/
|
|
8
|
-
import { componentsOf } from "./components.js";
|
|
9
8
|
import { namesACaller, OUTSIDE } from "./graph.js";
|
|
10
9
|
/**
|
|
11
10
|
* Answers which declarations of one graph are dead under the input's mode.
|
|
12
11
|
*
|
|
13
12
|
* The order is the order the answers depend on: the callers and the marks are live
|
|
14
13
|
* before either relation runs, the candidate set is every judged declaration at least
|
|
15
|
-
* one relation does not hold live, the tests of dead code join it
|
|
16
|
-
* are computed over the set that results.
|
|
14
|
+
* one relation does not hold live, and the tests of dead code join it.
|
|
17
15
|
*/
|
|
18
16
|
export function sweep(graph, input) {
|
|
19
17
|
const marked = graph.symbols.map(() => false);
|
|
@@ -70,11 +68,7 @@ export function sweep(graph, input) {
|
|
|
70
68
|
testOfDeadCode: testOfDeadCode[at] === true,
|
|
71
69
|
});
|
|
72
70
|
});
|
|
73
|
-
return {
|
|
74
|
-
liveUnder: live,
|
|
75
|
-
candidates,
|
|
76
|
-
components: componentsOf(graph, dead, testOfDeadCode),
|
|
77
|
-
};
|
|
71
|
+
return { liveUnder: live, candidates };
|
|
78
72
|
}
|
|
79
73
|
/**
|
|
80
74
|
* Per declaration, whether a seed reaches it. The two seeds reach through different
|
|
@@ -86,15 +80,17 @@ function reachable(graph, marked, mode) {
|
|
|
86
80
|
const reached = graph.symbols.map(() => false);
|
|
87
81
|
const expanded = graph.symbols.map(() => false);
|
|
88
82
|
const queue = [];
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
83
|
+
// Reaching a file reaches its top level: the references written outside every
|
|
84
|
+
// declaration it holds. A reference that reads a file reads the module's namespace,
|
|
85
|
+
// which reaches what the module exports as well. An evaluation runs the top level
|
|
86
|
+
// and reads no export, and a root does not expand either: a file a runtime runs
|
|
87
|
+
// without reading its exports is a root by itself.
|
|
88
|
+
const enter = (at, expands) => {
|
|
93
89
|
if (!reached[at]) {
|
|
94
90
|
reached[at] = true;
|
|
95
91
|
queue.push(at);
|
|
96
92
|
}
|
|
97
|
-
if (!
|
|
93
|
+
if (!expands || expanded[at] === true) {
|
|
98
94
|
return;
|
|
99
95
|
}
|
|
100
96
|
expanded[at] = true;
|
|
@@ -108,7 +104,7 @@ function reachable(graph, marked, mode) {
|
|
|
108
104
|
if (!held && mode.production && edge.test) {
|
|
109
105
|
continue;
|
|
110
106
|
}
|
|
111
|
-
enter(edge.to,
|
|
107
|
+
enter(edge.to, !edge.evaluation);
|
|
112
108
|
}
|
|
113
109
|
}
|
|
114
110
|
};
|
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an import or export chain leads in one project: from one symbol to each
|
|
3
|
+
* declaration of the target it is, and along the chain of aliases it heads to each
|
|
4
|
+
* declaration every link of that chain is.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { SymbolFlags, type Symbol as TSSymbol } from "@typescript/native/unstable/sync";
|
|
8
|
+
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
9
|
+
import type { ProjectView } from "./session.ts";
|
|
10
|
+
|
|
11
|
+
/** One declaration a chain reaches, and whether a step along an alias reached it. */
|
|
12
|
+
export interface ChainTarget {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
/** False for a declaration the head symbol itself is, true for one a link stands for. */
|
|
15
|
+
readonly stepped: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The chain walks over one project's aliases, each step asked of the compiler once. */
|
|
19
|
+
export interface AliasChains {
|
|
20
|
+
/**
|
|
21
|
+
* The declarations of the inventory one symbol is, each once. A declaration outside
|
|
22
|
+
* the project's own files belongs to another program and is not one of them.
|
|
23
|
+
*/
|
|
24
|
+
declarationsOf(symbol: TSSymbol): readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* The declarations one symbol is, and those of every link of the alias chain it
|
|
27
|
+
* stands at the head of, each once and in chain order. A link the inventory does
|
|
28
|
+
* not declare is stepped over rather than named.
|
|
29
|
+
*/
|
|
30
|
+
chainOf(symbol: TSSymbol): readonly ChainTarget[];
|
|
31
|
+
/**
|
|
32
|
+
* The declarations of the first link after `symbol` along its chain that the
|
|
33
|
+
* inventory declares, stepping over the links it does not, or none where no later
|
|
34
|
+
* link is declared.
|
|
35
|
+
*/
|
|
36
|
+
nextDeclared(symbol: TSSymbol): readonly string[];
|
|
37
|
+
/** How many steps the compiler was asked for: one per alias, whatever the chains it heads. */
|
|
38
|
+
readonly steps: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The chain walks over one project, against the declarations of `held`, the same
|
|
43
|
+
* project's inventory.
|
|
44
|
+
*
|
|
45
|
+
* A declaration handle carries the file it is in, so whether it is one of the
|
|
46
|
+
* project's own files is answered before the handle is resolved: a declaration of a
|
|
47
|
+
* library file is never fetched to find out that the inventory does not hold it.
|
|
48
|
+
*/
|
|
49
|
+
export function aliasChains<Brand>(project: ProjectView<Brand>, held: Inventory): AliasChains {
|
|
50
|
+
const ownFiles = new Set(project.ownSourceFiles().map((file) => file.fileName));
|
|
51
|
+
const steps = new Map<number, TSSymbol | undefined>();
|
|
52
|
+
const chains = new Map<number, readonly ChainTarget[]>();
|
|
53
|
+
let asked = 0;
|
|
54
|
+
|
|
55
|
+
const declarationsOf = (symbol: TSSymbol): string[] => {
|
|
56
|
+
const ids: string[] = [];
|
|
57
|
+
for (const handle of symbol.declarations) {
|
|
58
|
+
if (!ownFiles.has(handle.path)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const node = project.declarationAt(handle)?.node;
|
|
62
|
+
const id =
|
|
63
|
+
node === undefined ? undefined : held.declarations.get(nodeKey(node.getSourceFile(), node));
|
|
64
|
+
if (id !== undefined && !ids.includes(id)) {
|
|
65
|
+
ids.push(id);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return ids;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* What one alias names, one link along. Only a symbol the binder flagged an alias is
|
|
73
|
+
* asked, which is the condition the step asserts on: a default export of an
|
|
74
|
+
* expression is written in an alias form and names nothing declared elsewhere, so its
|
|
75
|
+
* chain ends at itself.
|
|
76
|
+
*/
|
|
77
|
+
const stepOf = (symbol: TSSymbol): TSSymbol | undefined => {
|
|
78
|
+
if ((symbol.flags & SymbolFlags.Alias) === 0) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (steps.has(symbol.id)) {
|
|
82
|
+
return steps.get(symbol.id);
|
|
83
|
+
}
|
|
84
|
+
const next = project.aliasStepOf(symbol);
|
|
85
|
+
asked += 1;
|
|
86
|
+
steps.set(symbol.id, next);
|
|
87
|
+
return next;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const chainOf = (symbol: TSSymbol): readonly ChainTarget[] => {
|
|
91
|
+
const cached = chains.get(symbol.id);
|
|
92
|
+
if (cached !== undefined) {
|
|
93
|
+
return cached;
|
|
94
|
+
}
|
|
95
|
+
const targets: ChainTarget[] = [];
|
|
96
|
+
const walked = new Set<number>();
|
|
97
|
+
let at: TSSymbol | undefined = symbol;
|
|
98
|
+
let stepped = false;
|
|
99
|
+
while (at !== undefined && !walked.has(at.id)) {
|
|
100
|
+
walked.add(at.id);
|
|
101
|
+
for (const id of declarationsOf(at)) {
|
|
102
|
+
if (!targets.some((target) => target.id === id)) {
|
|
103
|
+
targets.push({ id, stepped });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
at = stepOf(at);
|
|
107
|
+
stepped = true;
|
|
108
|
+
}
|
|
109
|
+
chains.set(symbol.id, targets);
|
|
110
|
+
return targets;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const nextDeclared = (symbol: TSSymbol): readonly string[] => {
|
|
114
|
+
const walked = new Set<number>([symbol.id]);
|
|
115
|
+
let at = stepOf(symbol);
|
|
116
|
+
while (at !== undefined && !walked.has(at.id)) {
|
|
117
|
+
walked.add(at.id);
|
|
118
|
+
const ids = declarationsOf(at);
|
|
119
|
+
if (ids.length > 0) {
|
|
120
|
+
return ids;
|
|
121
|
+
}
|
|
122
|
+
at = stepOf(at);
|
|
123
|
+
}
|
|
124
|
+
return [];
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
declarationsOf,
|
|
129
|
+
chainOf,
|
|
130
|
+
nextDeclared,
|
|
131
|
+
get steps() {
|
|
132
|
+
return asked;
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
package/src/analysis.ts
CHANGED
|
@@ -12,17 +12,17 @@ import type { Diagnostic } from "@typescript/native/unstable/sync";
|
|
|
12
12
|
import type { Config, Provenance } from "./config.ts";
|
|
13
13
|
import { diagnosticErrors, discoverProjects, DiscoveryError } from "./discover.ts";
|
|
14
14
|
import type { Finding } from "./finding.ts";
|
|
15
|
-
import { graphOf } from "./graph.ts";
|
|
16
15
|
import type { Host } from "./host.ts";
|
|
17
|
-
import { inventory, type Inventory
|
|
16
|
+
import { inventory, type Inventory } from "./inventory.ts";
|
|
18
17
|
import { readManifest } from "./manifest.ts";
|
|
18
|
+
import { matrixOf, sweepMatrix, type Configured, type Matrix, type SweepResult } from "./matrix.ts";
|
|
19
19
|
import { relativePath, resolvePath } from "./paths.ts";
|
|
20
20
|
import { byPosition, type Position } from "./position.ts";
|
|
21
21
|
import { references } from "./references.ts";
|
|
22
22
|
import { roots, unmatchedEverywhere, unmatchedRoots, type RootKind, type Roots } from "./roots.ts";
|
|
23
23
|
import type { Scope } from "./scope.ts";
|
|
24
24
|
import { diagnosticsOf, runSession, type Engine, type ProjectView } from "./session.ts";
|
|
25
|
-
import {
|
|
25
|
+
import type { SweepInput } from "./sweep.ts";
|
|
26
26
|
|
|
27
27
|
/** The setting whose source decides where a finding about a configured root sits. */
|
|
28
28
|
const ROOTS_SETTING = "roots.patterns";
|
|
@@ -43,7 +43,7 @@ export interface RunRoot {
|
|
|
43
43
|
export interface RunRoots {
|
|
44
44
|
/**
|
|
45
45
|
* The configurations the run analyzed, each by the name discovery gives its
|
|
46
|
-
* project, in discovery order.
|
|
46
|
+
* project, in discovery order: the order a declared matrix lists them in.
|
|
47
47
|
*/
|
|
48
48
|
readonly configurations: readonly string[];
|
|
49
49
|
/**
|
|
@@ -122,10 +122,7 @@ function readProjects<Answer>(
|
|
|
122
122
|
}
|
|
123
123
|
const held = inventory(project, host, targetRoot);
|
|
124
124
|
const rooted = roots(project, held, targetRoot, options);
|
|
125
|
-
const configuration =
|
|
126
|
-
ids.get(project.configFile) ??
|
|
127
|
-
relativePath(targetRoot, project.configFile) ??
|
|
128
|
-
project.configFile;
|
|
125
|
+
const configuration = ids.get(project.configFile) ?? project.configFile;
|
|
129
126
|
return stage(project, { configuration, held, rooted });
|
|
130
127
|
});
|
|
131
128
|
if (failures.length > 0) {
|
|
@@ -195,42 +192,39 @@ export function runRoots(
|
|
|
195
192
|
};
|
|
196
193
|
}
|
|
197
194
|
|
|
198
|
-
/**
|
|
199
|
-
export interface
|
|
200
|
-
|
|
201
|
-
readonly configuration: string;
|
|
202
|
-
readonly symbols: readonly InventorySymbol[];
|
|
195
|
+
/** The run's sweep, beside the matrix it judged. */
|
|
196
|
+
export interface RunSweep {
|
|
197
|
+
readonly matrix: Matrix;
|
|
203
198
|
readonly sweep: SweepResult;
|
|
204
199
|
}
|
|
205
200
|
|
|
206
201
|
/**
|
|
207
|
-
* Sweeps
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
* swept under the input the caller decided once for the run.
|
|
202
|
+
* Sweeps the run the scope and the configuration describe. Each project's references
|
|
203
|
+
* are read against its own inventory with the configured test-file patterns while its
|
|
204
|
+
* view is open; the projects then merge into one matrix in discovery order, which is
|
|
205
|
+
* swept under the input the caller decided once for the run. A project that carries
|
|
206
|
+
* an error ends the run before any project is swept.
|
|
211
207
|
*/
|
|
212
|
-
export function
|
|
208
|
+
export function runSweep(
|
|
213
209
|
engine: Engine,
|
|
214
210
|
host: Host,
|
|
215
211
|
scope: Scope,
|
|
216
212
|
config: Config,
|
|
217
213
|
input: SweepInput,
|
|
218
|
-
):
|
|
214
|
+
): RunSweep {
|
|
219
215
|
const targetRoot = scope.target.path;
|
|
220
|
-
|
|
216
|
+
const configured = readProjects(engine, host, scope, config, (project, read): Configured => {
|
|
221
217
|
const resolved = references(project, read.held, targetRoot, {
|
|
222
218
|
testFiles: config.ts.testFiles,
|
|
223
219
|
});
|
|
224
|
-
const graph = graphOf(
|
|
225
|
-
read.held.symbols,
|
|
226
|
-
resolved.references,
|
|
227
|
-
read.rooted.liveUnderReachability,
|
|
228
|
-
resolved.testFilePaths,
|
|
229
|
-
);
|
|
230
220
|
return {
|
|
231
221
|
configuration: read.configuration,
|
|
232
222
|
symbols: read.held.symbols,
|
|
233
|
-
|
|
223
|
+
references: resolved.references,
|
|
224
|
+
roots: read.rooted.liveUnderReachability,
|
|
225
|
+
testFiles: resolved.testFilePaths,
|
|
234
226
|
};
|
|
235
227
|
});
|
|
228
|
+
const matrix = matrixOf(configured);
|
|
229
|
+
return { matrix, sweep: sweepMatrix(matrix, input) };
|
|
236
230
|
}
|
package/src/components.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface Component {
|
|
|
35
35
|
* neither and is counted by its own component.
|
|
36
36
|
*/
|
|
37
37
|
readonly falls: readonly string[];
|
|
38
|
-
/** The source lines the deletion removes
|
|
38
|
+
/** The source lines the deletion removes: the distinct lines {@link Component.falls} spans. */
|
|
39
39
|
readonly deletableLines: number;
|
|
40
40
|
}
|
|
41
41
|
|
package/src/graph.ts
CHANGED
|
@@ -72,6 +72,11 @@ export function namesACaller(kind: RootKind): boolean {
|
|
|
72
72
|
export interface Edge {
|
|
73
73
|
readonly to: number;
|
|
74
74
|
readonly test: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Whether the reference evaluates the module whose file it names, which reaches the
|
|
77
|
+
* module's top level and none of what it exports.
|
|
78
|
+
*/
|
|
79
|
+
readonly evaluation: boolean;
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
/** How many references one declaration carries, split by the classification of the file that made each. */
|
|
@@ -102,8 +107,8 @@ export interface Graph {
|
|
|
102
107
|
readonly subject: readonly boolean[];
|
|
103
108
|
/**
|
|
104
109
|
* Per declaration, the declarations its export table names where it is a file, and
|
|
105
|
-
* nothing for any other declaration. A reference
|
|
106
|
-
*
|
|
110
|
+
* nothing for any other declaration. A reference that reads a file reads the module's
|
|
111
|
+
* namespace, which reaches everything the module exports.
|
|
107
112
|
*/
|
|
108
113
|
readonly exportsOf: readonly (readonly number[])[];
|
|
109
114
|
/** Every root naming a declaration the inventory holds, in the order the root set gives them. */
|
|
@@ -144,7 +149,11 @@ export function graphOf(
|
|
|
144
149
|
counts.production += 1;
|
|
145
150
|
}
|
|
146
151
|
}
|
|
147
|
-
out[at(reference.from)]?.push({
|
|
152
|
+
out[at(reference.from)]?.push({
|
|
153
|
+
to,
|
|
154
|
+
test: reference.test,
|
|
155
|
+
evaluation: reference.use === "evaluation",
|
|
156
|
+
});
|
|
148
157
|
}
|
|
149
158
|
|
|
150
159
|
const parent = symbols.map((symbol) => at(symbol.parent));
|