@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
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ancestors, joinPath, resolvePath } from "../paths.js";
|
|
2
|
+
import { LANGUAGE_ROWS } from "../contract/contract.generated.js";
|
|
3
|
+
import { LANGUAGE } from "../product.js";
|
|
4
|
+
import { bytewise } from "../text/compare.js";
|
|
5
|
+
import { globExpression } from "../text/glob.js";
|
|
6
|
+
/** The directories no walk of the tree enters, by the patterns the language's row names. */
|
|
7
|
+
const SKIPPED_DIRECTORIES = LANGUAGE_ROWS[LANGUAGE].skippedDirectories.map(globExpression);
|
|
8
|
+
function skipped(name) {
|
|
9
|
+
return SKIPPED_DIRECTORIES.some((pattern) => pattern.test(name));
|
|
10
|
+
}
|
|
11
|
+
/** One file's text without its line ending, or undefined where it cannot be read. */
|
|
12
|
+
function lineOf(host, path) {
|
|
13
|
+
try {
|
|
14
|
+
return host.readFile(path).replace(/\r?\n$/u, "");
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The repository whose working tree holds `dir`: the nearest directory at or above it with a
|
|
22
|
+
* `.git`. A `.git` file, which a linked worktree or a submodule holds, names the repository's
|
|
23
|
+
* directory on its `gitdir:` line; that directory's `commondir` file, where it has one, names
|
|
24
|
+
* the directory shared with the main worktree, which holds `info/exclude`:
|
|
25
|
+
* https://git-scm.com/docs/gitrepository-layout
|
|
26
|
+
*/
|
|
27
|
+
export function repositoryOf(host, dir) {
|
|
28
|
+
for (const root of ancestors(dir)) {
|
|
29
|
+
const dotGit = joinPath(root, ".git");
|
|
30
|
+
const kind = host.kindOf(dotGit);
|
|
31
|
+
if (kind === "directory") {
|
|
32
|
+
return { root, exclude: joinPath(dotGit, "info/exclude") };
|
|
33
|
+
}
|
|
34
|
+
if (kind === "file") {
|
|
35
|
+
const named = /^gitdir: (.+)$/u.exec(lineOf(host, dotGit) ?? "")?.[1];
|
|
36
|
+
const gitDir = named === undefined ? undefined : resolvePath(root, named);
|
|
37
|
+
const common = gitDir === undefined ? undefined : lineOf(host, joinPath(gitDir, "commondir"));
|
|
38
|
+
const shared = gitDir === undefined || common === undefined ? gitDir : resolvePath(gitDir, common);
|
|
39
|
+
return { root, exclude: shared === undefined ? undefined : joinPath(shared, "info/exclude") };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
/** The entries of one directory, none where it cannot be read. */
|
|
45
|
+
export function entriesOf(host, dir) {
|
|
46
|
+
try {
|
|
47
|
+
return host.readDirectory(dir);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The names of the files directly in `dir` whose name `accepts`, in bytewise order. */
|
|
54
|
+
export function filesIn(host, dir, accepts) {
|
|
55
|
+
return entriesOf(host, dir)
|
|
56
|
+
.filter((entry) => !entry.directory && accepts(entry.name))
|
|
57
|
+
.map((entry) => entry.name)
|
|
58
|
+
.sort(bytewise);
|
|
59
|
+
}
|
|
60
|
+
/** The names of the directories directly in `dir` a walk enters, in bytewise order. */
|
|
61
|
+
export function directoriesIn(host, dir) {
|
|
62
|
+
return entriesOf(host, dir)
|
|
63
|
+
.filter((entry) => entry.directory && !skipped(entry.name))
|
|
64
|
+
.map((entry) => entry.name)
|
|
65
|
+
.sort(bytewise);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Every file below `root` whose name `accepts`, by its path relative to `root` in bytewise
|
|
69
|
+
* order, read in no skipped directory.
|
|
70
|
+
*/
|
|
71
|
+
export function filesBelow(host, root, accepts) {
|
|
72
|
+
const found = [];
|
|
73
|
+
const pending = [""];
|
|
74
|
+
for (let dir = pending.pop(); dir !== undefined; dir = pending.pop()) {
|
|
75
|
+
for (const entry of entriesOf(host, joinPath(root, dir))) {
|
|
76
|
+
const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
77
|
+
if (!entry.directory) {
|
|
78
|
+
if (accepts(entry.name)) {
|
|
79
|
+
found.push(path);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (!skipped(entry.name)) {
|
|
83
|
+
pending.push(path);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return found.sort(bytewise);
|
|
88
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The generated-file class on TypeScript: every declaration in a file that marks itself
|
|
3
|
-
* generated,
|
|
4
|
-
* retained, because a generator owns the file and a deletion there is undone at the next
|
|
3
|
+
* generated, below a directory an applied convention row names as generated, or minified,
|
|
4
|
+
* is retained, because a generator owns the file and a deletion there is undone at the next
|
|
5
5
|
* generation.
|
|
6
6
|
*/
|
|
7
|
+
import { minified } from "./javascript-file.js";
|
|
7
8
|
/** The generated-code header, matched against each line of a leading comment. */
|
|
8
9
|
const HEADER_LINE = /^\/\/ Code generated .* DO NOT EDIT\.$/u;
|
|
9
10
|
/** The documentation tag `@generated` as a word of a comment. */
|
|
@@ -64,7 +65,7 @@ export function generatedByComment(text) {
|
|
|
64
65
|
/**
|
|
65
66
|
* Every own file of one project that is generated, by its path below the target root,
|
|
66
67
|
* with the detail a retention in it names: its leading comments' mark, else the row
|
|
67
|
-
* naming its directory as generated.
|
|
68
|
+
* naming its directory as generated, else its minified text.
|
|
68
69
|
*/
|
|
69
70
|
export function generatedFilesOf(files, byRow) {
|
|
70
71
|
const found = new Map();
|
|
@@ -72,7 +73,9 @@ export function generatedFilesOf(files, byRow) {
|
|
|
72
73
|
const row = byRow.get(path);
|
|
73
74
|
const detail = generatedByComment(file.text) ??
|
|
74
75
|
(row === undefined
|
|
75
|
-
?
|
|
76
|
+
? minified(file.fileName, file.text)
|
|
77
|
+
? "generated as a minified JavaScript file"
|
|
78
|
+
: undefined
|
|
76
79
|
: `generated below a directory the convention row ${row} names`);
|
|
77
80
|
if (detail !== undefined) {
|
|
78
81
|
found.set(path, detail);
|
package/dist/src/git-ignore.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Which files
|
|
2
|
+
* Which files git ignores below a tree's root, read as git reads its ignore files:
|
|
3
3
|
* https://git-scm.com/docs/gitignore#_pattern_format
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import { ancestors, dirnamePath, joinPath, relativePath } from "./paths.js";
|
|
6
|
+
import { message } from "./product.js";
|
|
7
|
+
import { gitignoreExpression } from "./text/glob.js";
|
|
8
|
+
import { repositoryOf } from "./fs/tree.js";
|
|
9
|
+
/** The rules one ignore file's text writes, in order. */
|
|
8
10
|
function rulesOf(text) {
|
|
9
11
|
const rules = [];
|
|
10
12
|
for (const raw of text.split(/\r?\n/u)) {
|
|
@@ -13,7 +15,7 @@ function rulesOf(text) {
|
|
|
13
15
|
continue;
|
|
14
16
|
}
|
|
15
17
|
const negated = line.startsWith("!");
|
|
16
|
-
line = negated
|
|
18
|
+
line = negated ? line.slice(1) : line;
|
|
17
19
|
const directoryOnly = line.endsWith("/");
|
|
18
20
|
line = line.replace(/\/+$/u, "");
|
|
19
21
|
if (line === "") {
|
|
@@ -26,43 +28,54 @@ function rulesOf(text) {
|
|
|
26
28
|
rules.push({
|
|
27
29
|
negated,
|
|
28
30
|
directoryOnly,
|
|
29
|
-
expression:
|
|
31
|
+
expression: gitignoreExpression(anchored ? pattern : `**/${pattern}`),
|
|
30
32
|
});
|
|
31
33
|
}
|
|
32
34
|
return rules;
|
|
33
35
|
}
|
|
36
|
+
function rulesIn(host, path, unread) {
|
|
37
|
+
try {
|
|
38
|
+
return rulesOf(host.readFile(path));
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (host.kindOf(path) !== "absent") {
|
|
42
|
+
unread(path, message(error));
|
|
43
|
+
}
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
34
47
|
/**
|
|
35
|
-
* Whether
|
|
36
|
-
*
|
|
37
|
-
*
|
|
48
|
+
* Whether git ignores one absolute path below `root`: by the repository's `info/exclude`, then
|
|
49
|
+
* the `.gitignore` files from its root down to the path's directory, a later pattern overriding
|
|
50
|
+
* an earlier one, though a file below an ignored directory stays ignored, as git cannot re-include
|
|
51
|
+
* it. A path outside `root` is not ignored. With no repository, the files at and below `root` are
|
|
52
|
+
* read; with one, the directories above `root` give patterns but are not tested. An unreadable
|
|
53
|
+
* ignore file ignores nothing, and `unread` is told it.
|
|
38
54
|
*/
|
|
39
|
-
export function gitIgnored(host, root) {
|
|
55
|
+
export function gitIgnored(host, root, unread = () => undefined) {
|
|
56
|
+
const found = repositoryOf(host, root);
|
|
57
|
+
const repository = found?.root;
|
|
58
|
+
const above = repository === undefined ? [] : [...ancestors(dirnamePath(root), repository)].reverse();
|
|
59
|
+
const excluded = found?.exclude === undefined ? [] : rulesIn(host, found.exclude, unread);
|
|
40
60
|
const read = new Map();
|
|
41
61
|
const rulesAt = (dir) => {
|
|
42
62
|
let rules = read.get(dir);
|
|
43
63
|
if (rules === undefined) {
|
|
44
|
-
|
|
45
|
-
rules = rulesOf(host.readFile(joinPath(dir, ".gitignore")));
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
rules = [];
|
|
49
|
-
}
|
|
64
|
+
rules = rulesIn(host, joinPath(dir, ".gitignore"), unread);
|
|
50
65
|
read.set(dir, rules);
|
|
51
66
|
}
|
|
52
67
|
return rules;
|
|
53
68
|
};
|
|
54
|
-
/** Whether the rules of `root` and of each directory down to one path's own decide it ignored. */
|
|
55
69
|
const decided = (path, directory) => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
dirs.unshift(root);
|
|
70
|
+
const sources = [
|
|
71
|
+
...(repository === undefined ? [] : [[repository, excluded]]),
|
|
72
|
+
...[...above, ...[...ancestors(dirnamePath(path), root)].reverse()].map((dir) => [dir, rulesAt(dir)]),
|
|
73
|
+
];
|
|
61
74
|
let ignored = false;
|
|
62
|
-
for (const dir of
|
|
63
|
-
const below = relativePath(dir, path);
|
|
64
|
-
for (const rule of
|
|
65
|
-
if ((directory || !rule.directoryOnly) && rule.expression.test(below
|
|
75
|
+
for (const [dir, rules] of sources) {
|
|
76
|
+
const below = relativePath(dir, path) ?? "";
|
|
77
|
+
for (const rule of rules) {
|
|
78
|
+
if ((directory || !rule.directoryOnly) && rule.expression.test(below)) {
|
|
66
79
|
ignored = !rule.negated;
|
|
67
80
|
}
|
|
68
81
|
}
|
package/dist/src/graph.js
CHANGED
|
@@ -34,9 +34,9 @@ const SWEPT = {
|
|
|
34
34
|
* Whether a root of each kind names a caller, which holds it live under both relations: a
|
|
35
35
|
* tool reads what it loads and what its strings name, a runtime runs a test, setup or
|
|
36
36
|
* worker file, a page's module script, a command, a script or a workflow step, the manifest
|
|
37
|
-
* names what enters the package, an alias global stands for its export,
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* names what enters the package, an alias global stands for its export, an outside
|
|
38
|
+
* declaration names a global interface merging into it, and an entry file, a configured root
|
|
39
|
+
* and a pattern assert a caller. A published API supposes a consumer: reachability alone.
|
|
40
40
|
*/
|
|
41
41
|
const NAMES_A_CALLER = {
|
|
42
42
|
"entry-file": true,
|
|
@@ -49,8 +49,10 @@ const NAMES_A_CALLER = {
|
|
|
49
49
|
pattern: true,
|
|
50
50
|
"type-error": true,
|
|
51
51
|
"type-query-alias": true,
|
|
52
|
+
"global-augmentation": true,
|
|
52
53
|
"configuration-file": true,
|
|
53
54
|
"configuration-string": true,
|
|
55
|
+
"bundler-entry": true,
|
|
54
56
|
"test-runner": true,
|
|
55
57
|
"browser-tests": true,
|
|
56
58
|
worker: true,
|
package/dist/src/html-entries.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { isExportDeclaration, isImportDeclaration, isStringLiteral, } from "@typescript/native/ast";
|
|
7
7
|
import { resolveRelativeSpecifier } from "./configuration-files.js";
|
|
8
|
-
import { dirnamePath, joinPath,
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import { ancestors, dirnamePath, joinPath, resolvePath } from "./paths.js";
|
|
9
|
+
import { filesBelow } from "./fs/tree.js";
|
|
10
|
+
import { holdsManifest } from "./fs/manifest.js";
|
|
11
11
|
/** A `<script` start tag, its attribute text captured. */
|
|
12
12
|
const SCRIPT_TAG = /<script(?=[\s>/])([^>]*)>/giu;
|
|
13
13
|
/** The end tag an inline module script's content runs to. */
|
|
@@ -20,29 +20,6 @@ const ELSEWHERE = /^(?:\/\/|[A-Za-z][A-Za-z\d+.-]*:)/u;
|
|
|
20
20
|
const CLASSIC_TYPES = new Set(["text/javascript", "application/javascript"]);
|
|
21
21
|
/** The prefixes an inline script's specifier names a file of the target with. */
|
|
22
22
|
const FILE_PREFIXES = ["/", "./", "../"];
|
|
23
|
-
/** Every HTML file below `dir`, outside every `node_modules` directory, by absolute path. */
|
|
24
|
-
function htmlFiles(host, dir) {
|
|
25
|
-
let entries;
|
|
26
|
-
try {
|
|
27
|
-
entries = host.readDirectory(dir);
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
const found = [];
|
|
33
|
-
for (const entry of entries) {
|
|
34
|
-
const path = joinPath(dir, entry.name);
|
|
35
|
-
if (entry.directory) {
|
|
36
|
-
if (entry.name !== SKIPPED) {
|
|
37
|
-
found.push(...htmlFiles(host, path));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else if (entry.name.endsWith(".html")) {
|
|
41
|
-
found.push(path);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return found.sort();
|
|
45
|
-
}
|
|
46
23
|
/** Each attribute of one start tag's attribute text, by its lowercase name. */
|
|
47
24
|
function attributes(text) {
|
|
48
25
|
const found = new Map();
|
|
@@ -98,12 +75,7 @@ function scriptValues(parse, path, text) {
|
|
|
98
75
|
}
|
|
99
76
|
/** The directory of the nearest `package.json` at or above `dir`, inside `root`, else `root`. */
|
|
100
77
|
function manifestDirectory(host, root, dir) {
|
|
101
|
-
|
|
102
|
-
if (host.kindOf(joinPath(at, "package.json")) === "file") {
|
|
103
|
-
return at;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return root;
|
|
78
|
+
return ancestors(dir, root).find((at) => holdsManifest(host, at)) ?? root;
|
|
107
79
|
}
|
|
108
80
|
/**
|
|
109
81
|
* The own files the scripts of every HTML file below the target root load. A value
|
|
@@ -115,7 +87,8 @@ function manifestDirectory(host, root, dir) {
|
|
|
115
87
|
export function htmlEntries(host, targetRoot, files, parse) {
|
|
116
88
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
117
89
|
const found = [];
|
|
118
|
-
for (const
|
|
90
|
+
for (const below of filesBelow(host, root, (name) => name.endsWith(".html"))) {
|
|
91
|
+
const path = joinPath(root, below);
|
|
119
92
|
let text;
|
|
120
93
|
try {
|
|
121
94
|
text = host.readFile(path);
|
package/dist/src/in-operands.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { isBinaryExpression, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
7
7
|
import { sourceFilesOf } from "./source-files.js";
|
|
8
|
+
import { bytewise } from "./text/compare.js";
|
|
8
9
|
/**
|
|
9
10
|
* The text of each string literal, or template literal with no substitution, written as
|
|
10
11
|
* the left operand of an `in` expression in a file of the project that is no test file.
|
|
@@ -26,5 +27,5 @@ export function inOperandNames(project, targetRoot, testFiles) {
|
|
|
26
27
|
file.forEachChild(visit);
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
return [...found].sort();
|
|
30
|
+
return [...found].sort(bytewise);
|
|
30
31
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -2,4 +2,4 @@ export { run, SETTING_OPTIONS } from "./run.js";
|
|
|
2
2
|
export { DECLINED_CONVENTIONS } from "./entry-point-gaps.js";
|
|
3
3
|
export { byPosition, positionKey, PositionError, renderPosition, } from "./position.js";
|
|
4
4
|
export { computedComponent, isRef, nameComponent, REF_EXPRESSIONS, renderRef, } from "./ref.js";
|
|
5
|
-
export { CONTRACT_VERSION } from "./
|
|
5
|
+
export { CONTRACT_VERSION } from "./contract/contract.generated.js";
|
|
@@ -24,7 +24,7 @@ function isInjected(node) {
|
|
|
24
24
|
/** The classes of the target a call whose callee an entry names passes, with that call's entry. */
|
|
25
25
|
function registrations(input) {
|
|
26
26
|
const { project, held } = input;
|
|
27
|
-
const resolved = resolveEntries(project, held, input.ts.
|
|
27
|
+
const resolved = resolveEntries(project, held, input.ts.injection_registrations);
|
|
28
28
|
if (!resolved.some(namesADeclaration)) {
|
|
29
29
|
return [];
|
|
30
30
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* call sites that use or discard it, each statement control flow cannot reach, each
|
|
5
5
|
* write to a local that no read reaches, and each case an earlier case covers.
|
|
6
6
|
*/
|
|
7
|
+
import { CODE } from "./contract/contract.generated.js";
|
|
7
8
|
import { isArrowFunction, isAwaitExpression, isBigIntLiteral, isBinaryExpression, isBlock, isBreakStatement, isBindingElement, isCallExpression, isCaseClause, isCatchClause, isClassDeclaration, isClassExpression, isConstructorDeclaration, isContinueStatement, isDecorator, isDefaultClause, isEmptyStatement, isExportAssignment, isExportSpecifier, isExpressionStatement, isFunctionDeclaration, isFunctionExpression, isForInStatement, isForOfStatement, isFunctionLikeDeclaration, isHeritageClause, isIdentifier, isIfStatement, isImportClause, isImportSpecifier, isInterfaceDeclaration, isMethodDeclaration, isModuleDeclaration, isNamespaceImport, isNewExpression, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, isParenthesizedExpression, isPrefixUnaryExpression, isPropertyAssignment, isPropertyAccessExpression, isQualifiedName, isReturnStatement, isShorthandPropertyAssignment, isSourceFile, isStringLiteral, isSwitchStatement, isThrowStatement, isTypeAliasDeclaration, isTypeReferenceNode, isVariableDeclarationList, isVariableStatement, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
8
9
|
import { SignatureKind } from "@typescript/native/sync";
|
|
9
10
|
import { exportsLocally, implicitReads, importBindings, } from "./import-bindings.js";
|
|
@@ -13,12 +14,7 @@ import { importRef } from "./ref.js";
|
|
|
13
14
|
import { isAnswered, UNANSWERED } from "./query.js";
|
|
14
15
|
import { signatureOf } from "./signatures.js";
|
|
15
16
|
import { spellsNoBinding } from "./spelled-names.js";
|
|
16
|
-
|
|
17
|
-
export const UNUSED_PARAMETER = "DS1801";
|
|
18
|
-
export const UNUSED_RESULT = "DS1803";
|
|
19
|
-
const UNREACHABLE_STATEMENT = "DS1805";
|
|
20
|
-
const DEAD_STORE = "DS1807";
|
|
21
|
-
const UNREACHABLE_CASE = "DS1809";
|
|
17
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
22
18
|
/** The result types that carry no value, as the checker spells them. */
|
|
23
19
|
const NO_VALUE = new Set([
|
|
24
20
|
"void",
|
|
@@ -34,17 +30,13 @@ function spanOf(file, root, node) {
|
|
|
34
30
|
const endLine = file.getLineAndCharacterOfPosition(node.end).line + 1;
|
|
35
31
|
return { ...start, endLine };
|
|
36
32
|
}
|
|
37
|
-
/** The parenthesized expression's content, through every pair of parentheses. */
|
|
38
|
-
function unwrapped(node) {
|
|
39
|
-
return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
|
|
40
|
-
}
|
|
41
33
|
/** The node the parentheses around one expression sit in. */
|
|
42
34
|
function outerParent(node) {
|
|
43
35
|
return isParenthesizedExpression(node.parent) ? outerParent(node.parent) : node.parent;
|
|
44
36
|
}
|
|
45
37
|
/** The name a call is made through: the identifier, or the member a property access names. */
|
|
46
38
|
function calleeName(call) {
|
|
47
|
-
const callee =
|
|
39
|
+
const callee = parenthesesRemoved(call.expression);
|
|
48
40
|
if (isIdentifier(callee)) {
|
|
49
41
|
return callee;
|
|
50
42
|
}
|
|
@@ -99,7 +91,7 @@ function unreachableExempt(statement) {
|
|
|
99
91
|
}
|
|
100
92
|
/** The value a case expression is a literal of, as a key two equal literals share, or none. */
|
|
101
93
|
function caseKey(expression) {
|
|
102
|
-
const node =
|
|
94
|
+
const node = parenthesesRemoved(expression);
|
|
103
95
|
if (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node)) {
|
|
104
96
|
return `s:${node.text}`;
|
|
105
97
|
}
|
|
@@ -210,7 +202,7 @@ function extendedNames(files) {
|
|
|
210
202
|
named = isQualifiedName(type.typeName) ? type.typeName.right : type.typeName;
|
|
211
203
|
}
|
|
212
204
|
else {
|
|
213
|
-
const expression =
|
|
205
|
+
const expression = parenthesesRemoved(type.expression);
|
|
214
206
|
named = isPropertyAccessExpression(expression) ? expression.name : expression;
|
|
215
207
|
}
|
|
216
208
|
if (isIdentifier(named)) {
|
|
@@ -254,7 +246,7 @@ function decoratorFixes(constructor) {
|
|
|
254
246
|
}
|
|
255
247
|
/** The name an expression spells a class by: an identifier, or the member a property access names. */
|
|
256
248
|
function spelledName(expression) {
|
|
257
|
-
const node =
|
|
249
|
+
const node = parenthesesRemoved(expression);
|
|
258
250
|
if (isIdentifier(node)) {
|
|
259
251
|
return node;
|
|
260
252
|
}
|
|
@@ -447,8 +439,8 @@ export function projectParts(project, held, references, targetRoot) {
|
|
|
447
439
|
if ((isImportClause(node) || isNamespaceImport(node)) && node.name !== undefined) {
|
|
448
440
|
specifiers.add(position(node.name));
|
|
449
441
|
}
|
|
450
|
-
if (isExportAssignment(node) && isIdentifier(
|
|
451
|
-
specifiers.add(position(
|
|
442
|
+
if (isExportAssignment(node) && isIdentifier(parenthesesRemoved(node.expression))) {
|
|
443
|
+
specifiers.add(position(parenthesesRemoved(node.expression)));
|
|
452
444
|
}
|
|
453
445
|
if (isSwitchStatement(node)) {
|
|
454
446
|
const holder = holderOf(node);
|
|
@@ -572,7 +564,7 @@ export function projectParts(project, held, references, targetRoot) {
|
|
|
572
564
|
}
|
|
573
565
|
walk.catches.forEach(({ name, holder }, at) => {
|
|
574
566
|
parts.push({
|
|
575
|
-
code:
|
|
567
|
+
code: CODE.deadStore,
|
|
576
568
|
declaration: holder,
|
|
577
569
|
kind: "store",
|
|
578
570
|
name: name.text,
|
|
@@ -584,7 +576,7 @@ export function projectParts(project, held, references, targetRoot) {
|
|
|
584
576
|
for (const { names, uses, holder } of loopVariables) {
|
|
585
577
|
for (const name of names) {
|
|
586
578
|
parts.push({
|
|
587
|
-
code:
|
|
579
|
+
code: CODE.deadStore,
|
|
588
580
|
declaration: holder,
|
|
589
581
|
kind: "store",
|
|
590
582
|
name: name.text,
|
|
@@ -602,7 +594,7 @@ export function projectParts(project, held, references, targetRoot) {
|
|
|
602
594
|
});
|
|
603
595
|
for (const { statement, holder } of walk.unreachable) {
|
|
604
596
|
parts.push({
|
|
605
|
-
code:
|
|
597
|
+
code: CODE.unreachableStatement,
|
|
606
598
|
declaration: holder,
|
|
607
599
|
kind: "statement",
|
|
608
600
|
name: "statement",
|
|
@@ -613,7 +605,7 @@ export function projectParts(project, held, references, targetRoot) {
|
|
|
613
605
|
}
|
|
614
606
|
for (const { clause, text, holder } of walk.cases) {
|
|
615
607
|
parts.push({
|
|
616
|
-
code:
|
|
608
|
+
code: CODE.unreachableCase,
|
|
617
609
|
declaration: holder,
|
|
618
610
|
kind: "case",
|
|
619
611
|
name: text,
|
|
@@ -772,10 +764,9 @@ function calleeSteers(project, signature, at) {
|
|
|
772
764
|
}
|
|
773
765
|
/**
|
|
774
766
|
* The parts with each name of `contextual` judged where the compiler gives its function a
|
|
775
|
-
* contextual type:
|
|
776
|
-
*
|
|
777
|
-
*
|
|
778
|
-
* name read.
|
|
767
|
+
* contextual type: a name an object pattern binds is marked as one, and an annotated
|
|
768
|
+
* parameter with whether the call's inference may read its annotation. A question the
|
|
769
|
+
* checker leaves unanswered keeps the name read.
|
|
779
770
|
*/
|
|
780
771
|
function withContextualRead(project, parts, contextual) {
|
|
781
772
|
if (contextual.length === 0) {
|
|
@@ -791,15 +782,17 @@ function withContextualRead(project, parts, contextual) {
|
|
|
791
782
|
continue;
|
|
792
783
|
}
|
|
793
784
|
const place = one.part.parameter;
|
|
794
|
-
judged.set(one.part,
|
|
785
|
+
judged.set(one.part, answer === UNANSWERED || place === undefined
|
|
795
786
|
? { ...one.part, used: true }
|
|
796
|
-
:
|
|
797
|
-
...one.part,
|
|
798
|
-
|
|
799
|
-
...
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
787
|
+
: one.pattern
|
|
788
|
+
? { ...one.part, parameter: { ...place, contextualPattern: true } }
|
|
789
|
+
: {
|
|
790
|
+
...one.part,
|
|
791
|
+
parameter: {
|
|
792
|
+
...place,
|
|
793
|
+
annotated: calleeSteers(project, one.signature, one.at) ? "steers" : "free",
|
|
794
|
+
},
|
|
795
|
+
});
|
|
803
796
|
}
|
|
804
797
|
return parts.map((part) => judged.get(part) ?? part);
|
|
805
798
|
}
|
|
@@ -825,7 +818,7 @@ function parameterParts(file, root, declaration, signature, reader, ids, place)
|
|
|
825
818
|
return;
|
|
826
819
|
}
|
|
827
820
|
const part = {
|
|
828
|
-
code:
|
|
821
|
+
code: CODE.unusedParameter,
|
|
829
822
|
declaration,
|
|
830
823
|
kind: "parameter",
|
|
831
824
|
name: name.text,
|
|
@@ -884,15 +877,28 @@ function functionOf(block) {
|
|
|
884
877
|
}
|
|
885
878
|
}
|
|
886
879
|
/**
|
|
887
|
-
* The locals one block declares with `let`, `const
|
|
888
|
-
* block that spell it. A `var` of a nested
|
|
889
|
-
*
|
|
880
|
+
* The locals one block declares with `let`, `const`, `var` or a function declaration
|
|
881
|
+
* inside a function, each with the names of the block that spell it. A `var` of a nested
|
|
882
|
+
* block is one where no nested function of its function names it, since the function scope
|
|
883
|
+
* holds it past the block's end.
|
|
890
884
|
*/
|
|
891
885
|
function localsOf(block) {
|
|
892
886
|
const declared = [];
|
|
893
887
|
const scope = functionOf(block);
|
|
894
888
|
const outlives = scope?.body !== block;
|
|
895
889
|
block.statements.forEach((statement, at) => {
|
|
890
|
+
if (isFunctionDeclaration(statement)) {
|
|
891
|
+
if (scope !== undefined && statement.name !== undefined && statement.body !== undefined) {
|
|
892
|
+
declared.push({
|
|
893
|
+
name: statement.name,
|
|
894
|
+
statement: at,
|
|
895
|
+
initialized: true,
|
|
896
|
+
outlives: false,
|
|
897
|
+
hoisted: true,
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
896
902
|
if (!isVariableStatement(statement)) {
|
|
897
903
|
return;
|
|
898
904
|
}
|
|
@@ -902,6 +908,11 @@ function localsOf(block) {
|
|
|
902
908
|
return;
|
|
903
909
|
}
|
|
904
910
|
for (const declaration of list.declarations) {
|
|
911
|
+
// A write to a local typed `never` is the compiler's exhaustiveness check, which
|
|
912
|
+
// fails to compile once a case goes unhandled.
|
|
913
|
+
if (declaration.type?.kind === SyntaxKind.NeverKeyword) {
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
905
916
|
for (const name of boundNames(declaration.name)) {
|
|
906
917
|
if (!blockScoped &&
|
|
907
918
|
outlives &&
|
|
@@ -913,6 +924,7 @@ function localsOf(block) {
|
|
|
913
924
|
statement: at,
|
|
914
925
|
initialized: declaration.initializer !== undefined,
|
|
915
926
|
outlives: !blockScoped && outlives,
|
|
927
|
+
hoisted: false,
|
|
916
928
|
});
|
|
917
929
|
}
|
|
918
930
|
}
|
|
@@ -931,7 +943,7 @@ function isPlainWrite(use) {
|
|
|
931
943
|
const parent = outerParent(use);
|
|
932
944
|
return (isBinaryExpression(parent) &&
|
|
933
945
|
parent.operatorToken.kind === SyntaxKind.EqualsToken &&
|
|
934
|
-
|
|
946
|
+
parenthesesRemoved(parent.left) === use);
|
|
935
947
|
}
|
|
936
948
|
/**
|
|
937
949
|
* The writes to the locals of one block that no read reaches. A write is a declaration's
|
|
@@ -944,7 +956,7 @@ function isPlainWrite(use) {
|
|
|
944
956
|
function deadStores(file, root, block, holder, locals, ids) {
|
|
945
957
|
const found = [];
|
|
946
958
|
const deadAt = (local, target) => ({
|
|
947
|
-
code:
|
|
959
|
+
code: CODE.deadStore,
|
|
948
960
|
declaration: holder,
|
|
949
961
|
kind: "store",
|
|
950
962
|
name: local.name.text,
|
|
@@ -953,12 +965,20 @@ function deadStores(file, root, block, holder, locals, ids) {
|
|
|
953
965
|
used: false,
|
|
954
966
|
});
|
|
955
967
|
for (const local of locals) {
|
|
956
|
-
const
|
|
968
|
+
const own = local.hoisted ? block.statements[local.statement] : undefined;
|
|
969
|
+
// A function's reads of itself keep it no more than its absence would.
|
|
970
|
+
const uses = local.uses.filter((use) => readsBinding(use, local.name, ids) && (own === undefined || !within(use, own)));
|
|
957
971
|
if (!local.outlives && uses.every(isPlainWrite)) {
|
|
958
|
-
|
|
972
|
+
// A declaration with no initializer writes `undefined`, the one store left to name.
|
|
973
|
+
const writes = [...(local.initialized ? [local.name] : []), ...uses];
|
|
974
|
+
const targets = writes.length === 0 ? [local.name] : writes;
|
|
959
975
|
found.push(...targets.map((target) => deadAt(local, target)));
|
|
960
976
|
continue;
|
|
961
977
|
}
|
|
978
|
+
// A read anywhere in the block reads a function declaration, which is written first.
|
|
979
|
+
if (local.hoisted) {
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
962
982
|
if (uses.some((use) => inNestedFunction(use, block))) {
|
|
963
983
|
continue;
|
|
964
984
|
}
|
|
@@ -969,12 +989,12 @@ function deadStores(file, root, block, holder, locals, ids) {
|
|
|
969
989
|
if (!isExpressionStatement(statement)) {
|
|
970
990
|
return undefined;
|
|
971
991
|
}
|
|
972
|
-
const expression =
|
|
992
|
+
const expression = parenthesesRemoved(statement.expression);
|
|
973
993
|
if (!isBinaryExpression(expression) ||
|
|
974
994
|
expression.operatorToken.kind !== SyntaxKind.EqualsToken) {
|
|
975
995
|
return undefined;
|
|
976
996
|
}
|
|
977
|
-
const target =
|
|
997
|
+
const target = parenthesesRemoved(expression.left);
|
|
978
998
|
return isIdentifier(target) && uses.includes(target)
|
|
979
999
|
? { target, value: expression.right }
|
|
980
1000
|
: undefined;
|
|
@@ -1052,7 +1072,7 @@ function resultParts(project, root, sites, references, calls) {
|
|
|
1052
1072
|
const valued = project.queries.nonNullable(result);
|
|
1053
1073
|
const members = isAnswered(valued) ? project.queries.propertiesOf(valued) : UNANSWERED;
|
|
1054
1074
|
found.push({
|
|
1055
|
-
code:
|
|
1075
|
+
code: CODE.unusedResult,
|
|
1056
1076
|
declaration: id,
|
|
1057
1077
|
kind: "result",
|
|
1058
1078
|
name: "result",
|