@cplieger/deadset-ts 7.0.0-dev.2 → 7.0.0-dev.4
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/src/alias-chain.js +2 -2
- package/dist/src/analysis.js +89 -44
- package/dist/src/augmentations.js +14 -12
- package/dist/src/calls.js +2 -2
- package/dist/src/class-members.js +1 -1
- package/dist/src/components.js +62 -20
- package/dist/src/configuration-files.js +6 -6
- package/dist/src/configured-declarations.js +2 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +71 -14
- package/dist/src/convention-rows.js +6 -2
- package/dist/src/conventions.js +4 -2
- package/dist/src/dependencies.js +81 -27
- package/dist/src/deprecation.js +1 -1
- package/dist/src/destructuring.js +2 -2
- package/dist/src/discover.js +1 -1
- package/dist/src/element-access.js +341 -0
- package/dist/src/entry-points.js +2 -2
- package/dist/src/enum-group.js +2 -2
- package/dist/src/erased-stores.js +317 -0
- package/dist/src/findings/completion.js +46 -15
- package/dist/src/findings/dependencies-and-module-machinery.js +1 -1
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/self-check.js +17 -4
- package/dist/src/findings-pass.js +6 -5
- package/dist/src/framework-lifecycle.js +1 -1
- package/dist/src/html-entries.js +1 -1
- package/dist/src/implementations.js +2 -2
- package/dist/src/in-operands.js +30 -0
- package/dist/src/indexed-access.js +264 -125
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +2 -2
- package/dist/src/intra-function-parts.js +26 -12
- package/dist/src/inventory.js +26 -1
- package/dist/src/keyof-comparisons.js +170 -0
- package/dist/src/kinds.js +17 -7
- package/dist/src/manifest.js +6 -16
- package/dist/src/matrix.js +20 -2
- package/dist/src/member-names.js +1 -1
- package/dist/src/module-declarations.js +1 -1
- package/dist/src/namespace-objects.js +1 -1
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +2 -2
- package/dist/src/query.js +4 -1
- package/dist/src/references.js +13 -2
- package/dist/src/reflective-lookup.js +2 -2
- package/dist/src/report.js +1 -1
- package/dist/src/reporters/sarif.js +4 -0
- package/dist/src/roots.js +1 -1
- package/dist/src/serialization-contract.js +45 -16
- package/dist/src/session.js +15 -8
- package/dist/src/signatures.js +1 -1
- package/dist/src/stores.js +1 -1
- package/dist/src/suppress-file.js +58 -6
- package/dist/src/suppress-inline.js +14 -5
- package/dist/src/suppress.js +54 -24
- package/dist/src/symbol-keys.js +1 -1
- package/dist/src/type-errors.js +1 -1
- package/dist/src/type-query-alias.js +1 -1
- package/dist/src/unimplemented-callees.js +312 -0
- package/dist/src/value-flow.js +1 -1
- package/dist/src/value-reach.js +8 -8
- package/dist/src/verbs/analyze.js +11 -9
- package/dist/src/workspace-programs.js +2 -2
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +2 -2
- package/src/analysis.ts +135 -52
- package/src/augmentations.ts +14 -11
- package/src/calls.ts +2 -2
- package/src/class-members.ts +2 -2
- package/src/component-layer.ts +1 -1
- package/src/components.ts +103 -21
- package/src/configuration-files.ts +6 -5
- package/src/configured-declarations.ts +2 -6
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +82 -18
- package/src/convention-rows.ts +6 -2
- package/src/conventions.ts +11 -2
- package/src/decorator.ts +1 -1
- package/src/dependencies.ts +108 -31
- package/src/deprecation.ts +1 -1
- package/src/destructuring.ts +2 -2
- package/src/discover.ts +2 -2
- package/src/element-access.ts +446 -0
- package/src/entry-points.ts +2 -2
- package/src/enum-group.ts +2 -2
- package/src/erased-stores.ts +413 -0
- package/src/file-facts.ts +3 -1
- package/src/findings/completion.ts +54 -20
- package/src/findings/dependencies-and-module-machinery.ts +1 -1
- package/src/findings/emitters.ts +1 -1
- package/src/findings/self-check.ts +18 -4
- package/src/findings-pass.ts +12 -5
- package/src/framework-lifecycle.ts +2 -2
- package/src/html-entries.ts +1 -1
- package/src/implementations.ts +2 -2
- package/src/in-operands.ts +45 -0
- package/src/indexed-access.ts +304 -146
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +2 -2
- package/src/intra-function-parts.ts +42 -17
- package/src/inventory.ts +34 -2
- package/src/keyof-comparisons.ts +211 -0
- package/src/kinds.ts +20 -7
- package/src/manifest.ts +6 -21
- package/src/matrix.ts +34 -2
- package/src/member-names.ts +1 -1
- package/src/module-declarations.ts +1 -1
- package/src/namespace-objects.ts +1 -1
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +2 -2
- package/src/position.ts +1 -1
- package/src/query.ts +12 -2
- package/src/references.ts +25 -3
- package/src/reflective-lookup.ts +2 -2
- package/src/report.ts +3 -1
- package/src/reporters/sarif.ts +4 -0
- package/src/roots.ts +2 -2
- package/src/serialization-contract.ts +55 -16
- package/src/session.ts +39 -32
- package/src/signatures.ts +1 -1
- package/src/source-files.ts +1 -1
- package/src/stores.ts +1 -1
- package/src/suppress-file.ts +68 -5
- package/src/suppress-inline.ts +15 -5
- package/src/suppress.ts +63 -28
- package/src/symbol-keys.ts +2 -2
- package/src/type-errors.ts +2 -2
- package/src/type-query-alias.ts +1 -1
- package/src/unimplemented-callees.ts +411 -0
- package/src/value-flow.ts +1 -1
- package/src/value-reach.ts +12 -8
- package/src/verbs/analyze.ts +15 -9
- package/src/workflow-steps.ts +1 -1
- package/src/workspace-programs.ts +4 -4
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
deadset-ts finds dead code in TypeScript projects, down to class members, type members and dead stores inside functions, and writes a report your CI can fail on.
|
|
6
6
|
|
|
7
|
-
It reads your `tsconfig.json` projects, `allowJs` JavaScript and Vue, Svelte and Astro scripts included, with the type information of TypeScript 7.1.0-dev.
|
|
7
|
+
It reads your `tsconfig.json` projects, `allowJs` JavaScript and Vue, Svelte and Astro scripts included, with the type information of TypeScript 7.1.0-dev.20261010.1, its one dependency. Your project keeps its own TypeScript version. A type error under that version skips the function or statement that holds it. deadset-ts reports and never edits your code. It is pre-release, tested only on Linux, needs Node.js 24 or later and is licensed under GPL-3.0-or-later.
|
|
8
8
|
|
|
9
9
|
## Why use it
|
|
10
10
|
|
package/dist/src/alias-chain.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* declaration of the target it is, and along the chain of aliases it heads to each
|
|
4
4
|
* declaration every link of that chain is.
|
|
5
5
|
*/
|
|
6
|
-
import { isExportAssignment, isExportSpecifier, isIdentifier, isImportClause, isImportSpecifier, isNamespaceExport, isNamespaceImport, } from "@typescript/native/
|
|
7
|
-
import { SymbolFlags } from "@typescript/native/
|
|
6
|
+
import { isExportAssignment, isExportSpecifier, isIdentifier, isImportClause, isImportSpecifier, isNamespaceExport, isNamespaceImport, } from "@typescript/native/ast";
|
|
7
|
+
import { SymbolFlags } from "@typescript/native/sync";
|
|
8
8
|
import { declarationsByName, nodeKey } from "./inventory.js";
|
|
9
9
|
import { UNANSWERED } from "./query.js";
|
|
10
10
|
/**
|
package/dist/src/analysis.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One run's passes, composed: the projects discovery names, opened in one snapshot
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* carries a project's identity in its type so a handle cannot cross to another
|
|
8
|
-
* project's checker, and that holds only for views the session itself hands out.
|
|
2
|
+
* One run's passes, composed: the projects discovery names, opened in one snapshot of one
|
|
3
|
+
* client, each read through the stages in dependency order, and the per-project answers
|
|
4
|
+
* merged into the run's. The stages take a project's view, which only this composition
|
|
5
|
+
* makes: a view carries a project's identity in its type so a handle cannot cross to
|
|
6
|
+
* another project's checker, which holds only for views the session hands out.
|
|
9
7
|
*/
|
|
10
8
|
import { lineOf, readComponent } from "./component-files.js";
|
|
11
9
|
import { consumerLoads, consumerReferences, moduleIdentity, } from "./consumers.js";
|
|
@@ -18,6 +16,7 @@ import { conventionFailuresFor, generatedFiles, readConventions, } from "./conve
|
|
|
18
16
|
import { readEdgeSides } from "./edges.js";
|
|
19
17
|
import { enumGroup } from "./enum-group.js";
|
|
20
18
|
import { generatedFile, generatedFilesOf } from "./generated-file.js";
|
|
19
|
+
import { inOperandNames } from "./in-operands.js";
|
|
21
20
|
import { sourceFilesOf } from "./source-files.js";
|
|
22
21
|
import { evidenceRecords, heldRecords, disabledClasses, exemptionsOf, holdingWhile, isUnansweredRecord, retainedIn, } from "./exempt.js";
|
|
23
22
|
import { heldByInclusion, sourcePaths, sourceTree } from "./file-facts.js";
|
|
@@ -38,12 +37,12 @@ import { SetupError, setupLine } from "./setup-failure.js";
|
|
|
38
37
|
import { pathsOf, readErrors, skippedRoots, skippedUnits, } from "./type-errors.js";
|
|
39
38
|
import { reflectiveLookup } from "./reflective-lookup.js";
|
|
40
39
|
import { references, testFileRulesOf } from "./references.js";
|
|
41
|
-
import { roots, unmatchedEverywhere } from "./roots.js";
|
|
40
|
+
import { roots, unmatchedEverywhere, } from "./roots.js";
|
|
42
41
|
import { serializationContract } from "./serialization-contract.js";
|
|
43
42
|
import { outsideTestReferences, testFilesAt } from "./outside-tests.js";
|
|
44
43
|
import { joinedTestFiles, withJoinedTests } from "./test-joins.js";
|
|
45
44
|
import { supportReferences, testSupportFiles } from "./test-support.js";
|
|
46
|
-
import { diagnosticsOf, ownedAs, runSession } from "./session.js";
|
|
45
|
+
import { diagnosticsOf, ownedAs, runSession, } from "./session.js";
|
|
47
46
|
import { accessorsOf, storesOf } from "./stores.js";
|
|
48
47
|
import { isPartKind } from "./suppress.js";
|
|
49
48
|
import { bindRows, readBaseline, readIgnoreFile } from "./suppress-file.js";
|
|
@@ -75,21 +74,47 @@ function compare(a, b) {
|
|
|
75
74
|
}
|
|
76
75
|
/**
|
|
77
76
|
* The manifests the analysis reads, each once: the target's own, then every workspace
|
|
78
|
-
* member's below the target root.
|
|
77
|
+
* member's and every nested package's below the target root.
|
|
79
78
|
*/
|
|
80
|
-
function conventionManifests(host, targetRoot, memberDirs) {
|
|
79
|
+
function conventionManifests(host, targetRoot, memberDirs, nestedDirs) {
|
|
81
80
|
const found = new Map();
|
|
82
81
|
if (host.kindOf(joinPath(targetRoot, "package.json")) === "file") {
|
|
83
82
|
found.set(targetRoot, { dir: targetRoot, path: "package.json" });
|
|
84
83
|
}
|
|
85
|
-
for (const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
for (const [dirs, nested] of [
|
|
85
|
+
[memberDirs, false],
|
|
86
|
+
[nestedDirs, true],
|
|
87
|
+
]) {
|
|
88
|
+
for (const dir of dirs) {
|
|
89
|
+
const below = relativePath(targetRoot, dir);
|
|
90
|
+
if (below !== undefined && !found.has(dir)) {
|
|
91
|
+
found.set(dir, { dir, path: `${below}/package.json`, nested });
|
|
92
|
+
}
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
95
|
return [...found.values()];
|
|
92
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* The nested packages of a run: each directory below the target root, in no `node_modules`
|
|
99
|
+
* directory and none of `known`, that holds a `package.json` and lies at or above one of
|
|
100
|
+
* `files`, the program's source files and the compiler configuration files, in path order.
|
|
101
|
+
*/
|
|
102
|
+
function nestedPackageDirs(host, targetRoot, files, known) {
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
const found = [];
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
for (let dir = dirnamePath(file); dir !== targetRoot && !seen.has(dir) && relativePath(targetRoot, dir) !== undefined; dir = dirnamePath(dir)) {
|
|
107
|
+
seen.add(dir);
|
|
108
|
+
const below = relativePath(targetRoot, dir) ?? "";
|
|
109
|
+
if (!below.split("/").includes("node_modules") &&
|
|
110
|
+
!known.has(dir) &&
|
|
111
|
+
host.kindOf(joinPath(dir, "package.json")) === "file") {
|
|
112
|
+
found.push(dir);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return found.sort(compare);
|
|
117
|
+
}
|
|
93
118
|
/**
|
|
94
119
|
* Reads every project the scope discovers, and each declared consumer's, in one snapshot,
|
|
95
120
|
* each checked for errors first. `stage` is handed a target project and enumerates and roots
|
|
@@ -101,26 +126,41 @@ function conventionManifests(host, targetRoot, memberDirs) {
|
|
|
101
126
|
function readProjects(engine, host, scope, config, stage) {
|
|
102
127
|
const targetRoot = scope.target.path;
|
|
103
128
|
const discovered = discoverProjects(engine, host, scope, config.analysis.configurations);
|
|
129
|
+
if (discovered.configFiles.length === 0 && discovered.notBuilt.length === 0) {
|
|
130
|
+
throw new DiscoveryError(`${targetRoot} holds no compiler configuration, so the run holds no program`, []);
|
|
131
|
+
}
|
|
104
132
|
const ids = new Map(discovered.projects.map((project) => [project.configFile, project.id]));
|
|
105
133
|
const loads = consumerLoads(engine, host, scope, discovered.configFiles);
|
|
106
134
|
const consumerOf = new Map(loads.flatMap((load) => load.configFiles.map((configFile) => [configFile, load.consumer])));
|
|
107
135
|
const absoluteRoot = resolvePath(host.workingDirectory(), targetRoot);
|
|
108
136
|
const workspace = workspaceRun(engine, host, absoluteRoot, discovered.configFiles, readManifest(host, targetRoot));
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
137
|
+
const parse = (fileName, text) => engine.parseSourceFile(fileName, text);
|
|
138
|
+
const memberDirs = workspace?.memberDirs ?? [];
|
|
139
|
+
// The nested packages are known only once every program is open, and every visit reads
|
|
140
|
+
// the conventions and options they decide, so both are set in the session's first step.
|
|
141
|
+
let nestedDirs = [];
|
|
142
|
+
let conventions = readConventions(host, parse, [], config.ts.disabledConventions);
|
|
143
|
+
let options;
|
|
144
|
+
const readManifests = (opened) => {
|
|
145
|
+
nestedDirs = nestedPackageDirs(host, normalizePath(absoluteRoot), opened
|
|
146
|
+
.filter((one) => !consumerOf.has(one.configFile))
|
|
147
|
+
.flatMap((one) => [one.configFile, ...one.fileNames()]), new Set(memberDirs));
|
|
148
|
+
conventions = readConventions(host, parse, conventionManifests(host, absoluteRoot, memberDirs, nestedDirs), config.ts.disabledConventions);
|
|
149
|
+
options = {
|
|
150
|
+
host,
|
|
151
|
+
manifest: workspace?.manifest ?? readManifest(host, targetRoot),
|
|
152
|
+
otherPackages: [
|
|
153
|
+
...(workspace?.entries ?? []),
|
|
154
|
+
...packageEntries(host, normalizePath(absoluteRoot), nestedDirs),
|
|
155
|
+
],
|
|
156
|
+
patterns: config.rootPatterns,
|
|
157
|
+
entryFiles: config.ts.entryFiles,
|
|
158
|
+
conventions,
|
|
159
|
+
testFiles: config.ts.testFiles,
|
|
160
|
+
parse,
|
|
161
|
+
publishedAPI: config.targetKind === "library",
|
|
162
|
+
publishedMembers: new Set((workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir)),
|
|
163
|
+
};
|
|
124
164
|
};
|
|
125
165
|
const notBuilt = [...discovered.notBuilt];
|
|
126
166
|
const dropped = (configFile, error) => {
|
|
@@ -253,8 +293,8 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
253
293
|
}, discovered.derived ? "record" : "refuse", joins.size === 0
|
|
254
294
|
? workspace?.programs
|
|
255
295
|
: withJoinedTests(engine, host, workspace?.programs ?? new Map(), joins), () => {
|
|
256
|
-
outsideTests = testFilesAt(host, targetRoot, sourcePaths(host, targetRoot).filter((path) => !heldFiles.has(path) && testPatterns.some((pattern) => pattern.test(path))),
|
|
257
|
-
});
|
|
296
|
+
outsideTests = testFilesAt(host, targetRoot, sourcePaths(host, targetRoot).filter((path) => !heldFiles.has(path) && testPatterns.some((pattern) => pattern.test(path))), parse);
|
|
297
|
+
}, readManifests);
|
|
258
298
|
for (const configFile of session.unopened) {
|
|
259
299
|
if (consumerOf.has(configFile) || !isGuess(discovered, configFile)) {
|
|
260
300
|
throw new DiscoveryError(`${configFile} opened no project`, []);
|
|
@@ -286,7 +326,8 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
286
326
|
componentsUnread: workspace?.componentsUnread ?? [],
|
|
287
327
|
conventionsApplied: conventions.applied,
|
|
288
328
|
conventionUses: conventions.uses,
|
|
289
|
-
memberDirs
|
|
329
|
+
memberDirs,
|
|
330
|
+
nestedDirs,
|
|
290
331
|
outsideTests,
|
|
291
332
|
};
|
|
292
333
|
}
|
|
@@ -401,7 +442,7 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
401
442
|
line: lineOf(file.originalText, warning.offset),
|
|
402
443
|
reason: warning.reason,
|
|
403
444
|
}))),
|
|
404
|
-
extra: extra(project, projectRead, resolved.references),
|
|
445
|
+
extra: extra(project, projectRead, resolved.references, resolved.testFilePaths),
|
|
405
446
|
};
|
|
406
447
|
});
|
|
407
448
|
const outsidePaths = read.outsideTests.map((one) => one.path);
|
|
@@ -438,6 +479,9 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
438
479
|
...(projectRead.referenceOnly.length === 0
|
|
439
480
|
? {}
|
|
440
481
|
: { referenceOnly: projectRead.referenceOnly }),
|
|
482
|
+
...(projectRead.held.dependents.size === 0
|
|
483
|
+
? {}
|
|
484
|
+
: { dependents: projectRead.held.dependents }),
|
|
441
485
|
};
|
|
442
486
|
return {
|
|
443
487
|
configFile: projectRead.configFile,
|
|
@@ -485,6 +529,7 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
485
529
|
conventionsApplied: read.conventionsApplied,
|
|
486
530
|
conventionUses: read.conventionUses,
|
|
487
531
|
memberDirs: read.memberDirs,
|
|
532
|
+
nestedDirs: read.nestedDirs,
|
|
488
533
|
};
|
|
489
534
|
}
|
|
490
535
|
/**
|
|
@@ -611,14 +656,12 @@ function sweptOf(read, input) {
|
|
|
611
656
|
};
|
|
612
657
|
}
|
|
613
658
|
/**
|
|
614
|
-
* Sweeps the run the scope and the configuration describe. Each project's references
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
* configuration is a use. A project that carries an error ends the run before any
|
|
621
|
-
* project is swept.
|
|
659
|
+
* Sweeps the run the scope and the configuration describe. Each project's references and
|
|
660
|
+
* exemptions are read against its own inventory while its view is open; the projects then
|
|
661
|
+
* merge into one matrix in run order, swept under the caller's marks and mode with every
|
|
662
|
+
* project's exemptions. A record found in one configuration holds in every configuration
|
|
663
|
+
* that holds its declaration, because an exemption evidences a use the analysis cannot see.
|
|
664
|
+
* A project that carries an error ends the run before any project is swept.
|
|
622
665
|
*/
|
|
623
666
|
export function runSweep(engine, host, scope, config, input, detectors = DETECTORS) {
|
|
624
667
|
return sweptOf(readRun(engine, host, scope, config, input.mode, detectors, () => undefined), input);
|
|
@@ -626,13 +669,14 @@ export function runSweep(engine, host, scope, config, input, detectors = DETECTO
|
|
|
626
669
|
/** Reads a run's projects with every per-project fact an emitter reads. */
|
|
627
670
|
export function readEmitterRun(engine, host, scope, config, mode, detectors) {
|
|
628
671
|
const targetRoot = scope.target.path;
|
|
629
|
-
const run = readRun(engine, host, scope, config, mode, detectors, (project, read, references) => ({
|
|
672
|
+
const run = readRun(engine, host, scope, config, mode, detectors, (project, read, references, testFiles) => ({
|
|
630
673
|
deprecation: deprecatedDeclarations(project, read.held),
|
|
631
674
|
accessors: accessorsOf(project, read.held),
|
|
632
675
|
needs: projectNeeds(project, read.held, host, targetRoot, (fileName, text) => engine.parseSourceFile(fileName, text)),
|
|
633
676
|
implementations: implementations(project, read.held),
|
|
634
677
|
heldByInclusion: heldByInclusion(project, targetRoot),
|
|
635
678
|
generated: [...read.generated.keys()],
|
|
679
|
+
inOperands: inOperandNames(project, targetRoot, testFiles),
|
|
636
680
|
rooted: read.rooted,
|
|
637
681
|
directives: inlineDirectives(project, read.held, targetRoot).filter((directive) => !read.referenceOnly.includes(directive.site.path)),
|
|
638
682
|
declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
|
|
@@ -696,12 +740,13 @@ export function emitterInputOver(host, scope, config, read, input, selfCheck) {
|
|
|
696
740
|
references: one.configured.references,
|
|
697
741
|
accessors: one.extra.accessors,
|
|
698
742
|
})), swept.exempt, input.mode),
|
|
699
|
-
dependencies: dependenciesOf(host, targetRoot, [...projects.map((one) => one.extra.needs), configurationNeeds(read.conventionUses)], swept.sweep.candidates.map((candidate) => candidate.id), members.read, members.unbuilt),
|
|
743
|
+
dependencies: dependenciesOf(host, targetRoot, [...projects.map((one) => one.extra.needs), configurationNeeds(read.conventionUses)], swept.sweep.candidates.map((candidate) => candidate.id), members.read, members.unbuilt, read.nestedDirs),
|
|
700
744
|
implementations: mergeImplementations(projects.map((one) => one.extra.implementations)),
|
|
701
745
|
files: {
|
|
702
746
|
tree: sourceTree(host, targetRoot),
|
|
703
747
|
heldByInclusion: new Set(projects.flatMap((one) => one.extra.heldByInclusion)),
|
|
704
748
|
generated: new Set(projects.flatMap((one) => one.extra.generated)),
|
|
749
|
+
inOperands: new Set(projects.flatMap((one) => one.extra.inOperands)),
|
|
705
750
|
},
|
|
706
751
|
boundary: {
|
|
707
752
|
consumers: {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The references a use of an augmentation's declaration makes to what holds it.
|
|
3
|
-
*
|
|
4
|
-
* block
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The references a use of an augmentation's declaration makes to what holds it. A use of
|
|
3
|
+
* a declaration a `declare global` block or a module declaration holds is a use of the
|
|
4
|
+
* block, which no name spells or no import may name. An interface or namespace such a
|
|
5
|
+
* block, or the top level of a file that is no module, holds merges with the one of its
|
|
6
|
+
* name, which the language or a library types values by where the target never names it,
|
|
7
|
+
* as `import.meta` or `process.env`, so a use of its member is a use of it.
|
|
7
8
|
*/
|
|
8
9
|
/** Whether one declaration is a `declare global` block: a module-level namespace named `global`. */
|
|
9
10
|
function isGlobalBlock(symbol, held, files) {
|
|
@@ -13,24 +14,25 @@ function isGlobalBlock(symbol, held, files) {
|
|
|
13
14
|
!held.modules.has(symbol.id));
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
|
-
* For each reference to a declaration an augmentation block
|
|
17
|
-
*
|
|
18
|
-
* itself.
|
|
17
|
+
* For each reference to a declaration an augmentation block, or the global scope of a
|
|
18
|
+
* file in `scripts`, holds, a read at the same place of every interface and namespace
|
|
19
|
+
* between it and the block or the file, and of the block itself. `scripts` holds the
|
|
20
|
+
* files that are no module, by identifier.
|
|
19
21
|
*/
|
|
20
|
-
export function augmentationReferences(held, found) {
|
|
22
|
+
export function augmentationReferences(held, found, scripts = new Set()) {
|
|
21
23
|
const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
|
|
22
24
|
const globals = new Set(held.symbols.filter((one) => isGlobalBlock(one, held, files)).map((one) => one.id));
|
|
23
|
-
if (globals.size === 0 && held.modules.size === 0) {
|
|
25
|
+
if (globals.size === 0 && held.modules.size === 0 && scripts.size === 0) {
|
|
24
26
|
return [];
|
|
25
27
|
}
|
|
26
28
|
const byId = new Map(held.symbols.map((one) => [one.id, one]));
|
|
27
29
|
const holders = (id) => {
|
|
28
30
|
const ids = [];
|
|
29
31
|
for (let at = byId.get(byId.get(id)?.parent ?? ""); at !== undefined; at = byId.get(at.parent)) {
|
|
30
|
-
if (globals.has(at.id)) {
|
|
32
|
+
if (globals.has(at.id) || held.modules.has(at.id)) {
|
|
31
33
|
return [...ids, at.id];
|
|
32
34
|
}
|
|
33
|
-
if (
|
|
35
|
+
if (scripts.has(at.id)) {
|
|
34
36
|
return ids;
|
|
35
37
|
}
|
|
36
38
|
if (at.kind === "interface" || at.kind === "namespace") {
|
package/dist/src/calls.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* literal or a property value of an object literal written as one, at any depth, and a
|
|
6
6
|
* decorator written as a call is a call.
|
|
7
7
|
*/
|
|
8
|
-
import { isArrayLiteralExpression, isCallExpression, isClassDeclaration, isDecorator, isIdentifier, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadElement, } from "@typescript/native/
|
|
9
|
-
import { SymbolFlags } from "@typescript/native/
|
|
8
|
+
import { isArrayLiteralExpression, isCallExpression, isClassDeclaration, isDecorator, isIdentifier, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadElement, } from "@typescript/native/ast";
|
|
9
|
+
import { SymbolFlags } from "@typescript/native/sync";
|
|
10
10
|
import { nodeKey } from "./inventory.js";
|
|
11
11
|
import { must } from "./query.js";
|
|
12
12
|
/** The name node a callee or a decorator expression resolves through, where it has one. */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* for the class's type, so a member a base class declares is a member of every class
|
|
4
4
|
* that extends it and a member a subclass overrides is the subclass's own.
|
|
5
5
|
*/
|
|
6
|
-
import { isClassDeclaration, isClassExpression } from "@typescript/native/
|
|
6
|
+
import { isClassDeclaration, isClassExpression } from "@typescript/native/ast";
|
|
7
7
|
import { nodeKey } from "./inventory.js";
|
|
8
8
|
import { must } from "./query.js";
|
|
9
9
|
/** The name node of each class declaration of the target, by the class's identifier. */
|
package/dist/src/components.js
CHANGED
|
@@ -26,34 +26,74 @@ export function listing(component, cascade) {
|
|
|
26
26
|
/**
|
|
27
27
|
* Groups the declarations `dead` marks into components, ordered by their first member's
|
|
28
28
|
* site; `dead`, `testOfDeadCode` and `unreferencedTest` hold one flag per declaration of
|
|
29
|
-
* the graph. Root members sit on the cycles no other cycle references. A
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* the graph. Root members sit on the cycles no other cycle references. A component is a
|
|
30
|
+
* weakly connected part of the dead subgraph, because a cycle two roots reach is dead only
|
|
31
|
+
* through both. The held declarations `positional` decides form components of their own,
|
|
32
|
+
* and a dead declaration that is not held shares one with each of its dead dependents.
|
|
32
33
|
*/
|
|
33
|
-
export function componentsOf(graph, dead, testOfDeadCode, unreferencedTest = []) {
|
|
34
|
-
const { at, adjacent } = deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest);
|
|
34
|
+
export function componentsOf(graph, dead, testOfDeadCode, unreferencedTest = [], positional = { dependents: [], live: [] }) {
|
|
35
|
+
const { at, position, adjacent, references } = deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest);
|
|
35
36
|
if (at.length === 0) {
|
|
36
37
|
return [];
|
|
37
38
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const dependents = at.map((index) => (positional.dependents[index] ?? []).map((to) => ({
|
|
40
|
+
to: position[to] ?? UNVISITED,
|
|
41
|
+
live: positional.live[to] === true,
|
|
42
|
+
})));
|
|
43
|
+
const held = heldSet(dependents, references);
|
|
44
|
+
const kept = adjacent.map((targets, from) => targets.filter((to) => held[to] === held[from]));
|
|
45
|
+
const { componentOf, members } = connected(kept);
|
|
46
|
+
const referenced = referencedCycles(kept, componentOf, members.length);
|
|
47
|
+
const groups = joined(kept.map((targets, from) => [
|
|
48
|
+
...targets,
|
|
49
|
+
...(held[from] === true
|
|
50
|
+
? []
|
|
51
|
+
: (dependents[from] ?? []).flatMap(({ to }) => to === UNVISITED || held[to] === true ? [] : [to])),
|
|
52
|
+
]));
|
|
53
|
+
const idOf = (place) => graph.symbols[at[place] ?? 0]?.id ?? "";
|
|
42
54
|
return groups.map((group, place) => {
|
|
43
|
-
const roots = group.filter((
|
|
55
|
+
const roots = group.filter((one) => {
|
|
44
56
|
// A dead member's dead container is on the member's own cycle, and the container
|
|
45
57
|
// is where the deletion starts.
|
|
46
|
-
const container = graph.parent[at[
|
|
47
|
-
return referenced[componentOf[
|
|
58
|
+
const container = graph.parent[at[one] ?? 0] ?? OUTSIDE;
|
|
59
|
+
return referenced[componentOf[one] ?? 0] !== true && dead[container] !== true;
|
|
48
60
|
});
|
|
61
|
+
const isHeld = group.every((one) => held[one] === true);
|
|
49
62
|
return {
|
|
50
63
|
id: componentId(place + 1),
|
|
51
64
|
members: group.map(idOf),
|
|
52
65
|
roots: roots.map(idOf),
|
|
53
|
-
deletableLines: linesSpanned(group.map((
|
|
66
|
+
deletableLines: isHeld ? 0 : linesSpanned(group.map((one) => graph.symbols[at[one] ?? 0])),
|
|
67
|
+
held: isHeld,
|
|
54
68
|
};
|
|
55
69
|
});
|
|
56
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Per place of the dead subgraph, whether the declaration is held: one with a live
|
|
73
|
+
* dependent, and each dead declaration a held one references or has as a dependent,
|
|
74
|
+
* until no further one joins.
|
|
75
|
+
*/
|
|
76
|
+
function heldSet(dependents, references) {
|
|
77
|
+
const held = dependents.map((after) => after.some((one) => one.live));
|
|
78
|
+
const before = dependents.map(() => []);
|
|
79
|
+
dependents.forEach((after, from) => {
|
|
80
|
+
for (const { to } of after) {
|
|
81
|
+
if (to !== UNVISITED) {
|
|
82
|
+
before[to]?.push(from);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const pending = held.flatMap((one, place) => (one ? [place] : []));
|
|
87
|
+
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
|
88
|
+
for (const reached of [...(references[next] ?? []), ...(before[next] ?? [])]) {
|
|
89
|
+
if (held[reached] !== true) {
|
|
90
|
+
held[reached] = true;
|
|
91
|
+
pending.push(reached);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return held;
|
|
96
|
+
}
|
|
57
97
|
/**
|
|
58
98
|
* The distinct source lines a set of declarations spans, each declaration at least the
|
|
59
99
|
* line it starts on. A member written inside its container's span adds no line, because
|
|
@@ -89,12 +129,12 @@ function linesSpanned(symbols) {
|
|
|
89
129
|
return total;
|
|
90
130
|
}
|
|
91
131
|
/**
|
|
92
|
-
* The dead declarations by
|
|
93
|
-
*
|
|
94
|
-
* each reference one makes to another, except an unreferenced test-file declaration's
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
132
|
+
* The dead declarations by subgraph place (`at`, and `position` per graph declaration),
|
|
133
|
+
* a test file's only where it tests dead code or nothing the tests reach. `adjacent` holds
|
|
134
|
+
* each reference one makes to another, except an unreferenced test-file declaration's to
|
|
135
|
+
* production code, which decides nothing of its target; an edge each way between a dead
|
|
136
|
+
* member and its dead container; and one back from each production target of an admitted
|
|
137
|
+
* test, which puts the test in its subject's component. `references` holds the first alone.
|
|
98
138
|
*/
|
|
99
139
|
function deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest) {
|
|
100
140
|
const at = [];
|
|
@@ -107,6 +147,7 @@ function deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest) {
|
|
|
107
147
|
return at.length - 1;
|
|
108
148
|
});
|
|
109
149
|
const adjacent = at.map(() => []);
|
|
150
|
+
const references = at.map(() => []);
|
|
110
151
|
at.forEach((index, from) => {
|
|
111
152
|
for (const edge of graph.out[index] ?? []) {
|
|
112
153
|
const to = position[edge.to] ?? UNVISITED;
|
|
@@ -114,6 +155,7 @@ function deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest) {
|
|
|
114
155
|
continue;
|
|
115
156
|
}
|
|
116
157
|
adjacent[from]?.push(to);
|
|
158
|
+
references[from]?.push(to);
|
|
117
159
|
if (testOfDeadCode[index] === true &&
|
|
118
160
|
graph.test[edge.to] !== true &&
|
|
119
161
|
graph.subject[edge.to] === true) {
|
|
@@ -126,7 +168,7 @@ function deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest) {
|
|
|
126
168
|
adjacent[container]?.push(from);
|
|
127
169
|
}
|
|
128
170
|
});
|
|
129
|
-
return { at, adjacent };
|
|
171
|
+
return { at, position, adjacent, references };
|
|
130
172
|
}
|
|
131
173
|
/**
|
|
132
174
|
* Each declaration's cycle and each cycle's members by site, by Tarjan's
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* every string either holds as it likes, so each one that spells a declared dependency
|
|
7
7
|
* uses it and each relative one that names a source file of the program roots the file.
|
|
8
8
|
*/
|
|
9
|
-
import { isExportDeclaration, isImportDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/native/
|
|
9
|
+
import { isExportDeclaration, isImportDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/native/ast";
|
|
10
10
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
11
11
|
const MANIFEST = "package.json";
|
|
12
12
|
/** A configuration module's name, each form with the spelling a root reports it by. */
|
|
@@ -341,7 +341,7 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
341
341
|
documents: [...documents.values()],
|
|
342
342
|
outside: parse === undefined
|
|
343
343
|
? []
|
|
344
|
-
: outsideModules(host, [...dirs], files, holdsManifest, parse, named),
|
|
344
|
+
: outsideModules(host, root, [...dirs], files, holdsManifest, parse, named),
|
|
345
345
|
};
|
|
346
346
|
}
|
|
347
347
|
/** The relative specifiers one file's imports and re-exports name. */
|
|
@@ -386,10 +386,10 @@ export function resolveRelativeSpecifier(host, dir, specifier) {
|
|
|
386
386
|
}
|
|
387
387
|
/**
|
|
388
388
|
* The configuration modules of the directories that hold a manifest which the program
|
|
389
|
-
* does not hold, and the files they import by a relative specifier,
|
|
390
|
-
* file joins. A file that cannot be read is skipped.
|
|
389
|
+
* does not hold, and the project files below `root` they import by a relative specifier,
|
|
390
|
+
* until no further file joins. A file that cannot be read is skipped.
|
|
391
391
|
*/
|
|
392
|
-
function outsideModules(host, dirs, files, holdsManifest, parse, named) {
|
|
392
|
+
function outsideModules(host, root, dirs, files, holdsManifest, parse, named) {
|
|
393
393
|
const pending = [...named];
|
|
394
394
|
for (const dir of [...dirs].sort()) {
|
|
395
395
|
if (!holdsManifest(dir)) {
|
|
@@ -432,7 +432,7 @@ function outsideModules(host, dirs, files, holdsManifest, parse, named) {
|
|
|
432
432
|
outside.manifestDirs.push(next.manifestDir);
|
|
433
433
|
for (const specifier of relativeSpecifiers(outside.file)) {
|
|
434
434
|
const reached = resolveRelativeSpecifier(host, dirnamePath(next.path), specifier);
|
|
435
|
-
if (reached !== undefined) {
|
|
435
|
+
if (reached !== undefined && isProjectPath(root, reached)) {
|
|
436
436
|
pending.push({ path: reached, manifestDir: next.manifestDir });
|
|
437
437
|
}
|
|
438
438
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* member read through an instantiation of a generic type is a symbol of its own that
|
|
5
5
|
* keeps the declarations of the member it instantiates.
|
|
6
6
|
*/
|
|
7
|
-
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/
|
|
8
|
-
import { SymbolFlags
|
|
7
|
+
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
8
|
+
import { SymbolFlags } from "@typescript/native/sync";
|
|
9
9
|
import { resolvedTargets } from "./calls.js";
|
|
10
10
|
import { must, Unanswerable } from "./query.js";
|
|
11
11
|
import { nameComponent } from "./ref.js";
|
|
@@ -4,7 +4,7 @@ export const RECORD = {
|
|
|
4
4
|
conformance: {
|
|
5
5
|
corpusVersion: "5.0.0",
|
|
6
6
|
result: "pass",
|
|
7
|
-
digest: "sha256:
|
|
7
|
+
digest: "sha256:c46c7d06b6f0c58504c5f311870efefc0b85f18c2c46ba7d7b722081cb370c0c",
|
|
8
8
|
},
|
|
9
9
|
gaps: [],
|
|
10
10
|
};
|