@cplieger/deadset-ts 6.1.1-dev.1 → 7.0.0-dev.2
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/dist/src/analysis.js +45 -32
- package/dist/src/component-layer.js +1 -1
- package/dist/src/configuration-files.js +227 -27
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/contextual-uses.js +68 -10
- package/dist/src/convention-rows.js +48 -0
- package/dist/src/conventions.js +80 -0
- package/dist/src/dependencies.js +121 -46
- package/dist/src/discover.js +1 -1
- package/dist/src/edges.js +1 -1
- package/dist/src/exempt-classes.js +9 -22
- package/dist/src/exempt.js +3 -15
- package/dist/src/findings/completion.js +41 -11
- package/dist/src/findings/dependencies-and-module-machinery.js +6 -26
- package/dist/src/findings/emitters.js +0 -4
- package/dist/src/findings/self-check.js +14 -10
- package/dist/src/html-entries.js +29 -14
- package/dist/src/indexed-access.js +377 -81
- package/dist/src/intra-function-parts.js +57 -11
- package/dist/src/inventory.js +3 -12
- package/dist/src/kinds.js +6 -6
- package/dist/src/references.js +62 -23
- package/dist/src/report.js +3 -0
- package/dist/src/roots.js +96 -7
- package/dist/src/run.js +0 -2
- package/dist/src/schema.js +1 -1
- package/dist/src/scope.js +2 -2
- package/dist/src/session.js +6 -2
- package/dist/src/source-files.js +1 -0
- package/dist/src/suppress-inline.js +1 -1
- package/dist/src/suppress.js +15 -16
- package/dist/src/sweep.js +29 -17
- package/dist/src/symbol-keys.js +114 -0
- package/dist/src/test-joins.js +101 -0
- package/dist/src/version.js +1 -1
- package/dist/src/workflow-steps.js +21 -15
- package/dist/src/workspace-programs.js +1 -4
- package/dist/src/workspace-resolution.js +0 -2
- package/dist/src/workspace.js +0 -3
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/analysis.ts +55 -47
- package/src/calls.ts +3 -3
- package/src/component-files.ts +7 -7
- package/src/component-layer.ts +2 -2
- package/src/components.ts +1 -1
- package/src/config.ts +4 -4
- package/src/configuration-files.ts +246 -28
- package/src/conformance-record.ts +2 -2
- package/src/contextual-uses.ts +99 -13
- package/src/convention-rows.ts +76 -0
- package/src/conventions.ts +102 -0
- package/src/dependencies.ts +148 -56
- package/src/discover.ts +3 -3
- package/src/edges.ts +3 -3
- package/src/entry-points.ts +1 -1
- package/src/exempt-classes.ts +10 -24
- package/src/exempt.ts +5 -29
- package/src/file-facts.ts +1 -1
- package/src/finding.ts +5 -1
- package/src/findings/completion.ts +53 -11
- package/src/findings/dependencies-and-module-machinery.ts +9 -28
- package/src/findings/emitters.ts +3 -8
- package/src/findings/self-check.ts +17 -14
- package/src/findings-pass.ts +2 -2
- package/src/graph.ts +2 -2
- package/src/html-entries.ts +34 -16
- package/src/indexed-access.ts +476 -102
- package/src/intra-function-parts.ts +84 -12
- package/src/inventory.ts +5 -33
- package/src/json-document.ts +1 -1
- package/src/kinds.ts +7 -6
- package/src/manifest.ts +1 -1
- package/src/namespace-objects.ts +1 -1
- package/src/pnpm-workspace.ts +1 -1
- package/src/references.ts +93 -33
- package/src/report.ts +8 -4
- package/src/roots.ts +129 -9
- package/src/run.ts +0 -2
- package/src/schema.ts +2 -2
- package/src/scope.ts +2 -2
- package/src/session.ts +7 -3
- package/src/source-files.ts +3 -0
- package/src/stores.ts +1 -1
- package/src/suppress-inline.ts +3 -3
- package/src/suppress.ts +21 -24
- package/src/sweep.ts +29 -17
- package/src/symbol-keys.ts +172 -0
- package/src/template-field.ts +1 -1
- package/src/test-joins.ts +120 -0
- package/src/value-reach.ts +1 -1
- package/src/verbs/verb.ts +1 -4
- package/src/version.ts +1 -1
- package/src/workflow-steps.ts +23 -16
- package/src/workspace-programs.ts +3 -10
- package/src/workspace-resolution.ts +0 -3
- package/src/workspace-run.ts +1 -1
- package/src/workspace.ts +1 -9
|
@@ -1,72 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The exemption classes of the Contract's vocabulary, in its order: each class's name,
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* reason the fixed severity codes are, and a test holds them equal to the Contract
|
|
8
|
-
* release this analyzer implements. A class name is a type, so a detector table or a
|
|
9
|
-
* record naming a class the vocabulary does not hold, or one that runs on another
|
|
10
|
-
* language only, does not compile.
|
|
2
|
+
* The exemption classes of the Contract's vocabulary, in its order: each class's name, its
|
|
3
|
+
* languages and, for a TypeScript class, which private members it may retain. The rows are
|
|
4
|
+
* written out, as the fixed severity codes are, and a test holds them equal to the Contract.
|
|
5
|
+
* A class name is a type, so a table or a record naming a class outside the vocabulary, or
|
|
6
|
+
* one of another language only, does not compile.
|
|
11
7
|
*/
|
|
12
8
|
/** Every class of the vocabulary, in the order the Contract lists them. */
|
|
13
9
|
export const EXEMPTION_CLASSES = [
|
|
14
10
|
{
|
|
15
11
|
class: "interface-satisfaction",
|
|
16
12
|
languages: ["go", "ts"],
|
|
17
|
-
confidence: "certain",
|
|
18
13
|
typescriptVisibility: { private: false, privateName: false },
|
|
19
14
|
},
|
|
20
|
-
{ class: "encoding-reflection", languages: ["go"]
|
|
21
|
-
{ class: "format-verb-contract", languages: ["go"]
|
|
22
|
-
{ class: "errors-duck-typing", languages: ["go"]
|
|
15
|
+
{ class: "encoding-reflection", languages: ["go"] },
|
|
16
|
+
{ class: "format-verb-contract", languages: ["go"] },
|
|
17
|
+
{ class: "errors-duck-typing", languages: ["go"] },
|
|
23
18
|
{
|
|
24
19
|
class: "enum-group",
|
|
25
20
|
languages: ["go", "ts"],
|
|
26
|
-
confidence: "certain",
|
|
27
21
|
typescriptVisibility: { private: false, privateName: false },
|
|
28
22
|
},
|
|
29
23
|
{
|
|
30
24
|
class: "generated-file",
|
|
31
25
|
languages: ["go", "ts"],
|
|
32
|
-
confidence: "certain",
|
|
33
26
|
typescriptVisibility: { private: true, privateName: false },
|
|
34
27
|
},
|
|
35
|
-
{ class: "linkname-cgo-asm-plugin", languages: ["go"]
|
|
28
|
+
{ class: "linkname-cgo-asm-plugin", languages: ["go"] },
|
|
36
29
|
{
|
|
37
30
|
class: "template-field",
|
|
38
31
|
languages: ["go", "ts"],
|
|
39
|
-
confidence: "possible",
|
|
40
32
|
typescriptVisibility: { private: true, privateName: false },
|
|
41
33
|
},
|
|
42
34
|
{
|
|
43
35
|
class: "reflective-lookup",
|
|
44
36
|
languages: ["go", "ts"],
|
|
45
|
-
confidence: "possible",
|
|
46
37
|
typescriptVisibility: { private: true, privateName: false },
|
|
47
38
|
},
|
|
48
39
|
{
|
|
49
40
|
class: "decorator",
|
|
50
41
|
languages: ["ts"],
|
|
51
|
-
confidence: "certain",
|
|
52
42
|
typescriptVisibility: { private: true, privateName: false },
|
|
53
43
|
},
|
|
54
44
|
{
|
|
55
45
|
class: "injection-container",
|
|
56
46
|
languages: ["ts"],
|
|
57
|
-
confidence: "certain",
|
|
58
47
|
typescriptVisibility: { private: true, privateName: false },
|
|
59
48
|
},
|
|
60
49
|
{
|
|
61
50
|
class: "framework-lifecycle",
|
|
62
51
|
languages: ["ts"],
|
|
63
|
-
confidence: "certain",
|
|
64
52
|
typescriptVisibility: { private: true, privateName: false },
|
|
65
53
|
},
|
|
66
54
|
{
|
|
67
55
|
class: "serialization-contract",
|
|
68
56
|
languages: ["ts"],
|
|
69
|
-
confidence: "certain",
|
|
70
57
|
typescriptVisibility: { private: true, privateName: false },
|
|
71
58
|
},
|
|
72
59
|
];
|
package/dist/src/exempt.js
CHANGED
|
@@ -80,23 +80,11 @@ function detected(exemptionClass, detect, input) {
|
|
|
80
80
|
.map((symbol) => ({ id: symbol.id, detail: UNANSWERED_DETAIL, site: symbol.position }));
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
/**
|
|
84
|
-
* The records one project's detectors found, each class in the vocabulary's order and
|
|
85
|
-
* none of a switched-off class. Evidence on a private member the class's row says it
|
|
86
|
-
* cannot retain is dropped first, whatever the detector found. Under a production mode
|
|
87
|
-
* a record whose evidence is written in a test file holds nothing, as a reference a
|
|
88
|
-
* test file makes is none there.
|
|
89
|
-
*/
|
|
90
|
-
export function computeExemptions(input, detectors, holding) {
|
|
91
|
-
return detectExemptions(input, detectors, holding).records;
|
|
92
|
-
}
|
|
93
|
-
/** The records of {@link computeExemptions}, beside the test-file evidence it set aside. */
|
|
94
|
-
export function detectExemptions(input, detectors, holding) {
|
|
95
|
-
return heldRecords(evidenceRecords(input, detectors, holding.disabled), holding);
|
|
96
|
-
}
|
|
97
83
|
/**
|
|
98
84
|
* Every record one project's detectors found, one per piece of evidence, before the
|
|
99
|
-
* mode decides which of them hold: what {@link heldRecords} reads.
|
|
85
|
+
* mode decides which of them hold: what {@link heldRecords} reads. Classes run in the
|
|
86
|
+
* vocabulary's order and a switched-off class runs not at all; evidence on a private
|
|
87
|
+
* member the class's row says it cannot retain is dropped, whatever the detector found.
|
|
100
88
|
*/
|
|
101
89
|
export function evidenceRecords(input, detectors, disabled) {
|
|
102
90
|
const visibility = new Map(input.held.symbols.map((symbol) => [symbol.id, symbol.visibility]));
|
|
@@ -64,6 +64,29 @@ function reachabilityClasses(input) {
|
|
|
64
64
|
return published.has(id) ? outside : "certain";
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* The confidence ceiling of a test of dead code: the lowest reachability class among the
|
|
69
|
+
* target symbols it references, because its deadness is theirs, whether or not they share
|
|
70
|
+
* its component. Every other declaration has none below `certain`.
|
|
71
|
+
*/
|
|
72
|
+
function testCeilings(input, classOf) {
|
|
73
|
+
const union = input.swept.matrix.union;
|
|
74
|
+
const tests = new Set(input.swept.sweep.candidates.filter((one) => one.testOfDeadCode).map((one) => one.id));
|
|
75
|
+
return (id) => {
|
|
76
|
+
let lowest = "certain";
|
|
77
|
+
if (!tests.has(id)) {
|
|
78
|
+
return lowest;
|
|
79
|
+
}
|
|
80
|
+
for (const edge of union.out[union.at(id)] ?? []) {
|
|
81
|
+
const target = union.symbols[edge.to];
|
|
82
|
+
const testCode = union.test[edge.to] === true || union.support[edge.to] === true;
|
|
83
|
+
if (target !== undefined && !testCode && union.subject[edge.to] === true) {
|
|
84
|
+
lowest = capped(lowest, classOf(target.id));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return lowest;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
67
90
|
/**
|
|
68
91
|
* The component each dead declaration of the run falls in, and none for a live one, with
|
|
69
92
|
* its members listed in the canonical order where the run lists a component in full.
|
|
@@ -108,12 +131,11 @@ function findingComponents(swept, cascade) {
|
|
|
108
131
|
}
|
|
109
132
|
/**
|
|
110
133
|
* Every finding of the run completed, in the order given. A dead declaration carries its
|
|
111
|
-
* candidate's relation, counts, configurations and component; a live one carries no
|
|
112
|
-
*
|
|
113
|
-
* holds in every configuration. A subject in no dead component gets one
|
|
114
|
-
* numbered past the computed ones in finding order
|
|
115
|
-
*
|
|
116
|
-
* row is read from, the shipped one unless a caller supplies its own.
|
|
134
|
+
* candidate's relation, counts, configurations and component; a live one carries no relation and
|
|
135
|
+
* holds where it is declared; a file or a manifest row carries the class its emitter states,
|
|
136
|
+
* `certain` by default, and holds in every configuration. A subject in no dead component gets one
|
|
137
|
+
* of its own, numbered past the computed ones in finding order so no two families mint one, whose
|
|
138
|
+
* deletion removes no line. `kinds` defaults to the shipped vocabulary.
|
|
117
139
|
*/
|
|
118
140
|
export function completed(input, findings, kinds = KINDS) {
|
|
119
141
|
const { swept } = input;
|
|
@@ -121,6 +143,7 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
121
143
|
const positions = new Map(union.symbols.map((symbol, at) => [declarationKey(symbol.ref, symbol.position.path), at]));
|
|
122
144
|
const candidates = new Map(swept.sweep.candidates.map((candidate) => [candidate.id, candidate]));
|
|
123
145
|
const classOf = reachabilityClasses(input);
|
|
146
|
+
const ceilingOf = testCeilings(input, classOf);
|
|
124
147
|
const componentOf = findingComponents(swept, input.config.reporters.cascade);
|
|
125
148
|
let minted = swept.sweep.components.length;
|
|
126
149
|
const dead = new Set(swept.sweep.components.map((component) => component.id));
|
|
@@ -172,8 +195,12 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
172
195
|
const counts = candidate === undefined
|
|
173
196
|
? union.made[at]
|
|
174
197
|
: { production: candidate.productionRefs, test: candidate.testRefs };
|
|
175
|
-
const reachabilityClass = judged === undefined ? "certain" : classOf(judged.id);
|
|
198
|
+
const reachabilityClass = judged === undefined ? (finding.reachabilityClass ?? "certain") : classOf(judged.id);
|
|
176
199
|
const generated = input.files.generated?.has(finding.position.path) === true;
|
|
200
|
+
const fixability = generated ? "none" : row.fixability;
|
|
201
|
+
const lastUses = fixability === "deletable" && candidate !== undefined
|
|
202
|
+
? input.dependencies.lastUses.get(candidate.id)
|
|
203
|
+
: undefined;
|
|
177
204
|
return {
|
|
178
205
|
code: finding.code,
|
|
179
206
|
kind: row.name,
|
|
@@ -181,7 +208,7 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
181
208
|
position: finding.position,
|
|
182
209
|
symbol: finding.symbol,
|
|
183
210
|
reachabilityClass,
|
|
184
|
-
confidence: capped(reachabilityClass, row.maxClass),
|
|
211
|
+
confidence: capped(capped(reachabilityClass, row.maxClass), judged === undefined ? "certain" : ceilingOf(judged.id)),
|
|
185
212
|
...(candidate === undefined ? {} : { livenessRelation: candidate.relation }),
|
|
186
213
|
testOnly: judged !== undefined && counts?.production === 0 && counts.test > 0,
|
|
187
214
|
generated,
|
|
@@ -192,10 +219,12 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
192
219
|
? swept.matrix.configurations
|
|
193
220
|
: (swept.matrix.heldIn[at] ?? swept.matrix.configurations)),
|
|
194
221
|
consumersLoaded: input.boundary.consumers.loaded,
|
|
195
|
-
fixability
|
|
222
|
+
fixability,
|
|
196
223
|
severity: severityOf(input, finding.code, row.defaultSeverity),
|
|
197
224
|
message: finding.message,
|
|
198
|
-
details:
|
|
225
|
+
details: lastUses === undefined
|
|
226
|
+
? (finding.details ?? {})
|
|
227
|
+
: { ...finding.details, removesLastUseOf: lastUses },
|
|
199
228
|
};
|
|
200
229
|
});
|
|
201
230
|
return componentCapped(done, dead);
|
|
@@ -236,6 +265,7 @@ function dialed(finding, minConfidence) {
|
|
|
236
265
|
export function dialsOf(input, findings) {
|
|
237
266
|
const minConfidence = input.config.analysis.minConfidence;
|
|
238
267
|
const classOf = reachabilityClasses(input);
|
|
268
|
+
const ceilingOf = testCeilings(input, classOf);
|
|
239
269
|
const components = new Set(findings
|
|
240
270
|
.filter((finding) => finding.component.root && dialed(finding, minConfidence))
|
|
241
271
|
.map((finding) => finding.component.id));
|
|
@@ -245,7 +275,7 @@ export function dialsOf(input, findings) {
|
|
|
245
275
|
return (row !== undefined &&
|
|
246
276
|
dialed({
|
|
247
277
|
severity: severityOf(input, code, row.defaultSeverity),
|
|
248
|
-
confidence: capped(classOf(id), row.maxClass),
|
|
278
|
+
confidence: capped(capped(classOf(id), row.maxClass), ceilingOf(id)),
|
|
249
279
|
}, minConfidence));
|
|
250
280
|
},
|
|
251
281
|
withholds: (finding) => dialed(finding, minConfidence) || components.has(finding.component.id),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { declarationKey } from "../dependencies.js";
|
|
1
2
|
import { renderRef } from "../ref.js";
|
|
2
3
|
const UNUSED_DEPENDENCY = "DS1601";
|
|
3
4
|
const MESSAGES = {
|
|
@@ -5,38 +6,14 @@ const MESSAGES = {
|
|
|
5
6
|
"dev-dependency": "no import in the target needs the declared development dependency",
|
|
6
7
|
"peer-dependency": "no import in the target needs the declared peer dependency",
|
|
7
8
|
};
|
|
8
|
-
/**
|
|
9
|
-
* The dependencies the target needs: every package the projects' uses name, every
|
|
10
|
-
* declared dependency that ships a command, which a script, a workflow or a shell
|
|
11
|
-
* runs outside anything the analysis reads, and every peer a needed package
|
|
12
|
-
* requires, which the target satisfies by declaring it.
|
|
13
|
-
*/
|
|
14
|
-
function neededBy(dependencies) {
|
|
15
|
-
const needed = new Set(dependencies.needed);
|
|
16
|
-
for (const [name, installed] of dependencies.installed) {
|
|
17
|
-
if (installed.command) {
|
|
18
|
-
needed.add(name);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
const pending = [...needed];
|
|
22
|
-
for (let name = pending.pop(); name !== undefined; name = pending.pop()) {
|
|
23
|
-
for (const peer of dependencies.installed.get(name)?.peers ?? []) {
|
|
24
|
-
if (!needed.has(peer)) {
|
|
25
|
-
needed.add(peer);
|
|
26
|
-
pending.push(peer);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return needed;
|
|
31
|
-
}
|
|
32
9
|
/**
|
|
33
10
|
* A dependency, development dependency or peer dependency of a manifest the run reads
|
|
34
11
|
* that the target does not need, at the position of its key.
|
|
35
12
|
*/
|
|
36
13
|
function unusedDependencies(dependencies) {
|
|
37
|
-
const needed = neededBy(dependencies);
|
|
38
14
|
return dependencies.declared
|
|
39
|
-
.filter((dependency) => !needed.has(dependency.name)
|
|
15
|
+
.filter((dependency) => !dependencies.needed.has(dependency.name) &&
|
|
16
|
+
!dependencies.ran.has(declarationKey(dependency)))
|
|
40
17
|
.map((dependency) => ({
|
|
41
18
|
code: UNUSED_DEPENDENCY,
|
|
42
19
|
position: dependency.position,
|
|
@@ -52,6 +29,9 @@ function unusedDependencies(dependencies) {
|
|
|
52
29
|
},
|
|
53
30
|
message: MESSAGES[dependency.section],
|
|
54
31
|
details: { dependencyClass: dependency.section },
|
|
32
|
+
...(dependencies.unbuilt.has(dependency.position.path)
|
|
33
|
+
? { reachabilityClass: "possible" }
|
|
34
|
+
: {}),
|
|
55
35
|
}));
|
|
56
36
|
}
|
|
57
37
|
/** The findings of the dependencies-and-module-machinery family, `DS1600` to `DS1699`. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { REPOSITORY_DOCUMENT } from "../config.js";
|
|
2
|
-
import {
|
|
2
|
+
import { FIXED_SEVERITY_CODES } from "../kinds.js";
|
|
3
|
+
import { isRowCode, STALE_SUPPRESSION, SUPPRESSION_WITHOUT_REASON, } from "../suppress.js";
|
|
3
4
|
/** The code a configured root that matches nothing is reported under. */
|
|
4
5
|
const UNMATCHED_ROOT = "DS1704";
|
|
5
6
|
/** The code a configured declaration that names nothing is reported under. */
|
|
@@ -12,13 +13,6 @@ const SUPPRESSION_SUBJECT = "suppression";
|
|
|
12
13
|
const ROOT_SUBJECT = "root";
|
|
13
14
|
/** The fixed position of a finding about a document rather than about a line of one. */
|
|
14
15
|
const DOCUMENT_POSITION = 1;
|
|
15
|
-
/** A run with nothing for the self-check family to report. */
|
|
16
|
-
export const NO_SELF_CHECK = {
|
|
17
|
-
refusals: [],
|
|
18
|
-
unmatchedRoots: [],
|
|
19
|
-
rootsDocument: "",
|
|
20
|
-
unmatchedDeclarations: [],
|
|
21
|
-
};
|
|
22
16
|
/** The words a message names a mechanism by. */
|
|
23
17
|
const WRITTEN = {
|
|
24
18
|
inline: "inline directive",
|
|
@@ -68,7 +62,7 @@ function suppressionRef(symbol, path, files) {
|
|
|
68
62
|
return symbol === "" ? (files(path) ?? path) : symbol;
|
|
69
63
|
}
|
|
70
64
|
/** One finding per refused suppression: the grammar's reason refusal and its scope refusal. */
|
|
71
|
-
|
|
65
|
+
function refusedSuppressions(refusals, files) {
|
|
72
66
|
return refusals.map((refused) => aboutSuppression(refused.reported, suppressionRef(refused.symbol, refused.path, files), refused.site, `${WRITTEN[refused.mechanism]} for ${refused.code} ${refused.reported === SUPPRESSION_WITHOUT_REASON
|
|
73
67
|
? "carries no reason"
|
|
74
68
|
: "names a symbol and no path"}`, { mechanism: refused.mechanism, entry: writtenEntry(refused) }));
|
|
@@ -93,7 +87,17 @@ export function staleSuppressions(stale, files) {
|
|
|
93
87
|
const reports = [...new Set(records.flatMap((one) => one.reports))]
|
|
94
88
|
.filter((code) => !codes.includes(code))
|
|
95
89
|
.sort();
|
|
96
|
-
const
|
|
90
|
+
const rows = codes.filter(isRowCode);
|
|
91
|
+
const settable = rows.filter((code) => !FIXED_SEVERITY_CODES.includes(code));
|
|
92
|
+
const remedy = settable.length === 0
|
|
93
|
+
? "fix what the finding names instead"
|
|
94
|
+
: `fix what the finding names or set the severity of ${spelled(settable)} instead`;
|
|
95
|
+
const declared = reports.length === 0 ? "" : `its declaration reports ${spelled(reports)}`;
|
|
96
|
+
const instead = rows.length > 0
|
|
97
|
+
? `, because ${spelled(rows)} ${rows.length === 1 ? "reports a row" : "report rows"} of a document, which no suppression can name; ${remedy}${declared === "" ? "" : `; ${declared}`}`
|
|
98
|
+
: declared === ""
|
|
99
|
+
? ""
|
|
100
|
+
: `, and ${declared}`;
|
|
97
101
|
return {
|
|
98
102
|
code: STALE_SUPPRESSION,
|
|
99
103
|
mechanism: first.mechanism,
|
package/dist/src/html-entries.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The files the
|
|
3
|
-
*
|
|
2
|
+
* The files the scripts of the target's HTML files load: a module script's, and a
|
|
3
|
+
* classic script's that names its file by `src`. An HTML file is read as text and no
|
|
4
|
+
* markup is parsed, so a `<script>` inside a comment is read as well.
|
|
4
5
|
*/
|
|
5
6
|
import { isExportDeclaration, isImportDeclaration, isStringLiteral, } from "@typescript/native/unstable/ast";
|
|
6
7
|
import { resolveRelativeSpecifier } from "./configuration-files.js";
|
|
@@ -15,6 +16,8 @@ const SCRIPT_END = /<\/script\s*>/iu;
|
|
|
15
16
|
const ATTRIBUTE = /([^\s"'=<>/]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/gu;
|
|
16
17
|
/** A value that names a resource on another origin, or by another scheme. */
|
|
17
18
|
const ELSEWHERE = /^(?:\/\/|[A-Za-z][A-Za-z\d+.-]*:)/u;
|
|
19
|
+
/** The `type` values a classic script carries, compared in lowercase. */
|
|
20
|
+
const CLASSIC_TYPES = new Set(["text/javascript", "application/javascript"]);
|
|
18
21
|
/** The prefixes an inline script's specifier names a file of the target with. */
|
|
19
22
|
const FILE_PREFIXES = ["/", "./", "../"];
|
|
20
23
|
/** Every HTML file below `dir`, outside every `node_modules` directory, by absolute path. */
|
|
@@ -62,18 +65,22 @@ function specifiersOf(parse, fileName, content) {
|
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
65
|
-
* Every value a
|
|
66
|
-
*
|
|
67
|
-
* `./` or `../`.
|
|
68
|
+
* Every value a script of one HTML file names a file by: the `src` attribute's value of a
|
|
69
|
+
* module script or of a classic one, or each specifier an inline module script imports or
|
|
70
|
+
* re-exports that starts with `/`, `./` or `../`.
|
|
68
71
|
*/
|
|
69
72
|
function scriptValues(parse, path, text) {
|
|
70
73
|
const values = [];
|
|
71
74
|
for (const match of text.matchAll(SCRIPT_TAG)) {
|
|
72
75
|
const held = attributes(match[1] ?? "");
|
|
73
|
-
|
|
76
|
+
const type = held.get("type");
|
|
77
|
+
const src = held.get("src");
|
|
78
|
+
if (type !== "module") {
|
|
79
|
+
if (src !== undefined && (type === undefined || CLASSIC_TYPES.has(type.toLowerCase()))) {
|
|
80
|
+
values.push(src);
|
|
81
|
+
}
|
|
74
82
|
continue;
|
|
75
83
|
}
|
|
76
|
-
const src = held.get("src");
|
|
77
84
|
if (src !== undefined) {
|
|
78
85
|
values.push(src);
|
|
79
86
|
continue;
|
|
@@ -102,11 +109,11 @@ function manifestDirectory(host, root, dir) {
|
|
|
102
109
|
return root;
|
|
103
110
|
}
|
|
104
111
|
/**
|
|
105
|
-
* The own files the
|
|
112
|
+
* The own files the scripts of every HTML file below the target root load. A value
|
|
106
113
|
* starting with a single `/` is read against the nearest manifest's directory, any other
|
|
107
|
-
* against the HTML file's own,
|
|
108
|
-
*
|
|
109
|
-
*
|
|
114
|
+
* against the HTML file's own, as a configuration file's relative specifier. One naming no
|
|
115
|
+
* source file is read back through the project's emit mapping, and a `/` one also against
|
|
116
|
+
* an output directory that is the HTML file's own. Another origin or scheme loads nothing.
|
|
110
117
|
*/
|
|
111
118
|
export function htmlEntries(host, targetRoot, files, parse) {
|
|
112
119
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
@@ -124,12 +131,20 @@ export function htmlEntries(host, targetRoot, files, parse) {
|
|
|
124
131
|
if (ELSEWHERE.test(value)) {
|
|
125
132
|
continue;
|
|
126
133
|
}
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
const base = value.startsWith("/") ? manifestDirectory(host, root, dir) : dir;
|
|
135
|
+
const relative = value.startsWith("/") ? `.${value}` : value;
|
|
136
|
+
const reached = resolveRelativeSpecifier(host, base, relative);
|
|
130
137
|
const file = reached === undefined ? undefined : files.byName.get(reached);
|
|
131
138
|
if (file !== undefined) {
|
|
132
139
|
found.push({ file, source: value });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const served = value.startsWith("/")
|
|
143
|
+
? files.outputDirs.filter((outDir) => outDir === dir)
|
|
144
|
+
: [];
|
|
145
|
+
const emitting = [base, ...served].flatMap((against) => files.named(resolvePath(against, relative)));
|
|
146
|
+
for (const one of new Set(emitting)) {
|
|
147
|
+
found.push({ file: one, source: value });
|
|
133
148
|
}
|
|
134
149
|
}
|
|
135
150
|
}
|