@cplieger/deadset-ts 8.0.0-dev.1 → 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 +181 -341
- package/dist/src/augmentations.js +106 -13
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +6 -82
- package/dist/src/configuration-files.js +28 -176
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +1 -1
- 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 +73 -3
- package/dist/src/conventions.js +36 -66
- package/dist/src/dependencies.js +103 -40
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +11 -27
- 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 +139 -72
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +5 -5
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +13 -10
- package/dist/src/findings/non-code-artifacts.js +7 -8
- 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 +41 -32
- package/dist/src/findings/visibility-narrowing.js +19 -16
- 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 +7 -4
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/graph.js +5 -3
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory.js +2 -21
- 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} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
- 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 +76 -4
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/outside-tests.js +1 -1
- 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 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +1 -0
- package/dist/src/references.js +48 -30
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -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 -729
- package/dist/src/roots.js +273 -87
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +595 -21
- package/dist/src/session.js +34 -29
- package/dist/src/source-files.js +1 -1
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +33 -51
- package/dist/src/sweep.js +7 -9
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +8 -11
- 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 +11 -232
- 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 +166 -126
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +10 -7
- 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 +62 -64
- package/jsr.json +1 -1
- package/package.json +4 -3
- package/src/analysis.ts +245 -502
- package/src/augmentations.ts +135 -17
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +31 -302
- package/src/configuration-files.ts +34 -179
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +1 -1
- 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 +85 -3
- package/src/conventions.ts +46 -84
- package/src/dependencies.ts +118 -46
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +12 -38
- 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 +14 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +181 -84
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +7 -7
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +13 -11
- package/src/findings/non-code-artifacts.ts +9 -11
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +45 -32
- package/src/findings/visibility-narrowing.ts +20 -17
- 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 +7 -4
- package/src/git-ignore.ts +50 -26
- package/src/graph.ts +5 -3
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory.ts +2 -20
- 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} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
- 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 +122 -4
- package/src/mdx-entries.ts +3 -26
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +1 -0
- package/src/references.ts +52 -31
- package/src/report-schema.ts +68 -0
- package/src/report.ts +101 -224
- 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 -1114
- package/src/roots.ts +339 -104
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +763 -22
- package/src/session.ts +70 -54
- package/src/source-files.ts +1 -1
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +43 -58
- package/src/sweep.ts +23 -9
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +17 -13
- 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 +18 -288
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +224 -162
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +10 -11
- 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 +74 -64
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -468
- 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/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- 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
package/src/html-entries.ts
CHANGED
|
@@ -12,8 +12,10 @@ import {
|
|
|
12
12
|
} from "@typescript/native/ast";
|
|
13
13
|
import { resolveRelativeSpecifier, type ParseFile } from "./configuration-files.ts";
|
|
14
14
|
import type { Host } from "./host.ts";
|
|
15
|
-
import { dirnamePath, joinPath,
|
|
15
|
+
import { ancestors, dirnamePath, joinPath, resolvePath } from "./paths.ts";
|
|
16
16
|
import type { SourceFiles } from "./source-files.ts";
|
|
17
|
+
import { filesBelow } from "./fs/tree.ts";
|
|
18
|
+
import { holdsManifest } from "./fs/manifest.ts";
|
|
17
19
|
|
|
18
20
|
/** One file a script loads, and the value or specifier that named it. */
|
|
19
21
|
interface HTMLEntry {
|
|
@@ -21,9 +23,6 @@ interface HTMLEntry {
|
|
|
21
23
|
readonly source: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
/** The directory no HTML file is read below. */
|
|
25
|
-
const SKIPPED = "node_modules";
|
|
26
|
-
|
|
27
26
|
/** A `<script` start tag, its attribute text captured. */
|
|
28
27
|
const SCRIPT_TAG = /<script(?=[\s>/])([^>]*)>/giu;
|
|
29
28
|
|
|
@@ -42,28 +41,6 @@ const CLASSIC_TYPES: ReadonlySet<string> = new Set(["text/javascript", "applicat
|
|
|
42
41
|
/** The prefixes an inline script's specifier names a file of the target with. */
|
|
43
42
|
const FILE_PREFIXES = ["/", "./", "../"] as const;
|
|
44
43
|
|
|
45
|
-
/** Every HTML file below `dir`, outside every `node_modules` directory, by absolute path. */
|
|
46
|
-
function htmlFiles(host: Host, dir: string): readonly string[] {
|
|
47
|
-
let entries;
|
|
48
|
-
try {
|
|
49
|
-
entries = host.readDirectory(dir);
|
|
50
|
-
} catch {
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
const found: string[] = [];
|
|
54
|
-
for (const entry of entries) {
|
|
55
|
-
const path = joinPath(dir, entry.name);
|
|
56
|
-
if (entry.directory) {
|
|
57
|
-
if (entry.name !== SKIPPED) {
|
|
58
|
-
found.push(...htmlFiles(host, path));
|
|
59
|
-
}
|
|
60
|
-
} else if (entry.name.endsWith(".html")) {
|
|
61
|
-
found.push(path);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return found.sort();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
44
|
/** Each attribute of one start tag's attribute text, by its lowercase name. */
|
|
68
45
|
function attributes(text: string): ReadonlyMap<string, string> {
|
|
69
46
|
const found = new Map<string, string>();
|
|
@@ -127,12 +104,7 @@ function scriptValues(parse: ParseFile | undefined, path: string, text: string):
|
|
|
127
104
|
|
|
128
105
|
/** The directory of the nearest `package.json` at or above `dir`, inside `root`, else `root`. */
|
|
129
106
|
function manifestDirectory(host: Host, root: string, dir: string): string {
|
|
130
|
-
|
|
131
|
-
if (host.kindOf(joinPath(at, "package.json")) === "file") {
|
|
132
|
-
return at;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return root;
|
|
107
|
+
return ancestors(dir, root).find((at) => holdsManifest(host, at)) ?? root;
|
|
136
108
|
}
|
|
137
109
|
|
|
138
110
|
/**
|
|
@@ -150,7 +122,8 @@ export function htmlEntries(
|
|
|
150
122
|
): readonly HTMLEntry[] {
|
|
151
123
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
152
124
|
const found: HTMLEntry[] = [];
|
|
153
|
-
for (const
|
|
125
|
+
for (const below of filesBelow(host, root, (name) => name.endsWith(".html"))) {
|
|
126
|
+
const path = joinPath(root, below);
|
|
154
127
|
let text: string;
|
|
155
128
|
try {
|
|
156
129
|
text = host.readFile(path);
|
package/src/in-operands.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "@typescript/native/ast";
|
|
14
14
|
import type { ProjectView } from "./session.ts";
|
|
15
15
|
import { sourceFilesOf } from "./source-files.ts";
|
|
16
|
+
import { bytewise } from "./text/compare.ts";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* The text of each string literal, or template literal with no substitution, written as
|
|
@@ -41,5 +42,5 @@ export function inOperandNames<Brand>(
|
|
|
41
42
|
file.forEachChild(visit);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
return [...found].sort();
|
|
45
|
+
return [...found].sort(bytewise);
|
|
45
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -53,7 +53,7 @@ function isInjected(node: Node): boolean {
|
|
|
53
53
|
/** The classes of the target a call whose callee an entry names passes, with that call's entry. */
|
|
54
54
|
function registrations<Brand>(input: DetectorInput<Brand>): readonly Registration[] {
|
|
55
55
|
const { project, held } = input;
|
|
56
|
-
const resolved = resolveEntries(project, held, input.ts.
|
|
56
|
+
const resolved = resolveEntries(project, held, input.ts.injection_registrations);
|
|
57
57
|
if (!resolved.some(namesADeclaration)) {
|
|
58
58
|
return [];
|
|
59
59
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* write to a local that no read reaches, and each case an earlier case covers.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { CODE } from "./contract/contract.generated.ts";
|
|
8
9
|
import {
|
|
9
10
|
isArrowFunction,
|
|
10
11
|
isAwaitExpression,
|
|
@@ -95,6 +96,7 @@ import type { Reference } from "./references.ts";
|
|
|
95
96
|
import type { ProjectView } from "./session.ts";
|
|
96
97
|
import { signatureOf, type SignatureNode } from "./signatures.ts";
|
|
97
98
|
import { spellsNoBinding } from "./spelled-names.ts";
|
|
99
|
+
import { parenthesesRemoved } from "./unwrap.ts";
|
|
98
100
|
|
|
99
101
|
/** The subject kind of each part this module reads. */
|
|
100
102
|
type PartKind = "parameter" | "result" | "statement" | "store" | "case";
|
|
@@ -137,6 +139,12 @@ interface ParameterPlace {
|
|
|
137
139
|
* so the annotation may steer the call's inference.
|
|
138
140
|
*/
|
|
139
141
|
readonly annotated?: "steers" | "free";
|
|
142
|
+
/**
|
|
143
|
+
* Whether the name is one an object binding pattern of a parameter binds, and the
|
|
144
|
+
* compiler gives its function a contextual type: the code handing the argument may act on
|
|
145
|
+
* the pattern's read of the property, which the program does not show.
|
|
146
|
+
*/
|
|
147
|
+
readonly contextualPattern?: true;
|
|
140
148
|
/** Whether the compiler checks the parameter's file, so its unused-parameter rule may report it. */
|
|
141
149
|
readonly checked: boolean;
|
|
142
150
|
}
|
|
@@ -173,13 +181,6 @@ export interface ProjectParts {
|
|
|
173
181
|
readonly constructions: ReadonlySet<string>;
|
|
174
182
|
}
|
|
175
183
|
|
|
176
|
-
/** The codes of the parts this module reads. */
|
|
177
|
-
export const UNUSED_PARAMETER = "DS1801";
|
|
178
|
-
export const UNUSED_RESULT = "DS1803";
|
|
179
|
-
const UNREACHABLE_STATEMENT = "DS1805";
|
|
180
|
-
const DEAD_STORE = "DS1807";
|
|
181
|
-
const UNREACHABLE_CASE = "DS1809";
|
|
182
|
-
|
|
183
184
|
/** The result types that carry no value, as the checker spells them. */
|
|
184
185
|
const NO_VALUE: ReadonlySet<string> = new Set([
|
|
185
186
|
"void",
|
|
@@ -197,11 +198,6 @@ function spanOf(file: SourceFile, root: string, node: Node): FindingPosition {
|
|
|
197
198
|
return { ...start, endLine };
|
|
198
199
|
}
|
|
199
200
|
|
|
200
|
-
/** The parenthesized expression's content, through every pair of parentheses. */
|
|
201
|
-
function unwrapped(node: Node): Node {
|
|
202
|
-
return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
201
|
/** The node the parentheses around one expression sit in. */
|
|
206
202
|
function outerParent(node: Node): Node {
|
|
207
203
|
return isParenthesizedExpression(node.parent) ? outerParent(node.parent) : node.parent;
|
|
@@ -209,7 +205,7 @@ function outerParent(node: Node): Node {
|
|
|
209
205
|
|
|
210
206
|
/** The name a call is made through: the identifier, or the member a property access names. */
|
|
211
207
|
function calleeName(call: CallExpression): Identifier | undefined {
|
|
212
|
-
const callee =
|
|
208
|
+
const callee = parenthesesRemoved(call.expression);
|
|
213
209
|
if (isIdentifier(callee)) {
|
|
214
210
|
return callee;
|
|
215
211
|
}
|
|
@@ -276,7 +272,7 @@ function unreachableExempt(statement: Statement): boolean {
|
|
|
276
272
|
|
|
277
273
|
/** The value a case expression is a literal of, as a key two equal literals share, or none. */
|
|
278
274
|
function caseKey(expression: Node): string | undefined {
|
|
279
|
-
const node =
|
|
275
|
+
const node = parenthesesRemoved(expression);
|
|
280
276
|
if (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node)) {
|
|
281
277
|
return `s:${node.text}`;
|
|
282
278
|
}
|
|
@@ -402,7 +398,7 @@ function extendedNames(files: readonly SourceFile[]): ReadonlySet<string> {
|
|
|
402
398
|
if (isTypeReferenceNode(type)) {
|
|
403
399
|
named = isQualifiedName(type.typeName) ? type.typeName.right : type.typeName;
|
|
404
400
|
} else {
|
|
405
|
-
const expression =
|
|
401
|
+
const expression = parenthesesRemoved(type.expression);
|
|
406
402
|
named = isPropertyAccessExpression(expression) ? expression.name : expression;
|
|
407
403
|
}
|
|
408
404
|
if (isIdentifier(named)) {
|
|
@@ -455,7 +451,7 @@ function decoratorFixes(constructor: ConstructorDeclaration): boolean {
|
|
|
455
451
|
|
|
456
452
|
/** The name an expression spells a class by: an identifier, or the member a property access names. */
|
|
457
453
|
function spelledName(expression: Node): Identifier | undefined {
|
|
458
|
-
const node =
|
|
454
|
+
const node = parenthesesRemoved(expression);
|
|
459
455
|
if (isIdentifier(node)) {
|
|
460
456
|
return node;
|
|
461
457
|
}
|
|
@@ -724,8 +720,8 @@ export function projectParts<Brand>(
|
|
|
724
720
|
if ((isImportClause(node) || isNamespaceImport(node)) && node.name !== undefined) {
|
|
725
721
|
specifiers.add(position(node.name));
|
|
726
722
|
}
|
|
727
|
-
if (isExportAssignment(node) && isIdentifier(
|
|
728
|
-
specifiers.add(position(
|
|
723
|
+
if (isExportAssignment(node) && isIdentifier(parenthesesRemoved(node.expression))) {
|
|
724
|
+
specifiers.add(position(parenthesesRemoved(node.expression)));
|
|
729
725
|
}
|
|
730
726
|
if (isSwitchStatement(node)) {
|
|
731
727
|
const holder = holderOf(node);
|
|
@@ -863,7 +859,7 @@ export function projectParts<Brand>(
|
|
|
863
859
|
}
|
|
864
860
|
walk.catches.forEach(({ name, holder }, at) => {
|
|
865
861
|
parts.push({
|
|
866
|
-
code:
|
|
862
|
+
code: CODE.deadStore,
|
|
867
863
|
declaration: holder,
|
|
868
864
|
kind: "store",
|
|
869
865
|
name: name.text,
|
|
@@ -875,7 +871,7 @@ export function projectParts<Brand>(
|
|
|
875
871
|
for (const { names, uses, holder } of loopVariables) {
|
|
876
872
|
for (const name of names) {
|
|
877
873
|
parts.push({
|
|
878
|
-
code:
|
|
874
|
+
code: CODE.deadStore,
|
|
879
875
|
declaration: holder,
|
|
880
876
|
kind: "store",
|
|
881
877
|
name: name.text,
|
|
@@ -893,7 +889,7 @@ export function projectParts<Brand>(
|
|
|
893
889
|
});
|
|
894
890
|
for (const { statement, holder } of walk.unreachable) {
|
|
895
891
|
parts.push({
|
|
896
|
-
code:
|
|
892
|
+
code: CODE.unreachableStatement,
|
|
897
893
|
declaration: holder,
|
|
898
894
|
kind: "statement",
|
|
899
895
|
name: "statement",
|
|
@@ -904,7 +900,7 @@ export function projectParts<Brand>(
|
|
|
904
900
|
}
|
|
905
901
|
for (const { clause, text, holder } of walk.cases) {
|
|
906
902
|
parts.push({
|
|
907
|
-
code:
|
|
903
|
+
code: CODE.unreachableCase,
|
|
908
904
|
declaration: holder,
|
|
909
905
|
kind: "case",
|
|
910
906
|
name: text,
|
|
@@ -1093,10 +1089,9 @@ function calleeSteers<Brand>(
|
|
|
1093
1089
|
|
|
1094
1090
|
/**
|
|
1095
1091
|
* The parts with each name of `contextual` judged where the compiler gives its function a
|
|
1096
|
-
* contextual type:
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
* name read.
|
|
1092
|
+
* contextual type: a name an object pattern binds is marked as one, and an annotated
|
|
1093
|
+
* parameter with whether the call's inference may read its annotation. A question the
|
|
1094
|
+
* checker leaves unanswered keeps the name read.
|
|
1100
1095
|
*/
|
|
1101
1096
|
function withContextualRead<Brand>(
|
|
1102
1097
|
project: ProjectView<Brand>,
|
|
@@ -1118,15 +1113,17 @@ function withContextualRead<Brand>(
|
|
|
1118
1113
|
const place = one.part.parameter;
|
|
1119
1114
|
judged.set(
|
|
1120
1115
|
one.part,
|
|
1121
|
-
|
|
1116
|
+
answer === UNANSWERED || place === undefined
|
|
1122
1117
|
? { ...one.part, used: true }
|
|
1123
|
-
:
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
...
|
|
1127
|
-
|
|
1118
|
+
: one.pattern
|
|
1119
|
+
? { ...one.part, parameter: { ...place, contextualPattern: true } }
|
|
1120
|
+
: {
|
|
1121
|
+
...one.part,
|
|
1122
|
+
parameter: {
|
|
1123
|
+
...place,
|
|
1124
|
+
annotated: calleeSteers(project, one.signature, one.at) ? "steers" : "free",
|
|
1125
|
+
},
|
|
1128
1126
|
},
|
|
1129
|
-
},
|
|
1130
1127
|
);
|
|
1131
1128
|
}
|
|
1132
1129
|
return parts.map((part) => judged.get(part) ?? part);
|
|
@@ -1169,7 +1166,7 @@ function parameterParts(
|
|
|
1169
1166
|
return;
|
|
1170
1167
|
}
|
|
1171
1168
|
const part: PartFact = {
|
|
1172
|
-
code:
|
|
1169
|
+
code: CODE.unusedParameter,
|
|
1173
1170
|
declaration,
|
|
1174
1171
|
kind: "parameter",
|
|
1175
1172
|
name: name.text,
|
|
@@ -1205,6 +1202,8 @@ interface Local {
|
|
|
1205
1202
|
readonly uses: readonly Identifier[];
|
|
1206
1203
|
/** A `var` of a nested block, whose function scope outlives the block's end. */
|
|
1207
1204
|
readonly outlives: boolean;
|
|
1205
|
+
/** A function declaration, written at the start of the block whatever its place. */
|
|
1206
|
+
readonly hoisted: boolean;
|
|
1208
1207
|
}
|
|
1209
1208
|
|
|
1210
1209
|
/**
|
|
@@ -1244,9 +1243,10 @@ function functionOf(block: Block): FunctionLikeDeclaration | undefined {
|
|
|
1244
1243
|
}
|
|
1245
1244
|
|
|
1246
1245
|
/**
|
|
1247
|
-
* The locals one block declares with `let`, `const
|
|
1248
|
-
* block that spell it. A `var` of a nested
|
|
1249
|
-
*
|
|
1246
|
+
* The locals one block declares with `let`, `const`, `var` or a function declaration
|
|
1247
|
+
* inside a function, each with the names of the block that spell it. A `var` of a nested
|
|
1248
|
+
* block is one where no nested function of its function names it, since the function scope
|
|
1249
|
+
* holds it past the block's end.
|
|
1250
1250
|
*/
|
|
1251
1251
|
function localsOf(block: Block): readonly Local[] {
|
|
1252
1252
|
const declared: {
|
|
@@ -1254,10 +1254,23 @@ function localsOf(block: Block): readonly Local[] {
|
|
|
1254
1254
|
statement: number;
|
|
1255
1255
|
initialized: boolean;
|
|
1256
1256
|
outlives: boolean;
|
|
1257
|
+
hoisted: boolean;
|
|
1257
1258
|
}[] = [];
|
|
1258
1259
|
const scope = functionOf(block);
|
|
1259
1260
|
const outlives = scope?.body !== block;
|
|
1260
1261
|
block.statements.forEach((statement, at) => {
|
|
1262
|
+
if (isFunctionDeclaration(statement)) {
|
|
1263
|
+
if (scope !== undefined && statement.name !== undefined && statement.body !== undefined) {
|
|
1264
|
+
declared.push({
|
|
1265
|
+
name: statement.name,
|
|
1266
|
+
statement: at,
|
|
1267
|
+
initialized: true,
|
|
1268
|
+
outlives: false,
|
|
1269
|
+
hoisted: true,
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1261
1274
|
if (!isVariableStatement(statement)) {
|
|
1262
1275
|
return;
|
|
1263
1276
|
}
|
|
@@ -1267,6 +1280,11 @@ function localsOf(block: Block): readonly Local[] {
|
|
|
1267
1280
|
return;
|
|
1268
1281
|
}
|
|
1269
1282
|
for (const declaration of list.declarations) {
|
|
1283
|
+
// A write to a local typed `never` is the compiler's exhaustiveness check, which
|
|
1284
|
+
// fails to compile once a case goes unhandled.
|
|
1285
|
+
if (declaration.type?.kind === SyntaxKind.NeverKeyword) {
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1270
1288
|
for (const name of boundNames(declaration.name)) {
|
|
1271
1289
|
if (
|
|
1272
1290
|
!blockScoped &&
|
|
@@ -1282,6 +1300,7 @@ function localsOf(block: Block): readonly Local[] {
|
|
|
1282
1300
|
statement: at,
|
|
1283
1301
|
initialized: declaration.initializer !== undefined,
|
|
1284
1302
|
outlives: !blockScoped && outlives,
|
|
1303
|
+
hoisted: false,
|
|
1285
1304
|
});
|
|
1286
1305
|
}
|
|
1287
1306
|
}
|
|
@@ -1302,7 +1321,7 @@ function isPlainWrite(use: Identifier): boolean {
|
|
|
1302
1321
|
return (
|
|
1303
1322
|
isBinaryExpression(parent) &&
|
|
1304
1323
|
parent.operatorToken.kind === SyntaxKind.EqualsToken &&
|
|
1305
|
-
|
|
1324
|
+
parenthesesRemoved(parent.left) === use
|
|
1306
1325
|
);
|
|
1307
1326
|
}
|
|
1308
1327
|
|
|
@@ -1324,7 +1343,7 @@ function deadStores(
|
|
|
1324
1343
|
): PartFact[] {
|
|
1325
1344
|
const found: PartFact[] = [];
|
|
1326
1345
|
const deadAt = (local: Local, target: Identifier): PartFact => ({
|
|
1327
|
-
code:
|
|
1346
|
+
code: CODE.deadStore,
|
|
1328
1347
|
declaration: holder,
|
|
1329
1348
|
kind: "store",
|
|
1330
1349
|
name: local.name.text,
|
|
@@ -1333,12 +1352,22 @@ function deadStores(
|
|
|
1333
1352
|
used: false,
|
|
1334
1353
|
});
|
|
1335
1354
|
for (const local of locals) {
|
|
1336
|
-
const
|
|
1355
|
+
const own = local.hoisted ? block.statements[local.statement] : undefined;
|
|
1356
|
+
// A function's reads of itself keep it no more than its absence would.
|
|
1357
|
+
const uses = local.uses.filter(
|
|
1358
|
+
(use) => readsBinding(use, local.name, ids) && (own === undefined || !within(use, own)),
|
|
1359
|
+
);
|
|
1337
1360
|
if (!local.outlives && uses.every(isPlainWrite)) {
|
|
1338
|
-
|
|
1361
|
+
// A declaration with no initializer writes `undefined`, the one store left to name.
|
|
1362
|
+
const writes = [...(local.initialized ? [local.name] : []), ...uses];
|
|
1363
|
+
const targets = writes.length === 0 ? [local.name] : writes;
|
|
1339
1364
|
found.push(...targets.map((target) => deadAt(local, target)));
|
|
1340
1365
|
continue;
|
|
1341
1366
|
}
|
|
1367
|
+
// A read anywhere in the block reads a function declaration, which is written first.
|
|
1368
|
+
if (local.hoisted) {
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1342
1371
|
if (uses.some((use) => inNestedFunction(use, block))) {
|
|
1343
1372
|
continue;
|
|
1344
1373
|
}
|
|
@@ -1349,14 +1378,14 @@ function deadStores(
|
|
|
1349
1378
|
if (!isExpressionStatement(statement)) {
|
|
1350
1379
|
return undefined;
|
|
1351
1380
|
}
|
|
1352
|
-
const expression =
|
|
1381
|
+
const expression = parenthesesRemoved(statement.expression);
|
|
1353
1382
|
if (
|
|
1354
1383
|
!isBinaryExpression(expression) ||
|
|
1355
1384
|
expression.operatorToken.kind !== SyntaxKind.EqualsToken
|
|
1356
1385
|
) {
|
|
1357
1386
|
return undefined;
|
|
1358
1387
|
}
|
|
1359
|
-
const target =
|
|
1388
|
+
const target = parenthesesRemoved(expression.left);
|
|
1360
1389
|
return isIdentifier(target) && uses.includes(target)
|
|
1361
1390
|
? { target, value: expression.right }
|
|
1362
1391
|
: undefined;
|
|
@@ -1443,7 +1472,7 @@ function resultParts<Brand>(
|
|
|
1443
1472
|
const valued = project.queries.nonNullable(result);
|
|
1444
1473
|
const members = isAnswered(valued) ? project.queries.propertiesOf(valued) : UNANSWERED;
|
|
1445
1474
|
found.push({
|
|
1446
|
-
code:
|
|
1475
|
+
code: CODE.unusedResult,
|
|
1447
1476
|
declaration: id,
|
|
1448
1477
|
kind: "result",
|
|
1449
1478
|
name: "result",
|
package/src/inventory.ts
CHANGED
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
import { computedComponent, nameComponent, renderRef, type Component, type Module } from "./ref.ts";
|
|
51
51
|
import { isAnswered, UNANSWERED } from "./query.ts";
|
|
52
52
|
import type { Handle, ProjectView } from "./session.ts";
|
|
53
|
+
import { manifestAt } from "./fs/manifest.ts";
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* The symbol inventory of one project: every declaration the target's own files
|
|
@@ -233,8 +234,6 @@ interface ExportScope {
|
|
|
233
234
|
readonly nameNode: Node;
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
const MANIFEST = "package.json";
|
|
237
|
-
|
|
238
237
|
/** One manifest that names a package, and the directory below the root that holds it. */
|
|
239
238
|
interface Named {
|
|
240
239
|
readonly name: string;
|
|
@@ -255,24 +254,7 @@ export function packageScope(host: Host, root: string): (path: string) => Module
|
|
|
255
254
|
const named = new Map<string, Named | undefined>();
|
|
256
255
|
|
|
257
256
|
const manifestName = (dir: string): string | undefined => {
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
text = host.readFile(joinPath(root, dir, MANIFEST));
|
|
261
|
-
} catch {
|
|
262
|
-
return undefined;
|
|
263
|
-
}
|
|
264
|
-
let value: unknown;
|
|
265
|
-
try {
|
|
266
|
-
value = JSON.parse(text);
|
|
267
|
-
} catch {
|
|
268
|
-
// A manifest this program cannot read names no package, which is the same
|
|
269
|
-
// answer as an absent one: the file is then scoped by the target root.
|
|
270
|
-
return undefined;
|
|
271
|
-
}
|
|
272
|
-
if (typeof value !== "object" || value === null) {
|
|
273
|
-
return undefined;
|
|
274
|
-
}
|
|
275
|
-
const name = (value as Record<string, unknown>)["name"];
|
|
257
|
+
const name = manifestAt(host, joinPath(root, dir))?.["name"];
|
|
276
258
|
return typeof name === "string" && name !== "" ? name : undefined;
|
|
277
259
|
};
|
|
278
260
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const JAVASCRIPT_FILE = /\.(?:js|jsx|mjs|cjs)$/u;
|
|
2
|
+
|
|
3
|
+
/** A minified file by its name; any other is one by its size and its mean line length. */
|
|
4
|
+
const MINIFIED_NAME = /\.min\.(?:js|mjs|cjs)$/u;
|
|
5
|
+
const MINIFIED_BYTES = 10_240;
|
|
6
|
+
const MINIFIED_LINE = 500;
|
|
7
|
+
|
|
8
|
+
/** Whether one name is a minified file's, which decides it without its text. */
|
|
9
|
+
export function minifiedByName(fileName: string): boolean {
|
|
10
|
+
return MINIFIED_NAME.test(fileName);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Whether one JavaScript file is minified: by its name, or by its text's size and mean line length. */
|
|
14
|
+
export function minified(fileName: string, text: string): boolean {
|
|
15
|
+
if (!JAVASCRIPT_FILE.test(fileName)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (minifiedByName(fileName)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
// A UTF-16 unit encodes to at most three UTF-8 bytes, so a shorter text is below the size.
|
|
22
|
+
if (text.length * 3 < MINIFIED_BYTES) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const lines = text.split(/\r?\n/u).length;
|
|
26
|
+
return utf8Bytes(text) >= MINIFIED_BYTES && text.length / lines > MINIFIED_LINE;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The length of a text in UTF-8, a lone surrogate counting as the three bytes of U+FFFD. */
|
|
30
|
+
function utf8Bytes(text: string): number {
|
|
31
|
+
let bytes = 0;
|
|
32
|
+
for (const character of text) {
|
|
33
|
+
const point = character.codePointAt(0) ?? 0;
|
|
34
|
+
bytes += point < 0x80 ? 1 : point < 0x800 ? 2 : point < 0x10000 ? 3 : 4;
|
|
35
|
+
}
|
|
36
|
+
return bytes;
|
|
37
|
+
}
|