@cplieger/deadset-ts 2.3.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 +7 -16
- package/dist/src/alias-chain.js +96 -0
- package/dist/src/analysis.js +50 -16
- package/dist/src/components.js +289 -0
- package/dist/src/discover.js +98 -24
- package/dist/src/graph.js +120 -0
- package/dist/src/index.js +0 -2
- package/dist/src/matrix.js +138 -0
- package/dist/src/print.js +3 -6
- package/dist/src/references.js +162 -79
- package/dist/src/resolve.js +82 -20
- package/dist/src/roots.js +14 -37
- package/dist/src/run.js +11 -7
- package/dist/src/schema.js +1 -1
- package/dist/src/session.js +16 -5
- package/dist/src/sweep.js +152 -0
- package/dist/src/version.js +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alias-chain.ts +135 -0
- package/src/analysis.ts +85 -24
- package/src/components.ts +372 -0
- package/src/config.ts +23 -2
- package/src/discover.ts +144 -29
- package/src/graph.ts +187 -0
- package/src/index.ts +0 -20
- package/src/matrix.ts +212 -0
- package/src/print.ts +5 -6
- package/src/references.ts +235 -93
- package/src/resolve.ts +117 -24
- package/src/roots.ts +15 -46
- package/src/run.ts +11 -7
- package/src/schema.ts +5 -2
- package/src/session.ts +18 -5
- package/src/sweep.ts +224 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
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
|
+
* reachability holds it live when a path of references reaches it from a root. So a
|
|
6
|
+
* declaration only unreachable ones reference is live under the first, dead under the second.
|
|
7
|
+
*/
|
|
8
|
+
import { namesACaller, OUTSIDE } from "./graph.js";
|
|
9
|
+
/**
|
|
10
|
+
* Answers which declarations of one graph are dead under the input's mode.
|
|
11
|
+
*
|
|
12
|
+
* The order is the order the answers depend on: the callers and the marks are live
|
|
13
|
+
* before either relation runs, the candidate set is every judged declaration at least
|
|
14
|
+
* one relation does not hold live, and the tests of dead code join it.
|
|
15
|
+
*/
|
|
16
|
+
export function sweep(graph, input) {
|
|
17
|
+
const marked = graph.symbols.map(() => false);
|
|
18
|
+
for (const id of input.marked) {
|
|
19
|
+
const at = graph.at(id);
|
|
20
|
+
if (at !== OUTSIDE) {
|
|
21
|
+
marked[at] = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Every root and every mark seeds reachability. A root of a kind that names a caller,
|
|
25
|
+
// and a mark, are live under reference counting as well; a root that only supposes a
|
|
26
|
+
// caller is not.
|
|
27
|
+
const called = graph.symbols.map(() => false);
|
|
28
|
+
for (const root of graph.rooted) {
|
|
29
|
+
if (namesACaller(root.kind)) {
|
|
30
|
+
called[root.at] = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const counted = (at) => {
|
|
34
|
+
const made = graph.made[at];
|
|
35
|
+
if (made === undefined) {
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
return input.mode.production ? made.production : made.production + made.test;
|
|
39
|
+
};
|
|
40
|
+
const reached = reachable(graph, marked, input.mode);
|
|
41
|
+
const liveUnder = graph.symbols.map((_symbol, at) => {
|
|
42
|
+
const relations = [];
|
|
43
|
+
if (marked[at] === true || called[at] === true || counted(at) > 0) {
|
|
44
|
+
relations.push("reference-counting");
|
|
45
|
+
}
|
|
46
|
+
if (reached[at] === true) {
|
|
47
|
+
relations.push("reachability");
|
|
48
|
+
}
|
|
49
|
+
return relations;
|
|
50
|
+
});
|
|
51
|
+
const dead = graph.symbols.map((_symbol, at) => graph.subject[at] === true && (liveUnder[at]?.length ?? 0) < 2);
|
|
52
|
+
const testOfDeadCode = admitTestsOfDeadCode(graph, dead, marked);
|
|
53
|
+
const candidates = [];
|
|
54
|
+
const live = new Map();
|
|
55
|
+
graph.symbols.forEach((symbol, at) => {
|
|
56
|
+
const relations = liveUnder[at] ?? [];
|
|
57
|
+
if (relations.length > 0) {
|
|
58
|
+
live.set(symbol.id, relations);
|
|
59
|
+
}
|
|
60
|
+
if (dead[at] !== true) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
candidates.push({
|
|
64
|
+
id: symbol.id,
|
|
65
|
+
productionRefs: graph.made[at]?.production ?? 0,
|
|
66
|
+
testRefs: graph.made[at]?.test ?? 0,
|
|
67
|
+
relation: counted(at) === 0 ? "reference-counting" : "reachability",
|
|
68
|
+
testOfDeadCode: testOfDeadCode[at] === true,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
return { liveUnder: live, candidates };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Per declaration, whether a seed reaches it. The two seeds reach through different
|
|
75
|
+
* reference sets: a marked declaration is live by a mechanism the analysis cannot see, so
|
|
76
|
+
* no mode withholds a reference it makes, while a root reaches through the references the
|
|
77
|
+
* mode counts. The marks run first, so what they reach is expanded under the wider rule.
|
|
78
|
+
*/
|
|
79
|
+
function reachable(graph, marked, mode) {
|
|
80
|
+
const reached = graph.symbols.map(() => false);
|
|
81
|
+
const expanded = graph.symbols.map(() => false);
|
|
82
|
+
const queue = [];
|
|
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) => {
|
|
89
|
+
if (!reached[at]) {
|
|
90
|
+
reached[at] = true;
|
|
91
|
+
queue.push(at);
|
|
92
|
+
}
|
|
93
|
+
if (!expands || expanded[at] === true) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
expanded[at] = true;
|
|
97
|
+
for (const exported of graph.exportsOf[at] ?? []) {
|
|
98
|
+
enter(exported, false);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const walk = (held) => {
|
|
102
|
+
for (let at = queue.pop(); at !== undefined; at = queue.pop()) {
|
|
103
|
+
for (const edge of graph.out[at] ?? []) {
|
|
104
|
+
if (!held && mode.production && edge.test) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
enter(edge.to, !edge.evaluation);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
marked.forEach((mark, at) => {
|
|
112
|
+
if (mark) {
|
|
113
|
+
enter(at, false);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
walk(true);
|
|
117
|
+
for (const root of graph.rooted) {
|
|
118
|
+
enter(root.at, false);
|
|
119
|
+
}
|
|
120
|
+
walk(false);
|
|
121
|
+
return reached;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Admits every test declaration whose set of referenced production declarations is
|
|
125
|
+
* non-empty and wholly dead, and returns which it admitted. The set is read from every
|
|
126
|
+
* reference the test makes, not the ones the mode counts: a production sweep counts none,
|
|
127
|
+
* which is what makes the targets dead. A test referencing one live target is never admitted.
|
|
128
|
+
*/
|
|
129
|
+
function admitTestsOfDeadCode(graph, dead, marked) {
|
|
130
|
+
const admitted = graph.symbols.map(() => false);
|
|
131
|
+
graph.symbols.forEach((_symbol, at) => {
|
|
132
|
+
if (graph.test[at] !== true || graph.subject[at] !== true || marked[at] === true) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let targets = 0;
|
|
136
|
+
let live = 0;
|
|
137
|
+
for (const edge of graph.out[at] ?? []) {
|
|
138
|
+
if (graph.test[edge.to] === true || graph.subject[edge.to] !== true) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
targets += 1;
|
|
142
|
+
if (dead[edge.to] !== true) {
|
|
143
|
+
live += 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (targets > 0 && live === 0) {
|
|
147
|
+
dead[at] = true;
|
|
148
|
+
admitted[at] = true;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return admitted;
|
|
152
|
+
}
|
package/dist/src/version.js
CHANGED
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
|
@@ -13,13 +13,16 @@ 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
15
|
import type { Host } from "./host.ts";
|
|
16
|
-
import { inventory } from "./inventory.ts";
|
|
16
|
+
import { inventory, type Inventory } from "./inventory.ts";
|
|
17
17
|
import { readManifest } from "./manifest.ts";
|
|
18
|
+
import { matrixOf, sweepMatrix, type Configured, type Matrix, type SweepResult } from "./matrix.ts";
|
|
18
19
|
import { relativePath, resolvePath } from "./paths.ts";
|
|
19
20
|
import { byPosition, type Position } from "./position.ts";
|
|
20
|
-
import {
|
|
21
|
+
import { references } from "./references.ts";
|
|
22
|
+
import { roots, unmatchedEverywhere, unmatchedRoots, type RootKind, type Roots } from "./roots.ts";
|
|
21
23
|
import type { Scope } from "./scope.ts";
|
|
22
|
-
import { diagnosticsOf, runSession, type Engine } from "./session.ts";
|
|
24
|
+
import { diagnosticsOf, runSession, type Engine, type ProjectView } from "./session.ts";
|
|
25
|
+
import type { SweepInput } from "./sweep.ts";
|
|
23
26
|
|
|
24
27
|
/** The setting whose source decides where a finding about a configured root sits. */
|
|
25
28
|
const ROOTS_SETTING = "roots.patterns";
|
|
@@ -39,8 +42,8 @@ export interface RunRoot {
|
|
|
39
42
|
/** The root set of one run, and the findings about the roots the configuration names. */
|
|
40
43
|
export interface RunRoots {
|
|
41
44
|
/**
|
|
42
|
-
* The configurations the run analyzed
|
|
43
|
-
*
|
|
45
|
+
* The configurations the run analyzed, each by the name discovery gives its
|
|
46
|
+
* project, in discovery order: the order a declared matrix lists them in.
|
|
44
47
|
*/
|
|
45
48
|
readonly configurations: readonly string[];
|
|
46
49
|
/**
|
|
@@ -80,26 +83,31 @@ function compare(a: string, b: string): number {
|
|
|
80
83
|
return a < b ? -1 : 1;
|
|
81
84
|
}
|
|
82
85
|
|
|
86
|
+
/** What every stage after the root set reads of one project. */
|
|
87
|
+
interface ProjectRead {
|
|
88
|
+
readonly configuration: string;
|
|
89
|
+
readonly held: Inventory;
|
|
90
|
+
readonly rooted: Roots;
|
|
91
|
+
}
|
|
92
|
+
|
|
83
93
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* configured string is a finding when it names nothing in every project, and the
|
|
89
|
-
* finding sits in the document that declared the roots.
|
|
94
|
+
* Reads every project the scope discovers, in one snapshot: each is checked for errors
|
|
95
|
+
* first, then enumerated, then rooted, and `stage` reads the rest of what it needs from
|
|
96
|
+
* the project while its view is open. A project carrying an error fails the run with no
|
|
97
|
+
* answer, as every verb that reads declarations does.
|
|
90
98
|
*/
|
|
91
|
-
|
|
99
|
+
function readProjects<Answer>(
|
|
92
100
|
engine: Engine,
|
|
93
101
|
host: Host,
|
|
94
102
|
scope: Scope,
|
|
95
103
|
config: Config,
|
|
96
|
-
|
|
97
|
-
):
|
|
104
|
+
stage: <Brand>(project: ProjectView<Brand>, read: ProjectRead) => Answer,
|
|
105
|
+
): readonly Answer[] {
|
|
98
106
|
const targetRoot = scope.target.path;
|
|
99
|
-
const discovered = discoverProjects(engine, host, scope);
|
|
100
|
-
const
|
|
107
|
+
const discovered = discoverProjects(engine, host, scope, config.analysis.configurations);
|
|
108
|
+
const ids = new Map(discovered.projects.map((project) => [project.configFile, project.id]));
|
|
101
109
|
const options = {
|
|
102
|
-
manifest,
|
|
110
|
+
manifest: readManifest(host, targetRoot),
|
|
103
111
|
patterns: config.rootPatterns,
|
|
104
112
|
entryFiles: config.ts.entryFiles,
|
|
105
113
|
testFiles: config.ts.testFiles,
|
|
@@ -113,7 +121,9 @@ export function runRoots(
|
|
|
113
121
|
return undefined;
|
|
114
122
|
}
|
|
115
123
|
const held = inventory(project, host, targetRoot);
|
|
116
|
-
|
|
124
|
+
const rooted = roots(project, held, targetRoot, options);
|
|
125
|
+
const configuration = ids.get(project.configFile) ?? project.configFile;
|
|
126
|
+
return stage(project, { configuration, held, rooted });
|
|
117
127
|
});
|
|
118
128
|
if (failures.length > 0) {
|
|
119
129
|
throw new DiscoveryError(
|
|
@@ -121,15 +131,29 @@ export function runRoots(
|
|
|
121
131
|
failures,
|
|
122
132
|
);
|
|
123
133
|
}
|
|
134
|
+
return projects.filter((answer): answer is Answer => answer !== undefined);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The root set of the run the scope and the configuration describe.
|
|
139
|
+
*
|
|
140
|
+
* A configured string is a finding when it names nothing in every project, and the
|
|
141
|
+
* finding sits in the document that declared the roots.
|
|
142
|
+
*/
|
|
143
|
+
export function runRoots(
|
|
144
|
+
engine: Engine,
|
|
145
|
+
host: Host,
|
|
146
|
+
scope: Scope,
|
|
147
|
+
config: Config,
|
|
148
|
+
provenance: Provenance,
|
|
149
|
+
): RunRoots {
|
|
150
|
+
const targetRoot = scope.target.path;
|
|
151
|
+
const projects = readProjects(engine, host, scope, config, (_project, read) => read);
|
|
124
152
|
|
|
125
153
|
const configurations: string[] = [];
|
|
126
154
|
const merged = new Map<string, Omit<RunRoot, "configurations"> & { configurations: string[] }>();
|
|
127
155
|
for (const answer of projects) {
|
|
128
|
-
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
const configuration =
|
|
132
|
-
relativePath(targetRoot, answer.rooted.configFile) ?? answer.rooted.configFile;
|
|
156
|
+
const configuration = answer.configuration;
|
|
133
157
|
configurations.push(configuration);
|
|
134
158
|
const symbols = new Map(answer.held.symbols.map((symbol) => [symbol.id, symbol]));
|
|
135
159
|
for (const root of answer.rooted.liveUnderReachability) {
|
|
@@ -159,7 +183,7 @@ export function runRoots(
|
|
|
159
183
|
);
|
|
160
184
|
const unmatched = unmatchedEverywhere(
|
|
161
185
|
config.rootPatterns,
|
|
162
|
-
projects.
|
|
186
|
+
projects.map((answer) => answer.rooted),
|
|
163
187
|
);
|
|
164
188
|
return {
|
|
165
189
|
configurations,
|
|
@@ -167,3 +191,40 @@ export function runRoots(
|
|
|
167
191
|
findings: unmatchedRoots(unmatched, documentOf(host, provenance, ROOTS_SETTING, targetRoot)),
|
|
168
192
|
};
|
|
169
193
|
}
|
|
194
|
+
|
|
195
|
+
/** The run's sweep, beside the matrix it judged. */
|
|
196
|
+
export interface RunSweep {
|
|
197
|
+
readonly matrix: Matrix;
|
|
198
|
+
readonly sweep: SweepResult;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
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.
|
|
207
|
+
*/
|
|
208
|
+
export function runSweep(
|
|
209
|
+
engine: Engine,
|
|
210
|
+
host: Host,
|
|
211
|
+
scope: Scope,
|
|
212
|
+
config: Config,
|
|
213
|
+
input: SweepInput,
|
|
214
|
+
): RunSweep {
|
|
215
|
+
const targetRoot = scope.target.path;
|
|
216
|
+
const configured = readProjects(engine, host, scope, config, (project, read): Configured => {
|
|
217
|
+
const resolved = references(project, read.held, targetRoot, {
|
|
218
|
+
testFiles: config.ts.testFiles,
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
configuration: read.configuration,
|
|
222
|
+
symbols: read.held.symbols,
|
|
223
|
+
references: resolved.references,
|
|
224
|
+
roots: read.rooted.liveUnderReachability,
|
|
225
|
+
testFiles: resolved.testFilePaths,
|
|
226
|
+
};
|
|
227
|
+
});
|
|
228
|
+
const matrix = matrixOf(configured);
|
|
229
|
+
return { matrix, sweep: sweepMatrix(matrix, input) };
|
|
230
|
+
}
|