@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.10
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/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +245 -309
- package/dist/src/augmentations.js +124 -18
- package/dist/src/automatic-types.js +170 -0
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +9 -9
- package/dist/src/config.js +6 -81
- package/dist/src/configuration-files.js +78 -188
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +146 -1
- package/dist/src/conventions.js +136 -70
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +280 -62
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +9 -18
- package/dist/src/entry-points.js +106 -21
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +225 -63
- package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
- package/dist/src/findings/emitters.js +6 -6
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +53 -19
- package/dist/src/findings/non-code-artifacts.js +71 -39
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +57 -28
- package/dist/src/findings/visibility-narrowing.js +40 -18
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +18 -4
- package/dist/src/git-ignore.js +99 -0
- package/dist/src/graph.js +9 -4
- package/dist/src/html-entries.js +6 -33
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +341 -69
- package/dist/src/inventory.js +54 -29
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +88 -61
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +94 -13
- package/dist/src/mdx-entries.js +174 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +41 -10
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -78
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +11 -0
- package/dist/src/references.js +52 -31
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +59 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -727
- package/dist/src/roots.js +374 -116
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -342
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +649 -29
- package/dist/src/session.js +41 -29
- package/dist/src/source-files.js +40 -15
- package/dist/src/suppress-file.js +34 -29
- package/dist/src/suppress-inline.js +21 -4
- package/dist/src/suppress.js +40 -56
- package/dist/src/sweep.js +32 -17
- package/dist/src/symbol-keys.js +105 -6
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +119 -18
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +27 -221
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +181 -125
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +14 -9
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +128 -33
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +333 -457
- package/src/augmentations.ts +152 -23
- package/src/automatic-types.ts +228 -0
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +9 -10
- package/src/config.ts +31 -299
- package/src/configuration-files.ts +100 -191
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +2 -2
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +186 -1
- package/src/conventions.ts +166 -90
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +356 -78
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +11 -20
- package/src/entry-points.ts +124 -28
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +24 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +297 -77
- package/src/findings/dependencies-and-module-machinery.ts +90 -31
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +8 -8
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +64 -20
- package/src/findings/non-code-artifacts.ts +88 -50
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +65 -28
- package/src/findings/visibility-narrowing.ts +51 -19
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +23 -4
- package/src/git-ignore.ts +122 -0
- package/src/graph.ts +11 -3
- package/src/host.ts +17 -14
- package/src/html-entries.ts +6 -33
- package/src/import-bindings.ts +128 -0
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +152 -30
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +449 -85
- package/src/inventory.ts +73 -27
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +127 -80
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +154 -18
- package/src/mdx-entries.ts +218 -0
- package/src/module-declarations.ts +11 -13
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +51 -12
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -91
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +13 -0
- package/src/references.ts +62 -32
- package/src/report-schema.ts +68 -0
- package/src/report.ts +105 -215
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1111
- package/src/roots.ts +490 -137
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -387
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +818 -26
- package/src/session.ts +77 -54
- package/src/source-files.ts +67 -17
- package/src/suppress-file.ts +36 -34
- package/src/suppress-inline.ts +20 -3
- package/src/suppress.ts +52 -63
- package/src/sweep.ts +53 -18
- package/src/symbol-keys.ts +131 -7
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +155 -18
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +35 -273
- package/src/type-parameter-reads.ts +227 -0
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +245 -161
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +15 -13
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +149 -34
- package/dist/src/discover.js +0 -347
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/discover.ts +0 -510
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
|
@@ -4,21 +4,38 @@
|
|
|
4
4
|
* together with every finding that falls with a root they withhold.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
CODE,
|
|
9
|
+
FAMILY_KEY_LENGTH,
|
|
10
|
+
type Confidence,
|
|
11
|
+
type Severity,
|
|
12
|
+
KIND_ROWS,
|
|
13
|
+
type KindRow,
|
|
14
|
+
} from "../contract/contract.generated.ts";
|
|
7
15
|
import type { RunSweep } from "../analysis.ts";
|
|
8
16
|
import { componentId, type Component } from "../components.ts";
|
|
9
|
-
import
|
|
17
|
+
import { deadComponents } from "../matrix.ts";
|
|
18
|
+
import type { Cascade } from "../config.ts";
|
|
10
19
|
import type { CompletedFinding, Finding, FindingComponent, PositionedSymbol } from "../finding.ts";
|
|
11
20
|
import { OUTSIDE, type Graph } from "../graph.ts";
|
|
12
21
|
import type { SymbolKind } from "../inventory.ts";
|
|
13
|
-
import { KINDS
|
|
22
|
+
import { KINDS } from "../kinds.ts";
|
|
14
23
|
import { byPosition, positionKey } from "../position.ts";
|
|
15
24
|
import { declarationKey } from "../ref.ts";
|
|
16
|
-
import { FAMILY_KEY_LENGTH } from "../resolve.ts";
|
|
17
25
|
import { isPartSubject, isRowSubject, type Dials } from "../suppress.ts";
|
|
26
|
+
import type { Candidate } from "../sweep.ts";
|
|
18
27
|
import type { Boundary } from "./boundary.ts";
|
|
19
28
|
import type { EmitterInput } from "./emitter.ts";
|
|
20
29
|
|
|
21
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* The codes whose subject the run that counts test references holds live, so a test
|
|
32
|
+
* file's candidate reported under one carries none of a dead declaration's sweep facts.
|
|
33
|
+
*/
|
|
34
|
+
const LIVE_SUBJECT: ReadonlySet<string> = new Set(
|
|
35
|
+
KIND_ROWS.filter((row) => row.holdsLive).map((row) => row.code),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
/** The fixability grammar/analysis/fixability.md gives a finding about a held declaration. */
|
|
22
39
|
const MANUAL = "manual";
|
|
23
40
|
|
|
24
41
|
/** The member kinds an object property can declare, which an `in` expression tests by name. */
|
|
@@ -48,10 +65,9 @@ function everyDeclaredLoaded(boundary: Boundary): boolean {
|
|
|
48
65
|
* naming its family, else the code's default.
|
|
49
66
|
*/
|
|
50
67
|
function severityOf(input: EmitterInput, code: string, fallback: Severity): Severity {
|
|
51
|
-
const {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
);
|
|
68
|
+
const { severity } = input.config;
|
|
69
|
+
const key = [code, code.slice(0, FAMILY_KEY_LENGTH)].find((one) => Object.hasOwn(severity, one));
|
|
70
|
+
return key === undefined ? fallback : (severity[key] ?? fallback);
|
|
55
71
|
}
|
|
56
72
|
|
|
57
73
|
/**
|
|
@@ -94,17 +110,13 @@ function testedByIn(input: EmitterInput): ReadonlySet<string> {
|
|
|
94
110
|
* published API, a public member of a published type included, is `certain` where the
|
|
95
111
|
* run loaded every consumer the scope declared and declared one, `probable` where one did
|
|
96
112
|
* not load, and `possible` where it declared none, whatever the configuration says of the
|
|
97
|
-
* consumer set.
|
|
98
|
-
*
|
|
113
|
+
* consumer set. A member an `in` expression tests by name, and one a function outside the
|
|
114
|
+
* analysis fills, is `possible`.
|
|
99
115
|
*/
|
|
100
116
|
function reachabilityClasses(input: EmitterInput): (id: string) => Confidence {
|
|
101
117
|
const tested = testedByIn(input);
|
|
118
|
+
const filled = input.boundary.filledOutside ?? new Set<string>();
|
|
102
119
|
const published = publishedIds(input.swept.matrix.union);
|
|
103
|
-
const usedByTests = new Set(
|
|
104
|
-
input.swept.sweep.candidates
|
|
105
|
-
.filter((candidate) => candidate.heldByTests !== undefined)
|
|
106
|
-
.map((candidate) => candidate.id),
|
|
107
|
-
);
|
|
108
120
|
const { declared } = input.boundary.consumers;
|
|
109
121
|
const outside: Confidence =
|
|
110
122
|
declared.length === 0
|
|
@@ -113,13 +125,41 @@ function reachabilityClasses(input: EmitterInput): (id: string) => Confidence {
|
|
|
113
125
|
? "certain"
|
|
114
126
|
: "probable";
|
|
115
127
|
return (id) => {
|
|
116
|
-
if (
|
|
128
|
+
if (tested.has(id) || filled.has(id)) {
|
|
117
129
|
return "possible";
|
|
118
130
|
}
|
|
119
131
|
return published.has(id) ? outside : "certain";
|
|
120
132
|
};
|
|
121
133
|
}
|
|
122
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Whether one test of dead code references a target symbol that is published API of a run
|
|
137
|
+
* that declares no consumer, which outside programs the run cannot see may use, so the
|
|
138
|
+
* target counts as live for the test and the test is no test of dead code.
|
|
139
|
+
*/
|
|
140
|
+
export function testsUnseenAPI(input: EmitterInput): (id: string) => boolean {
|
|
141
|
+
if (input.boundary.consumers.declared.length > 0) {
|
|
142
|
+
return () => false;
|
|
143
|
+
}
|
|
144
|
+
const union = input.swept.matrix.union;
|
|
145
|
+
const published = publishedIds(union);
|
|
146
|
+
return (id) => testTargets(union, id).some((target) => published.has(target));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The target symbols one test of dead code references, by id: production declarations the sweep judges. */
|
|
150
|
+
function testTargets(union: Graph, id: string): readonly string[] {
|
|
151
|
+
return (union.out[union.at(id)] ?? []).flatMap((edge) => {
|
|
152
|
+
const target = union.symbols[edge.to];
|
|
153
|
+
const testCode = union.test[edge.to] === true || union.support[edge.to] === true;
|
|
154
|
+
return target !== undefined &&
|
|
155
|
+
!testCode &&
|
|
156
|
+
union.ambient[edge.to] !== true &&
|
|
157
|
+
union.subject[edge.to] === true
|
|
158
|
+
? [target.id]
|
|
159
|
+
: [];
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
123
163
|
/**
|
|
124
164
|
* The confidence ceiling of a test of dead code: the lowest reachability class among the
|
|
125
165
|
* target symbols it references, because its deadness is theirs, whether or not they share
|
|
@@ -133,20 +173,59 @@ function testCeilings(
|
|
|
133
173
|
const tests = new Set(
|
|
134
174
|
input.swept.sweep.candidates.filter((one) => one.testOfDeadCode).map((one) => one.id),
|
|
135
175
|
);
|
|
136
|
-
return (id) =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
176
|
+
return (id) =>
|
|
177
|
+
tests.has(id)
|
|
178
|
+
? testTargets(union, id).reduce<Confidence>(
|
|
179
|
+
(lowest, target) => capped(lowest, classOf(target)),
|
|
180
|
+
"certain",
|
|
181
|
+
)
|
|
182
|
+
: "certain";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The subjects dead only because production code only tests use is: each declaration the
|
|
187
|
+
* sweep marks test-used, each `DS1602` file of such code, and each test of dead code every
|
|
188
|
+
* target of which is one. Nothing they hold references a dead declaration outside them, so
|
|
189
|
+
* the rest of a component they share is reported without them.
|
|
190
|
+
*/
|
|
191
|
+
function testUsedSubjects(input: EmitterInput): {
|
|
192
|
+
readonly declarations: ReadonlySet<string>;
|
|
193
|
+
readonly files: ReadonlySet<string>;
|
|
194
|
+
} {
|
|
195
|
+
const { matrix, sweep } = input.swept;
|
|
196
|
+
const union = matrix.union;
|
|
197
|
+
const used = new Set(sweep.candidates.filter((one) => one.testUsed).map((one) => one.id));
|
|
198
|
+
for (const test of sweep.candidates.filter((one) => one.testOfDeadCode)) {
|
|
199
|
+
const targets = testTargets(union, test.id);
|
|
200
|
+
if (targets.length > 0 && targets.every((target) => used.has(target))) {
|
|
201
|
+
used.add(test.id);
|
|
147
202
|
}
|
|
148
|
-
|
|
149
|
-
|
|
203
|
+
}
|
|
204
|
+
const files = new Set(
|
|
205
|
+
union.symbols.flatMap((symbol, at) =>
|
|
206
|
+
matrix.testUsed[at] === true ? [symbol.position.path] : [],
|
|
207
|
+
),
|
|
208
|
+
);
|
|
209
|
+
return { declarations: used, files };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The ceiling the roots the run found put on one dead subject: a subject other declarations
|
|
214
|
+
* reference, found dead only because no root reaches them, is `probable` unless the
|
|
215
|
+
* configuration declares the roots complete and no configuration holding the subject loads
|
|
216
|
+
* a module the analysis cannot resolve, because a root the analysis cannot see would make
|
|
217
|
+
* it live; every other declaration has none below `certain`.
|
|
218
|
+
*/
|
|
219
|
+
function rootsCeiling(
|
|
220
|
+
input: EmitterInput,
|
|
221
|
+
finding: Finding,
|
|
222
|
+
candidate: Candidate | undefined,
|
|
223
|
+
configurations: readonly string[],
|
|
224
|
+
): Confidence {
|
|
225
|
+
const unreachable = candidate?.relation === "reachability" || finding.unreachableOnly === true;
|
|
226
|
+
const unseen = new Set((input.files.unseenLoads ?? []).map((load) => load.configuration));
|
|
227
|
+
const complete = input.config.ts.roots_complete && !configurations.some((one) => unseen.has(one));
|
|
228
|
+
return unreachable && !complete ? "probable" : "certain";
|
|
150
229
|
}
|
|
151
230
|
|
|
152
231
|
/**
|
|
@@ -155,17 +234,37 @@ function testCeilings(
|
|
|
155
234
|
*/
|
|
156
235
|
function findingComponents(
|
|
157
236
|
swept: RunSweep,
|
|
237
|
+
components: readonly Component[],
|
|
158
238
|
cascade: Cascade,
|
|
159
239
|
): (id: string) => FindingComponent | undefined {
|
|
160
240
|
const held = new Map<string, Component>();
|
|
161
|
-
for (const component of
|
|
241
|
+
for (const component of components) {
|
|
162
242
|
for (const member of component.members) {
|
|
163
243
|
held.set(member, component);
|
|
164
244
|
}
|
|
165
245
|
}
|
|
246
|
+
const listed = membersListed(swept);
|
|
247
|
+
return (id) => {
|
|
248
|
+
const component = held.get(id);
|
|
249
|
+
return component === undefined
|
|
250
|
+
? undefined
|
|
251
|
+
: {
|
|
252
|
+
id: component.id,
|
|
253
|
+
root: component.roots.includes(id),
|
|
254
|
+
symbolCount: component.members.length,
|
|
255
|
+
deletableLines: component.deletableLines,
|
|
256
|
+
...(cascade === "full" ? { members: listed(component.members) } : {}),
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** The members of a component as the run lists them, positioned and in the canonical order. */
|
|
262
|
+
function membersListed(
|
|
263
|
+
swept: RunSweep,
|
|
264
|
+
): (members: readonly string[]) => readonly PositionedSymbol[] {
|
|
166
265
|
const symbols = new Map(swept.matrix.union.symbols.map((symbol) => [symbol.id, symbol]));
|
|
167
|
-
|
|
168
|
-
|
|
266
|
+
return (members) =>
|
|
267
|
+
members
|
|
169
268
|
.flatMap((member) => {
|
|
170
269
|
const symbol = symbols.get(member);
|
|
171
270
|
return symbol === undefined
|
|
@@ -182,17 +281,76 @@ function findingComponents(
|
|
|
182
281
|
];
|
|
183
282
|
})
|
|
184
283
|
.sort((a, b) => byPosition(a.position, b.position));
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* The run's dead components with each file a `DS1602` finding reports dead among them, so
|
|
288
|
+
* the file's declarations fall with it, and with the components each subject that is no
|
|
289
|
+
* declaration falls with joined into one.
|
|
290
|
+
*/
|
|
291
|
+
function runComponents(
|
|
292
|
+
swept: RunSweep,
|
|
293
|
+
findings: readonly Finding[],
|
|
294
|
+
): {
|
|
295
|
+
readonly components: readonly Component[];
|
|
296
|
+
readonly computed: number;
|
|
297
|
+
readonly files: ReadonlyMap<string, string>;
|
|
298
|
+
} {
|
|
299
|
+
const union = swept.matrix.union;
|
|
300
|
+
const reported = new Set(
|
|
301
|
+
findings.filter((one) => one.code === CODE.fileNeverImported).map((one) => one.position.path),
|
|
302
|
+
);
|
|
303
|
+
const files = new Map(
|
|
304
|
+
union.symbols.flatMap((symbol, at) =>
|
|
305
|
+
symbol.kind === "file" && reported.has(symbol.position.path) ? [[at, symbol] as const] : [],
|
|
306
|
+
),
|
|
307
|
+
);
|
|
308
|
+
const computed =
|
|
309
|
+
files.size === 0
|
|
310
|
+
? swept.sweep.components
|
|
311
|
+
: deadComponents(swept.matrix, swept.sweep, new Set(files.keys()));
|
|
312
|
+
const place = new Map<string, number>();
|
|
313
|
+
computed.forEach((component, at) => {
|
|
314
|
+
for (const member of component.members) {
|
|
315
|
+
place.set(member, at);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
const leader = computed.map((_component, at) => at);
|
|
319
|
+
const find = (at: number): number => {
|
|
320
|
+
let top = at;
|
|
321
|
+
while ((leader[top] ?? top) !== top) {
|
|
322
|
+
top = leader[top] ?? top;
|
|
323
|
+
}
|
|
324
|
+
return top;
|
|
325
|
+
};
|
|
326
|
+
for (const { fallsWith } of findings) {
|
|
327
|
+
const joined = (fallsWith ?? []).flatMap((id) => place.get(id) ?? []).map(find);
|
|
328
|
+
const first = Math.min(...joined);
|
|
329
|
+
for (const one of joined) {
|
|
330
|
+
leader[one] = first;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const merged = new Map<number, Component>();
|
|
334
|
+
computed.forEach((component, at) => {
|
|
335
|
+
const top = find(at);
|
|
336
|
+
const into = merged.get(top);
|
|
337
|
+
merged.set(
|
|
338
|
+
top,
|
|
339
|
+
into === undefined
|
|
340
|
+
? component
|
|
341
|
+
: {
|
|
342
|
+
id: into.id,
|
|
343
|
+
members: [...into.members, ...component.members],
|
|
344
|
+
roots: [...into.roots, ...component.roots],
|
|
345
|
+
deletableLines: into.deletableLines + component.deletableLines,
|
|
346
|
+
held: into.held && component.held,
|
|
347
|
+
},
|
|
348
|
+
);
|
|
349
|
+
});
|
|
350
|
+
return {
|
|
351
|
+
components: [...merged.values()],
|
|
352
|
+
computed: computed.length,
|
|
353
|
+
files: new Map([...files.values()].map((symbol) => [symbol.position.path, symbol.id])),
|
|
196
354
|
};
|
|
197
355
|
}
|
|
198
356
|
|
|
@@ -217,18 +375,20 @@ export function completed(
|
|
|
217
375
|
const candidates = new Map(swept.sweep.candidates.map((candidate) => [candidate.id, candidate]));
|
|
218
376
|
const classOf = reachabilityClasses(input);
|
|
219
377
|
const ceilingOf = testCeilings(input, classOf);
|
|
220
|
-
const
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
378
|
+
const testUsed = testUsedSubjects(input);
|
|
379
|
+
const { components, computed, files } = runComponents(swept, findings);
|
|
380
|
+
const componentOf = findingComponents(swept, components, input.config.reporters.cascade);
|
|
381
|
+
const held = new Set(components.filter((one) => one.held).flatMap((one) => one.members));
|
|
382
|
+
// A join keeps its first component's identifier, so a minted one counts past every computed one.
|
|
383
|
+
let minted = computed;
|
|
384
|
+
const dead = new Set(components.map((component) => component.id));
|
|
385
|
+
const fallingWith = new Map<string, string[]>();
|
|
386
|
+
for (const { id, heldByTests } of swept.sweep.candidates) {
|
|
228
387
|
if (heldByTests !== undefined) {
|
|
229
|
-
fallingWith.set(heldByTests, (fallingWith.get(heldByTests) ??
|
|
388
|
+
fallingWith.set(heldByTests, [...(fallingWith.get(heldByTests) ?? []), id]);
|
|
230
389
|
}
|
|
231
390
|
}
|
|
391
|
+
const listed = membersListed(swept);
|
|
232
392
|
|
|
233
393
|
const done = findings.map((finding): CompletedFinding => {
|
|
234
394
|
const row = kinds.get(finding.code);
|
|
@@ -239,18 +399,33 @@ export function completed(
|
|
|
239
399
|
// a row is still never the declaration. Two declarations of one file can share a
|
|
240
400
|
// reference, so the one the finding is positioned at is the subject where there is one.
|
|
241
401
|
const declared = union.at(positionKey(finding.position));
|
|
402
|
+
const ref = finding.symbol.holder ?? finding.symbol.ref;
|
|
242
403
|
const at = isRowSubject(finding.symbol.kind)
|
|
243
404
|
? OUTSIDE
|
|
244
|
-
: union.symbols[declared]?.ref ===
|
|
405
|
+
: union.symbols[declared]?.ref === ref
|
|
245
406
|
? declared
|
|
246
|
-
: (positions.get(declarationKey(
|
|
407
|
+
: (positions.get(declarationKey(ref, finding.position.path)) ?? OUTSIDE);
|
|
247
408
|
// A part names the declaration that holds it, and is decided inside it rather than by
|
|
248
409
|
// the declaration's liveness, so it carries none of the declaration's sweep facts.
|
|
249
410
|
const part = isPartSubject(finding.symbol.kind);
|
|
250
411
|
const symbol = union.symbols[at];
|
|
251
412
|
const judged = !part && union.subject[at] === true ? symbol : undefined;
|
|
252
|
-
const candidate =
|
|
253
|
-
|
|
413
|
+
const candidate =
|
|
414
|
+
judged === undefined || LIVE_SUBJECT.has(finding.code)
|
|
415
|
+
? undefined
|
|
416
|
+
: candidates.get(judged.id);
|
|
417
|
+
const fallsWith = finding.fallsWith?.[0];
|
|
418
|
+
const joined = fallsWith === undefined ? undefined : componentOf(fallsWith);
|
|
419
|
+
const file =
|
|
420
|
+
finding.code === CODE.fileNeverImported ? files.get(finding.position.path) : undefined;
|
|
421
|
+
const found =
|
|
422
|
+
joined !== undefined
|
|
423
|
+
? { ...joined, root: false }
|
|
424
|
+
: file !== undefined
|
|
425
|
+
? componentOf(file)
|
|
426
|
+
: symbol === undefined || part
|
|
427
|
+
? undefined
|
|
428
|
+
: componentOf(symbol.id);
|
|
254
429
|
// A test file's declaration is a component member only as a test of dead code, and
|
|
255
430
|
// test code keeps the test-support code it references out of every component.
|
|
256
431
|
if (
|
|
@@ -264,19 +439,50 @@ export function completed(
|
|
|
264
439
|
let component = found;
|
|
265
440
|
if (component === undefined) {
|
|
266
441
|
minted += 1;
|
|
442
|
+
const falling = symbol?.kind === "interface" ? fallingWith.get(symbol.id) : undefined;
|
|
267
443
|
component = {
|
|
268
444
|
id: componentId(minted),
|
|
269
445
|
root: true,
|
|
270
|
-
symbolCount:
|
|
446
|
+
symbolCount: falling?.length ?? 1,
|
|
271
447
|
deletableLines: 0,
|
|
448
|
+
...(input.config.reporters.cascade !== "full"
|
|
449
|
+
? {}
|
|
450
|
+
: falling === undefined
|
|
451
|
+
? {
|
|
452
|
+
members: [
|
|
453
|
+
{
|
|
454
|
+
ref: finding.symbol.ref,
|
|
455
|
+
name: finding.symbol.name,
|
|
456
|
+
position: finding.position,
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
}
|
|
460
|
+
: { members: listed(falling) }),
|
|
272
461
|
};
|
|
273
462
|
}
|
|
274
463
|
const counts =
|
|
275
464
|
candidate === undefined
|
|
276
465
|
? union.made[at]
|
|
277
466
|
: { production: candidate.productionRefs, test: candidate.testRefs };
|
|
278
|
-
const
|
|
467
|
+
const ownClass =
|
|
279
468
|
judged === undefined ? (finding.reachabilityClass ?? "certain") : classOf(judged.id);
|
|
469
|
+
const used =
|
|
470
|
+
judged !== undefined
|
|
471
|
+
? candidate !== undefined && testUsed.declarations.has(judged.id)
|
|
472
|
+
: finding.code === CODE.fileNeverImported && testUsed.files.has(finding.position.path);
|
|
473
|
+
const reachabilityClass = used && candidate?.testOfDeadCode !== true ? "possible" : ownClass;
|
|
474
|
+
const configurations =
|
|
475
|
+
candidate?.configurations ??
|
|
476
|
+
(judged === undefined && !part
|
|
477
|
+
? swept.matrix.configurations
|
|
478
|
+
: (swept.matrix.heldIn[at] ?? swept.matrix.configurations));
|
|
479
|
+
const untested = capped(
|
|
480
|
+
capped(
|
|
481
|
+
capped(ownClass, row.maxClass),
|
|
482
|
+
rootsCeiling(input, finding, candidate, configurations),
|
|
483
|
+
),
|
|
484
|
+
judged === undefined ? "certain" : ceilingOf(judged.id),
|
|
485
|
+
);
|
|
280
486
|
const generated = input.files.generated?.has(finding.position.path) === true;
|
|
281
487
|
const fixability = generated
|
|
282
488
|
? "none"
|
|
@@ -287,9 +493,13 @@ export function completed(
|
|
|
287
493
|
? MANUAL
|
|
288
494
|
: row.fixability;
|
|
289
495
|
const lastUses =
|
|
290
|
-
fixability
|
|
291
|
-
?
|
|
292
|
-
: undefined
|
|
496
|
+
fixability !== "deletable"
|
|
497
|
+
? undefined
|
|
498
|
+
: candidate !== undefined
|
|
499
|
+
? input.dependencies.lastUses.get(candidate.id)
|
|
500
|
+
: finding.code === CODE.fileNeverImported
|
|
501
|
+
? input.dependencies.fileLastUses.get(finding.position.path)
|
|
502
|
+
: undefined;
|
|
293
503
|
return {
|
|
294
504
|
code: finding.code,
|
|
295
505
|
kind: row.name,
|
|
@@ -297,20 +507,14 @@ export function completed(
|
|
|
297
507
|
position: finding.position,
|
|
298
508
|
symbol: finding.symbol,
|
|
299
509
|
reachabilityClass,
|
|
300
|
-
confidence:
|
|
301
|
-
|
|
302
|
-
judged === undefined ? "certain" : ceilingOf(judged.id),
|
|
303
|
-
),
|
|
510
|
+
confidence: used ? "possible" : untested,
|
|
511
|
+
...(used ? { componentConfidence: untested } : {}),
|
|
304
512
|
...(candidate === undefined ? {} : { livenessRelation: candidate.relation }),
|
|
305
513
|
testOnly: judged !== undefined && counts?.production === 0 && counts.test > 0,
|
|
306
514
|
generated,
|
|
307
515
|
component,
|
|
308
516
|
retainedBy: [],
|
|
309
|
-
configurations
|
|
310
|
-
candidate?.configurations ??
|
|
311
|
-
(judged === undefined && !part
|
|
312
|
-
? swept.matrix.configurations
|
|
313
|
-
: (swept.matrix.heldIn[at] ?? swept.matrix.configurations)),
|
|
517
|
+
configurations,
|
|
314
518
|
consumersLoaded: input.boundary.consumers.loaded,
|
|
315
519
|
fixability,
|
|
316
520
|
severity: severityOf(input, finding.code, row.defaultSeverity),
|
|
@@ -327,7 +531,7 @@ export function completed(
|
|
|
327
531
|
/**
|
|
328
532
|
* The findings with every finding of a dead component capped by the lowest confidence
|
|
329
533
|
* among the component's root members, so the minimum confidence reports or withholds a
|
|
330
|
-
* component whole.
|
|
534
|
+
* component whole but for the findings test-used code lowers on their own.
|
|
331
535
|
*/
|
|
332
536
|
function componentCapped(
|
|
333
537
|
findings: readonly CompletedFinding[],
|
|
@@ -338,17 +542,29 @@ function componentCapped(
|
|
|
338
542
|
const { id, root } = finding.component;
|
|
339
543
|
if (root && dead.has(id)) {
|
|
340
544
|
const held = lowest.get(id);
|
|
341
|
-
|
|
545
|
+
const own = componentConfidenceOf(finding);
|
|
546
|
+
lowest.set(id, held === undefined ? own : capped(held, own));
|
|
342
547
|
}
|
|
343
548
|
}
|
|
344
549
|
return findings.map((finding) => {
|
|
345
550
|
const cap = lowest.get(finding.component.id);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
551
|
+
if (cap === undefined) {
|
|
552
|
+
return finding;
|
|
553
|
+
}
|
|
554
|
+
const own = finding.componentConfidence;
|
|
555
|
+
return {
|
|
556
|
+
...finding,
|
|
557
|
+
confidence: capped(finding.confidence, cap),
|
|
558
|
+
...(own === undefined ? {} : { componentConfidence: capped(own, cap) }),
|
|
559
|
+
};
|
|
349
560
|
});
|
|
350
561
|
}
|
|
351
562
|
|
|
563
|
+
/** The confidence a finding gives its component's cap and the dials. */
|
|
564
|
+
function componentConfidenceOf(finding: CompletedFinding): Confidence {
|
|
565
|
+
return finding.componentConfidence ?? finding.confidence;
|
|
566
|
+
}
|
|
567
|
+
|
|
352
568
|
/** Whether the dials withhold a finding by its own severity or confidence. */
|
|
353
569
|
function dialed(
|
|
354
570
|
finding: Pick<CompletedFinding, "severity" | "confidence">,
|
|
@@ -368,10 +584,14 @@ export function dialsOf(
|
|
|
368
584
|
input: EmitterInput,
|
|
369
585
|
findings: readonly CompletedFinding[],
|
|
370
586
|
): Dials<CompletedFinding> {
|
|
371
|
-
const minConfidence = input.config.analysis.
|
|
587
|
+
const minConfidence = input.config.analysis.min_confidence;
|
|
372
588
|
const components = new Set(
|
|
373
589
|
findings
|
|
374
|
-
.filter(
|
|
590
|
+
.filter(
|
|
591
|
+
(finding) =>
|
|
592
|
+
finding.component.root &&
|
|
593
|
+
dialed({ ...finding, confidence: componentConfidenceOf(finding) }, minConfidence),
|
|
594
|
+
)
|
|
375
595
|
.map((finding) => finding.component.id),
|
|
376
596
|
);
|
|
377
597
|
return {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import { declarationKey, type Dependencies } from "../dependencies.ts";
|
|
2
3
|
import type { Finding } from "../finding.ts";
|
|
3
4
|
import { renderRef, type DependencySection } from "../ref.ts";
|
|
4
|
-
import type { Emitter } from "./emitter.ts";
|
|
5
|
-
|
|
6
|
-
const UNUSED_DEPENDENCY = "DS1601";
|
|
5
|
+
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
6
|
+
import { nonCodeArtifacts } from "./non-code-artifacts.ts";
|
|
7
7
|
|
|
8
8
|
const MESSAGES: Readonly<Record<DependencySection, string>> = {
|
|
9
9
|
dependency: "no import in the target needs the declared dependency",
|
|
@@ -11,38 +11,97 @@ const MESSAGES: Readonly<Record<DependencySection, string>> = {
|
|
|
11
11
|
"peer-dependency": "no import in the target needs the declared peer dependency",
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
const DEAD_USERS_MESSAGES: Readonly<Record<DependencySection, string>> = {
|
|
15
|
+
dependency: "only code that is itself dead uses the declared dependency",
|
|
16
|
+
"dev-dependency": "only code that is itself dead uses the declared development dependency",
|
|
17
|
+
"peer-dependency": "only code that is itself dead uses the declared peer dependency",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** How the run judged the declarations and files a dependency's uses may sit in. */
|
|
21
|
+
interface DeadCode {
|
|
22
|
+
/** The members of the dead components, and the top level of each file reported unimported. */
|
|
23
|
+
readonly dead: ReadonlySet<string>;
|
|
24
|
+
/** Those found dead under reachability alone. */
|
|
25
|
+
readonly reachedOnly: ReadonlySet<string>;
|
|
26
|
+
/** The declarations of a library's published API, whose findings sit at the consumers' class. */
|
|
27
|
+
readonly published: ReadonlySet<string>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The run's dead code: test code keeps the code it references out of every dead component
|
|
32
|
+
* unless that code is dead as well, and a file's top level is dead where the file is.
|
|
33
|
+
*/
|
|
34
|
+
function deadCode(input: EmitterInput): DeadCode {
|
|
35
|
+
const { swept } = input;
|
|
36
|
+
const union = swept.matrix.union;
|
|
37
|
+
const files = new Map(
|
|
38
|
+
union.symbols.filter((one) => one.kind === "file").map((one) => [one.position.path, one.id]),
|
|
39
|
+
);
|
|
40
|
+
const dead = new Set(swept.sweep.components.flatMap((component) => component.members));
|
|
41
|
+
const reachedOnly = new Set(
|
|
42
|
+
swept.sweep.candidates.filter((one) => one.relation === "reachability").map((one) => one.id),
|
|
43
|
+
);
|
|
44
|
+
for (const finding of nonCodeArtifacts(input)) {
|
|
45
|
+
const id = files.get(finding.position.path);
|
|
46
|
+
if (finding.code === CODE.fileNeverImported && id !== undefined) {
|
|
47
|
+
dead.add(id);
|
|
48
|
+
if (finding.unreachableOnly === true) {
|
|
49
|
+
reachedOnly.add(id);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const published = new Set(
|
|
54
|
+
union.rooted
|
|
55
|
+
.filter((root) => root.kind === "published-api")
|
|
56
|
+
.flatMap((root) => union.symbols[root.at]?.id ?? []),
|
|
57
|
+
);
|
|
58
|
+
return { dead, reachedOnly, published };
|
|
59
|
+
}
|
|
60
|
+
|
|
14
61
|
/**
|
|
15
62
|
* A dependency, development dependency or peer dependency of a manifest the run reads
|
|
16
|
-
* that the target does not need, at the position of its key
|
|
63
|
+
* that the target does not need, at the position of its key, and one whose every use is
|
|
64
|
+
* held by dead code, which is dead only through that code and falls with its component:
|
|
65
|
+
* at `possible` where one of its holders is published, and found under reachability alone
|
|
66
|
+
* where one of them is.
|
|
17
67
|
*/
|
|
18
|
-
function unusedDependencies(dependencies: Dependencies): Finding[] {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
68
|
+
function unusedDependencies(input: EmitterInput, dependencies: Dependencies): Finding[] {
|
|
69
|
+
const { dead, reachedOnly, published } = deadCode(input);
|
|
70
|
+
return dependencies.declared.flatMap((dependency) => {
|
|
71
|
+
const key = declarationKey(dependency);
|
|
72
|
+
const held = dependencies.heldBy.get(key);
|
|
73
|
+
const users = held?.every((id) => dead.has(id)) === true ? held : undefined;
|
|
74
|
+
if (dependencies.ran.has(key) || (dependencies.needed.has(key) && users === undefined)) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const unbuilt = dependencies.unbuilt.has(dependency.position.path);
|
|
78
|
+
const atPossible = unbuilt || users?.some((id) => published.has(id)) === true;
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
code: CODE.unusedDependency,
|
|
82
|
+
position: dependency.position,
|
|
83
|
+
symbol: {
|
|
84
|
+
ref: renderRef(dependency.manifest, {
|
|
85
|
+
of: "dependency",
|
|
86
|
+
name: dependency.name,
|
|
87
|
+
section: dependency.section,
|
|
88
|
+
}),
|
|
89
|
+
kind: "dependency",
|
|
31
90
|
name: dependency.name,
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
91
|
+
sizeLines: 1,
|
|
92
|
+
},
|
|
93
|
+
message: (users === undefined ? MESSAGES : DEAD_USERS_MESSAGES)[dependency.section],
|
|
94
|
+
details: { dependencyClass: dependency.section },
|
|
95
|
+
...(atPossible ? { reachabilityClass: "possible" as const } : {}),
|
|
96
|
+
...(users?.some((id) => reachedOnly.has(id)) === true
|
|
97
|
+
? { unreachableOnly: true as const }
|
|
98
|
+
: {}),
|
|
99
|
+
...(users === undefined ? {} : { fallsWith: users }),
|
|
37
100
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
...(dependencies.unbuilt.has(dependency.position.path)
|
|
41
|
-
? { reachabilityClass: "possible" as const }
|
|
42
|
-
: {}),
|
|
43
|
-
}));
|
|
101
|
+
];
|
|
102
|
+
});
|
|
44
103
|
}
|
|
45
104
|
|
|
46
|
-
/** The findings of the dependencies
|
|
47
|
-
export const dependenciesAndModuleMachinery: Emitter = (
|
|
48
|
-
unusedDependencies(dependencies);
|
|
105
|
+
/** The findings of the dependencies family, `DS1700` to `DS1799`. */
|
|
106
|
+
export const dependenciesAndModuleMachinery: Emitter = (input) =>
|
|
107
|
+
unusedDependencies(input, input.dependencies);
|