@cplieger/deadset-ts 8.0.0-dev.5 → 8.0.0-dev.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +3 -2
- package/dist/src/analysis.js +28 -46
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +17 -29
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +3 -2
- package/dist/src/configuration-files.js +27 -168
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +1084 -0
- package/dist/src/contract/report.generated.js +796 -0
- package/dist/src/conventions.js +20 -60
- package/dist/src/dependencies.js +25 -43
- package/dist/src/discover.js +13 -39
- package/dist/src/edges.js +7 -15
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +3 -10
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +3 -63
- package/dist/src/exempt.js +13 -40
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +11 -20
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +4 -4
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +8 -7
- package/dist/src/findings/non-code-artifacts.js +5 -6
- package/dist/src/findings/reads-and-writes.js +11 -9
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +28 -27
- package/dist/src/findings/visibility-narrowing.js +9 -11
- package/dist/src/findings-pass.js +7 -14
- package/dist/src/fs/manifest.js +43 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +34 -25
- 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/intra-function-parts.js +18 -26
- package/dist/src/inventory.js +2 -21
- package/dist/src/json-document.js +4 -0
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +4 -360
- package/dist/src/manifest.js +5 -43
- package/dist/src/mdx-entries.js +3 -26
- 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 +3 -2
- package/dist/src/product.js +13 -0
- package/dist/src/references.js +14 -27
- package/dist/src/report-schema.js +272 -0
- package/dist/src/report.js +39 -42
- package/dist/src/reporters/sarif.js +4 -2
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +12 -26
- package/dist/src/roots.js +7 -17
- package/dist/src/run.js +10 -8
- package/dist/src/schema.js +4 -3
- package/dist/src/scope.js +4 -5
- package/dist/src/serialization-contract.js +4 -8
- package/dist/src/session.js +2 -1
- package/dist/src/setup-failure.js +2 -6
- package/dist/src/suppress-file.js +12 -13
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +29 -48
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +5 -15
- package/dist/src/test-globals.js +6 -28
- package/dist/src/test-joins.js +6 -6
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/{yaml-document.js → text/yaml.js} +38 -0
- package/dist/src/type-errors.js +14 -38
- package/dist/src/unseen-loads.js +2 -9
- package/dist/src/unwrap.js +23 -0
- package/dist/src/verbs/analyze.js +22 -15
- package/dist/src/verbs/describe.js +7 -7
- package/dist/src/verbs/explain.js +7 -4
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +4 -2
- 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 +4 -3
- package/dist/src/workflow-steps.js +9 -46
- package/dist/src/workspace-manifests.js +2 -2
- package/dist/src/workspace-programs.js +5 -3
- package/dist/src/workspace-resolution.js +13 -17
- package/dist/src/workspace.js +28 -116
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/analysis.ts +29 -54
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +17 -38
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +11 -15
- package/src/configuration-files.ts +27 -170
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +1148 -0
- package/src/contract/report.generated.ts +1157 -0
- package/src/conventions.ts +26 -74
- package/src/dependencies.ts +32 -50
- package/src/discover.ts +14 -39
- package/src/edges.ts +7 -21
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +2 -13
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +5 -74
- package/src/exempt.ts +17 -47
- package/src/file-facts.ts +6 -24
- package/src/finding.ts +9 -9
- package/src/findings/completion.ts +22 -23
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +6 -7
- package/src/findings/emitters.ts +6 -6
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +8 -8
- package/src/findings/non-code-artifacts.ts +5 -7
- package/src/findings/reads-and-writes.ts +15 -12
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +28 -27
- package/src/findings/visibility-narrowing.ts +9 -12
- package/src/findings-pass.ts +7 -16
- package/src/fs/manifest.ts +52 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +38 -25
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/intra-function-parts.ts +18 -28
- package/src/inventory.ts +2 -20
- package/src/json-document.ts +5 -0
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +6 -406
- package/src/manifest.ts +5 -46
- package/src/mdx-entries.ts +3 -26
- package/src/partial.ts +2 -1
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +3 -2
- package/src/product.ts +18 -0
- package/src/references.ts +14 -29
- package/src/report-schema.ts +364 -0
- package/src/report.ts +66 -229
- package/src/reporters/annotations.ts +2 -2
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -2
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +17 -12
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +31 -38
- package/src/roots.ts +7 -20
- package/src/run.ts +10 -14
- package/src/schema.ts +4 -3
- package/src/scope.ts +4 -10
- package/src/serialization-contract.ts +4 -9
- package/src/session.ts +2 -1
- package/src/setup-failure.ts +5 -7
- package/src/suppress-file.ts +11 -15
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +39 -55
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +5 -18
- package/src/test-globals.ts +9 -27
- package/src/test-joins.ts +6 -8
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/shell.ts +161 -0
- package/src/{yaml-document.ts → text/yaml.ts} +46 -0
- package/src/type-errors.ts +25 -36
- package/src/unseen-loads.ts +1 -12
- package/src/unwrap.ts +34 -0
- package/src/verbs/analyze.ts +38 -25
- package/src/verbs/describe.ts +13 -7
- package/src/verbs/explain.ts +7 -4
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +4 -2
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +0 -6
- package/src/verbs/version.ts +4 -3
- package/src/workflow-steps.ts +9 -44
- package/src/workspace-manifests.ts +2 -2
- package/src/workspace-programs.ts +5 -3
- package/src/workspace-resolution.ts +21 -17
- package/src/workspace.ts +35 -121
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/version.js +0 -7
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/version.ts +0 -7
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { emittedFrom } from "./emit-map.js";
|
|
9
9
|
import { gitIgnored } from "./git-ignore.js";
|
|
10
|
-
import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
10
|
+
import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, } from "./paths.js";
|
|
11
11
|
import { satisfies } from "./version-range.js";
|
|
12
|
+
import { isRecord } from "./json-document.js";
|
|
13
|
+
import { INSTALLED_PACKAGES, MANIFEST, manifestAt } from "./fs/manifest.js";
|
|
12
14
|
/** The manifest members that name an entry point for the subpath `.` without `exports`. */
|
|
13
15
|
const LEGACY_MEMBERS = ["types", "typings", "module", "main"];
|
|
14
16
|
/** The dependency members a manifest declares other packages under. */
|
|
@@ -20,9 +22,6 @@ const DEPENDENCY_MEMBERS = [
|
|
|
20
22
|
];
|
|
21
23
|
const SOURCE = /(?<!\.d)\.(?:[mc]?ts|tsx)$/u;
|
|
22
24
|
const OUTPUT = /(?:\.d\.[mc]?ts|\.[mc]?js|\.jsx)$/u;
|
|
23
|
-
function isRecord(value) {
|
|
24
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
-
}
|
|
26
25
|
/** The package name a bare specifier names, or `undefined` for any other specifier. */
|
|
27
26
|
export function packageNameOf(specifier) {
|
|
28
27
|
if (specifier === "" ||
|
|
@@ -146,7 +145,7 @@ export function workspaceResolver(host, workspace) {
|
|
|
146
145
|
if (links.has(key)) {
|
|
147
146
|
return links.get(key);
|
|
148
147
|
}
|
|
149
|
-
const candidate = joinPath(dir,
|
|
148
|
+
const candidate = joinPath(dir, INSTALLED_PACKAGES, name);
|
|
150
149
|
let found;
|
|
151
150
|
if (host.kindOf(candidate) === "absent") {
|
|
152
151
|
const up = dirnamePath(dir);
|
|
@@ -171,16 +170,13 @@ export function workspaceResolver(host, workspace) {
|
|
|
171
170
|
if (manifests.has(dir)) {
|
|
172
171
|
return manifests.get(dir);
|
|
173
172
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
found =
|
|
182
|
-
up === dir || root === undefined || !inside(root, up) ? undefined : nearestManifest(up);
|
|
183
|
-
}
|
|
173
|
+
const manifest = manifestAt(host, dir);
|
|
174
|
+
const up = dirnamePath(dir);
|
|
175
|
+
const found = manifest !== undefined
|
|
176
|
+
? { dir, manifest }
|
|
177
|
+
: up === dir || root === undefined || !inside(root, up)
|
|
178
|
+
? undefined
|
|
179
|
+
: nearestManifest(up);
|
|
184
180
|
manifests.set(dir, found);
|
|
185
181
|
return found;
|
|
186
182
|
};
|
|
@@ -191,7 +187,7 @@ export function workspaceResolver(host, workspace) {
|
|
|
191
187
|
const member = candidates.find((one) => one.dir === linked);
|
|
192
188
|
return member === undefined ? { kind: "none" } : { kind: "member", member };
|
|
193
189
|
}
|
|
194
|
-
const scope = workspace.memberOf(joinPath(fromDir,
|
|
190
|
+
const scope = workspace.memberOf(joinPath(fromDir, MANIFEST));
|
|
195
191
|
if (scope?.name === name) {
|
|
196
192
|
return { kind: "member", member: scope };
|
|
197
193
|
}
|
|
@@ -221,7 +217,7 @@ export function workspaceResolver(host, workspace) {
|
|
|
221
217
|
* directory however its configuration writes it.
|
|
222
218
|
*/
|
|
223
219
|
const mirrored = (member, path) => {
|
|
224
|
-
for (
|
|
220
|
+
for (const dir of ancestors(dirnamePath(path), member.dir).filter((at) => at !== member.dir)) {
|
|
225
221
|
const found = emittedFrom(path, { outDir: dir, sourceDirs: [member.dir] }).find((candidate) => SOURCE.test(candidate) && !ignored(candidate) && host.kindOf(candidate) === "file");
|
|
226
222
|
if (found !== undefined) {
|
|
227
223
|
return found;
|
package/dist/src/workspace.js
CHANGED
|
@@ -6,42 +6,30 @@
|
|
|
6
6
|
* member is the workspace root or a directory the patterns name that holds a named
|
|
7
7
|
* manifest, never one below `node_modules`.
|
|
8
8
|
*/
|
|
9
|
-
import { DiscoveryError } from "./discover.js";
|
|
9
|
+
import { DiscoveryError, isConfigFile } from "./discover.js";
|
|
10
10
|
import { commonDirectory } from "./emit-map.js";
|
|
11
|
-
import { dirnamePath, joinPath, relativePath } from "./paths.js";
|
|
12
|
-
import {
|
|
13
|
-
|
|
11
|
+
import { ancestors, dirnamePath, joinPath, relativePath } from "./paths.js";
|
|
12
|
+
import { bytewise } from "./text/compare.js";
|
|
13
|
+
import { isRecord } from "./json-document.js";
|
|
14
|
+
import { directoriesIn, filesIn, repositoryOf } from "./fs/tree.js";
|
|
15
|
+
import { holdsManifest, inInstalledPackages, INSTALLED_PACKAGES, MANIFEST, manifestAt, } from "./fs/manifest.js";
|
|
16
|
+
import { braceExpanded, pathMatches, segmentMatches } from "./text/glob.js";
|
|
17
|
+
import { yamlKey } from "./text/yaml.js";
|
|
14
18
|
const PNPM_WORKSPACE = "pnpm-workspace.yaml";
|
|
15
|
-
const CONFIG_FILE = /^tsconfig.*\.json$/u;
|
|
16
|
-
function isRecord(value) {
|
|
17
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
|
-
}
|
|
19
|
-
/** One manifest, decoded, or `undefined` where there is none to read. */
|
|
20
|
-
function manifestAt(host, dir) {
|
|
21
|
-
let text;
|
|
22
|
-
try {
|
|
23
|
-
text = host.readFile(joinPath(dir, MANIFEST));
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
const value = JSON.parse(text);
|
|
30
|
-
return isRecord(value) ? value : undefined;
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
19
|
/** The patterns one directory declares a workspace with, or `undefined` where it declares none. */
|
|
37
20
|
function declarationAt(host, dir) {
|
|
38
21
|
const pnpm = joinPath(dir, PNPM_WORKSPACE);
|
|
39
22
|
if (host.kindOf(pnpm) === "file") {
|
|
40
|
-
const read =
|
|
41
|
-
if (read.kind === "
|
|
42
|
-
|
|
23
|
+
const read = yamlKey(host.readFile(pnpm), "packages");
|
|
24
|
+
if (read.kind === "absent") {
|
|
25
|
+
return { file: pnpm, patterns: [] };
|
|
26
|
+
}
|
|
27
|
+
const listed = read.kind === "value" ? read.value : undefined;
|
|
28
|
+
if (!Array.isArray(listed) || !listed.every((entry) => typeof entry === "string")) {
|
|
29
|
+
const reason = read.kind === "refused" ? read.reason : "packages is not a sequence of strings";
|
|
30
|
+
throw new DiscoveryError(`${pnpm}: ${reason}; the workspace's packages are read from a sequence of strings`, []);
|
|
43
31
|
}
|
|
44
|
-
return { file: pnpm, patterns:
|
|
32
|
+
return { file: pnpm, patterns: listed };
|
|
45
33
|
}
|
|
46
34
|
const manifest = manifestAt(host, dir);
|
|
47
35
|
if (manifest === undefined || !Object.hasOwn(manifest, "workspaces")) {
|
|
@@ -62,20 +50,6 @@ function declaresWorkspace(host, dir) {
|
|
|
62
50
|
const manifest = manifestAt(host, dir);
|
|
63
51
|
return manifest !== undefined && Object.hasOwn(manifest, "workspaces");
|
|
64
52
|
}
|
|
65
|
-
/** A pattern with every brace list expanded into its alternatives. */
|
|
66
|
-
function braceExpanded(pattern) {
|
|
67
|
-
const open = pattern.indexOf("{");
|
|
68
|
-
const close = open < 0 ? -1 : pattern.indexOf("}", open);
|
|
69
|
-
if (close < 0) {
|
|
70
|
-
return [pattern];
|
|
71
|
-
}
|
|
72
|
-
const before = pattern.slice(0, open);
|
|
73
|
-
const after = pattern.slice(close + 1);
|
|
74
|
-
return pattern
|
|
75
|
-
.slice(open + 1, close)
|
|
76
|
-
.split(",")
|
|
77
|
-
.flatMap((member) => braceExpanded(before + member + after));
|
|
78
|
-
}
|
|
79
53
|
/**
|
|
80
54
|
* The segments of one pattern below the workspace root, `.` and empty segments
|
|
81
55
|
* dropped and `..` taken back, or `undefined` where it leaves the root.
|
|
@@ -96,52 +70,6 @@ function segmentsOf(pattern) {
|
|
|
96
70
|
}
|
|
97
71
|
return segments;
|
|
98
72
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Whether one directory name matches one pattern segment: `*` stands for any run of
|
|
101
|
-
* characters and `?` for one, and neither matches a leading dot.
|
|
102
|
-
*/
|
|
103
|
-
function segmentMatches(segment, name) {
|
|
104
|
-
if (name.startsWith(".") && !segment.startsWith(".")) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
const source = Array.from(segment)
|
|
108
|
-
.map((character) => {
|
|
109
|
-
if (character === "*") {
|
|
110
|
-
return ".*";
|
|
111
|
-
}
|
|
112
|
-
if (character === "?") {
|
|
113
|
-
return ".";
|
|
114
|
-
}
|
|
115
|
-
return character.replaceAll(/[.+^${}()|[\]\\/]/gu, (c) => `\\${c}`);
|
|
116
|
-
})
|
|
117
|
-
.join("");
|
|
118
|
-
return new RegExp(`^${source}$`, "u").test(name);
|
|
119
|
-
}
|
|
120
|
-
/** Whether one path's segments match one pattern's, `**` standing for any run of segments. */
|
|
121
|
-
function pathMatches(pattern, path) {
|
|
122
|
-
const [head, ...rest] = pattern;
|
|
123
|
-
if (head === undefined) {
|
|
124
|
-
return path.length === 0;
|
|
125
|
-
}
|
|
126
|
-
if (head === "**") {
|
|
127
|
-
return (pathMatches(rest, path) ||
|
|
128
|
-
(path.length > 0 && !(path[0] ?? "").startsWith(".") && pathMatches(pattern, path.slice(1))));
|
|
129
|
-
}
|
|
130
|
-
const [name, ...below] = path;
|
|
131
|
-
return name !== undefined && segmentMatches(head, name) && pathMatches(rest, below);
|
|
132
|
-
}
|
|
133
|
-
function subdirectories(host, dir) {
|
|
134
|
-
try {
|
|
135
|
-
return host
|
|
136
|
-
.readDirectory(dir)
|
|
137
|
-
.filter((entry) => entry.directory && entry.name !== "node_modules" && entry.name !== ".git")
|
|
138
|
-
.map((entry) => entry.name)
|
|
139
|
-
.sort();
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
73
|
/**
|
|
146
74
|
* Every directory below `dir` one pattern names. `**` does not descend into a
|
|
147
75
|
* directory that declares a workspace of its own, whose packages are that
|
|
@@ -155,7 +83,7 @@ function expand(host, dir, pattern, into) {
|
|
|
155
83
|
}
|
|
156
84
|
if (head === "**") {
|
|
157
85
|
expand(host, dir, rest, into);
|
|
158
|
-
for (const name of
|
|
86
|
+
for (const name of directoriesIn(host, dir)) {
|
|
159
87
|
if (name.startsWith(".")) {
|
|
160
88
|
continue;
|
|
161
89
|
}
|
|
@@ -166,12 +94,12 @@ function expand(host, dir, pattern, into) {
|
|
|
166
94
|
}
|
|
167
95
|
if (!head.includes("*") && !head.includes("?")) {
|
|
168
96
|
const child = joinPath(dir, head);
|
|
169
|
-
if (head !==
|
|
97
|
+
if (head !== INSTALLED_PACKAGES && host.kindOf(child) === "directory") {
|
|
170
98
|
expand(host, child, rest, into);
|
|
171
99
|
}
|
|
172
100
|
return;
|
|
173
101
|
}
|
|
174
|
-
for (const name of
|
|
102
|
+
for (const name of directoriesIn(host, dir)) {
|
|
175
103
|
if (segmentMatches(head, name)) {
|
|
176
104
|
expand(host, joinPath(dir, name), rest, into);
|
|
177
105
|
}
|
|
@@ -208,10 +136,10 @@ function namesDirectory(root, patterns, dir) {
|
|
|
208
136
|
if (below === undefined) {
|
|
209
137
|
return false;
|
|
210
138
|
}
|
|
211
|
-
|
|
212
|
-
if (path.includes("node_modules")) {
|
|
139
|
+
if (inInstalledPackages(below)) {
|
|
213
140
|
return false;
|
|
214
141
|
}
|
|
142
|
+
const path = below.split("/");
|
|
215
143
|
let named = false;
|
|
216
144
|
for (const written of patterns) {
|
|
217
145
|
const negated = written.startsWith("!");
|
|
@@ -238,18 +166,7 @@ function layoutOf(configFile, parsed) {
|
|
|
238
166
|
return { outDir: parsed.outDir, declarationDir: parsed.declarationDir, sourceDirs };
|
|
239
167
|
}
|
|
240
168
|
function configurationsOf(host, dir, parse) {
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
names = host
|
|
244
|
-
.readDirectory(dir)
|
|
245
|
-
.filter((entry) => !entry.directory && CONFIG_FILE.test(entry.name))
|
|
246
|
-
.map((entry) => entry.name)
|
|
247
|
-
.sort();
|
|
248
|
-
}
|
|
249
|
-
catch {
|
|
250
|
-
return [];
|
|
251
|
-
}
|
|
252
|
-
return names.flatMap((name) => {
|
|
169
|
+
return filesIn(host, dir, isConfigFile).flatMap((name) => {
|
|
253
170
|
const configFile = joinPath(dir, name);
|
|
254
171
|
const parsed = parse(configFile);
|
|
255
172
|
return parsed === undefined
|
|
@@ -267,22 +184,17 @@ function configurationsOf(host, dir, parse) {
|
|
|
267
184
|
* highest directory below it, at or above the target root, that holds a manifest.
|
|
268
185
|
*/
|
|
269
186
|
function nearestDeclaration(host, targetRoot) {
|
|
270
|
-
let dir = targetRoot;
|
|
271
187
|
let highestPackage;
|
|
272
|
-
for (
|
|
188
|
+
for (const dir of ancestors(targetRoot, repositoryOf(host, targetRoot)?.root)) {
|
|
273
189
|
const declared = declarationAt(host, dir);
|
|
274
190
|
if (declared !== undefined) {
|
|
275
191
|
return { root: dir, ...declared, highestPackage };
|
|
276
192
|
}
|
|
277
|
-
if (host
|
|
193
|
+
if (holdsManifest(host, dir)) {
|
|
278
194
|
highestPackage = dir;
|
|
279
195
|
}
|
|
280
|
-
const up = dirnamePath(dir);
|
|
281
|
-
if (up === dir || host.kindOf(joinPath(dir, ".git")) !== "absent") {
|
|
282
|
-
return undefined;
|
|
283
|
-
}
|
|
284
|
-
dir = up;
|
|
285
196
|
}
|
|
197
|
+
return undefined;
|
|
286
198
|
}
|
|
287
199
|
/**
|
|
288
200
|
* The workspace the target sits in, or `undefined` where none holds it: no directory
|
|
@@ -320,7 +232,7 @@ export function findWorkspace(host, targetRoot, parse) {
|
|
|
320
232
|
if (members.length < 2) {
|
|
321
233
|
return undefined;
|
|
322
234
|
}
|
|
323
|
-
members.sort((a, b) => a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : a.dir
|
|
235
|
+
members.sort((a, b) => a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : bytewise(a.dir, b.dir));
|
|
324
236
|
const named = new Map();
|
|
325
237
|
for (const member of members) {
|
|
326
238
|
named.set(member.name, [...(named.get(member.name) ?? []), member]);
|
|
@@ -332,7 +244,7 @@ export function findWorkspace(host, targetRoot, parse) {
|
|
|
332
244
|
return scopes.get(dir);
|
|
333
245
|
}
|
|
334
246
|
const up = dirnamePath(dir);
|
|
335
|
-
const found = host
|
|
247
|
+
const found = holdsManifest(host, dir) ? dir : up === dir ? undefined : scopeOf(up);
|
|
336
248
|
scopes.set(dir, found);
|
|
337
249
|
return found;
|
|
338
250
|
};
|
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/deadset-ts",
|
|
3
|
-
"version": "8.0.0-dev.
|
|
3
|
+
"version": "8.0.0-dev.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Deterministic dead-code analysis for TypeScript, class and type members included",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc -p tsconfig.build.json",
|
|
22
|
+
"gen": "node scripts/gen-contract.ts",
|
|
22
23
|
"prepack": "npm run build",
|
|
23
24
|
"typecheck": "tsc -p tsconfig.json",
|
|
24
25
|
"typecheck:tests": "tsc -p tsconfig.test.json",
|
package/src/analysis.ts
CHANGED
|
@@ -62,7 +62,6 @@ import {
|
|
|
62
62
|
} from "./exempt.ts";
|
|
63
63
|
import type { TSExemptionClass } from "./exempt-classes.ts";
|
|
64
64
|
import { heldByInclusion, sourcePaths, sourceTree } from "./file-facts.ts";
|
|
65
|
-
import { globExpression } from "./glob.ts";
|
|
66
65
|
import type { Finding } from "./finding.ts";
|
|
67
66
|
import { findingsPass, recordedFindings, rowKey, type PassResult } from "./findings-pass.ts";
|
|
68
67
|
import type { EmitterInput } from "./findings/emitter.ts";
|
|
@@ -85,6 +84,7 @@ import {
|
|
|
85
84
|
type SweepResult,
|
|
86
85
|
} from "./matrix.ts";
|
|
87
86
|
import {
|
|
87
|
+
ancestors,
|
|
88
88
|
dirnamePath,
|
|
89
89
|
extensionOf,
|
|
90
90
|
joinPath,
|
|
@@ -94,7 +94,7 @@ import {
|
|
|
94
94
|
} from "./paths.ts";
|
|
95
95
|
import type { UnansweredQuestion } from "./query.ts";
|
|
96
96
|
import { byPosition, isComponentFile, type Position } from "./position.ts";
|
|
97
|
-
import {
|
|
97
|
+
import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
|
|
98
98
|
import { SetupError, setupLine, type SetupFailure } from "./setup-failure.ts";
|
|
99
99
|
import {
|
|
100
100
|
pathsOf,
|
|
@@ -136,6 +136,10 @@ import { inlineDirectives, inlineSuppressions, type InlineDirective } from "./su
|
|
|
136
136
|
import type { Exemption, Mode, SweepInput } from "./sweep.ts";
|
|
137
137
|
import { readTemplates, templateField } from "./template-field.ts";
|
|
138
138
|
import { ownErrors, workspaceRun } from "./workspace-run.ts";
|
|
139
|
+
import { bytewise } from "./text/compare.ts";
|
|
140
|
+
import { holdsManifest, inInstalledPackages, MANIFEST, manifestAt } from "./fs/manifest.ts";
|
|
141
|
+
import { globExpression } from "./text/glob.ts";
|
|
142
|
+
import { LANGUAGE } from "./product.ts";
|
|
139
143
|
|
|
140
144
|
/** The setting whose source decides where a finding about a configured root sits. */
|
|
141
145
|
const ROOTS_SETTING = "roots.patterns";
|
|
@@ -192,14 +196,6 @@ function documentOf(
|
|
|
192
196
|
return relativePath(resolvePath(host.workingDirectory(), targetRoot), document) ?? "";
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
/** Two strings ordered bytewise. */
|
|
196
|
-
function compare(a: string, b: string): number {
|
|
197
|
-
if (a === b) {
|
|
198
|
-
return 0;
|
|
199
|
-
}
|
|
200
|
-
return a < b ? -1 : 1;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
199
|
/** What every stage after the root set reads of one project. */
|
|
204
200
|
interface ProjectRead {
|
|
205
201
|
readonly configuration: string;
|
|
@@ -272,8 +268,8 @@ function conventionManifests(
|
|
|
272
268
|
nestedDirs: readonly string[],
|
|
273
269
|
): readonly ConventionManifest[] {
|
|
274
270
|
const found = new Map<string, ConventionManifest>();
|
|
275
|
-
if (host
|
|
276
|
-
found.set(targetRoot, { dir: targetRoot, path:
|
|
271
|
+
if (holdsManifest(host, targetRoot)) {
|
|
272
|
+
found.set(targetRoot, { dir: targetRoot, path: MANIFEST });
|
|
277
273
|
}
|
|
278
274
|
for (const [dirs, nested] of [
|
|
279
275
|
[memberDirs, false],
|
|
@@ -303,23 +299,18 @@ function nestedPackageDirs(
|
|
|
303
299
|
const seen = new Set<string>();
|
|
304
300
|
const found: string[] = [];
|
|
305
301
|
for (const file of files) {
|
|
306
|
-
for (
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
) {
|
|
302
|
+
for (const dir of ancestors(dirnamePath(file), targetRoot)) {
|
|
303
|
+
if (seen.has(dir) || relativePath(targetRoot, dir) === undefined) {
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
311
306
|
seen.add(dir);
|
|
312
307
|
const below = relativePath(targetRoot, dir) ?? "";
|
|
313
|
-
if (
|
|
314
|
-
!below.split("/").includes("node_modules") &&
|
|
315
|
-
!known.has(dir) &&
|
|
316
|
-
host.kindOf(joinPath(dir, "package.json")) === "file"
|
|
317
|
-
) {
|
|
308
|
+
if (!inInstalledPackages(below) && !known.has(dir) && holdsManifest(host, dir)) {
|
|
318
309
|
found.push(dir);
|
|
319
310
|
}
|
|
320
311
|
}
|
|
321
312
|
}
|
|
322
|
-
return found.sort(
|
|
313
|
+
return found.sort(bytewise);
|
|
323
314
|
}
|
|
324
315
|
|
|
325
316
|
/**
|
|
@@ -723,7 +714,9 @@ export function runRoots(
|
|
|
723
714
|
|
|
724
715
|
const ordered = [...merged.values()].sort(
|
|
725
716
|
(a, b) =>
|
|
726
|
-
byPosition(a.position, b.position) ||
|
|
717
|
+
byPosition(a.position, b.position) ||
|
|
718
|
+
bytewise(a.kind, b.kind) ||
|
|
719
|
+
bytewise(a.source, b.source),
|
|
727
720
|
);
|
|
728
721
|
const unmatched = unmatchedEverywhere(
|
|
729
722
|
config.rootPatterns,
|
|
@@ -872,20 +865,9 @@ function testEvidence(
|
|
|
872
865
|
}
|
|
873
866
|
}
|
|
874
867
|
const manifests = new Map<string, Readonly<Record<string, unknown>> | undefined>();
|
|
875
|
-
const
|
|
868
|
+
const manifestIn = (dir: string): Readonly<Record<string, unknown>> | undefined => {
|
|
876
869
|
if (!manifests.has(dir)) {
|
|
877
|
-
|
|
878
|
-
try {
|
|
879
|
-
value = JSON.parse(host.readFile(joinPath(dir, "package.json")));
|
|
880
|
-
} catch {
|
|
881
|
-
value = undefined;
|
|
882
|
-
}
|
|
883
|
-
manifests.set(
|
|
884
|
-
dir,
|
|
885
|
-
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
886
|
-
? (value as Record<string, unknown>)
|
|
887
|
-
: undefined,
|
|
888
|
-
);
|
|
870
|
+
manifests.set(dir, manifestAt(host, dir));
|
|
889
871
|
}
|
|
890
872
|
return manifests.get(dir);
|
|
891
873
|
};
|
|
@@ -895,16 +877,14 @@ function testEvidence(
|
|
|
895
877
|
return {
|
|
896
878
|
imports,
|
|
897
879
|
devDependency: (path, name) => {
|
|
898
|
-
for (
|
|
899
|
-
const manifest =
|
|
880
|
+
for (const dir of ancestors(dirnamePath(joinPath(root, path)), root)) {
|
|
881
|
+
const manifest = manifestIn(dir);
|
|
900
882
|
const other = OTHER_SECTIONS.some((one) => declares(manifest?.[one], name));
|
|
901
883
|
if (declares(manifest?.[DEV_SECTION], name) || other) {
|
|
902
884
|
return !other;
|
|
903
885
|
}
|
|
904
|
-
if (normalizePath(dir) === normalizePath(root) || dirnamePath(dir) === dir) {
|
|
905
|
-
return false;
|
|
906
|
-
}
|
|
907
886
|
}
|
|
887
|
+
return false;
|
|
908
888
|
},
|
|
909
889
|
};
|
|
910
890
|
}
|
|
@@ -1056,8 +1036,8 @@ function readRun<Extra>(
|
|
|
1056
1036
|
const exempt = detected.records;
|
|
1057
1037
|
const made = supportReferences([...one.references, ...(satisfied?.references ?? [])], support);
|
|
1058
1038
|
const held = new Set(projectRead.held.symbols.map((symbol) => symbol.position.path));
|
|
1059
|
-
const supportFiles = [...support].filter((path) => held.has(path)).sort();
|
|
1060
|
-
const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort();
|
|
1039
|
+
const supportFiles = [...support].filter((path) => held.has(path)).sort(bytewise);
|
|
1040
|
+
const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort(bytewise);
|
|
1061
1041
|
const configured: Configured = {
|
|
1062
1042
|
configuration: projectRead.configuration,
|
|
1063
1043
|
symbols: projectRead.held.symbols,
|
|
@@ -1285,7 +1265,7 @@ function sweptOf<Extra>(
|
|
|
1285
1265
|
unanswered: read.unanswered,
|
|
1286
1266
|
heldByUnanswered: [...held],
|
|
1287
1267
|
skipped: read.projects.flatMap((one) => one.skipped),
|
|
1288
|
-
componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(
|
|
1268
|
+
componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise),
|
|
1289
1269
|
};
|
|
1290
1270
|
}
|
|
1291
1271
|
|
|
@@ -1427,17 +1407,12 @@ function judgedMembers(
|
|
|
1427
1407
|
|
|
1428
1408
|
/** The directory of the nearest `package.json` at or above a file, inside `root`, else `root`. */
|
|
1429
1409
|
function nearestManifestDir(host: Host, root: string, file: string): string {
|
|
1430
|
-
|
|
1431
|
-
if (host.kindOf(joinPath(dir, "package.json")) === "file") {
|
|
1432
|
-
return dir;
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
return root;
|
|
1410
|
+
return ancestors(dirnamePath(file), root).find((dir) => holdsManifest(host, dir)) ?? root;
|
|
1436
1411
|
}
|
|
1437
1412
|
|
|
1438
1413
|
/**
|
|
1439
1414
|
* The input every emitter reads over one sweep of a read run: the facts beside the sweep
|
|
1440
|
-
* each
|
|
1415
|
+
* each emitter reads, every per-project one read in the same session as the sweep.
|
|
1441
1416
|
*/
|
|
1442
1417
|
export function emitterInputOver(
|
|
1443
1418
|
host: Host,
|
|
@@ -1523,7 +1498,7 @@ interface AnalysisReader {
|
|
|
1523
1498
|
* directives, the ignore file's entries and the baseline's rows are bound before any
|
|
1524
1499
|
* sweep, and the run is swept once under every bound record's mark and once under none,
|
|
1525
1500
|
* so a record is decided against the finding its code would have produced. The
|
|
1526
|
-
*
|
|
1501
|
+
* configuration family reports the refused suppressions and the roots that named nothing.
|
|
1527
1502
|
* Beside the inputs it answers the projects and test-file rules a report names.
|
|
1528
1503
|
*/
|
|
1529
1504
|
function readAnalysis(
|
|
@@ -1698,7 +1673,7 @@ export interface RunAnalysis {
|
|
|
1698
1673
|
|
|
1699
1674
|
/**
|
|
1700
1675
|
* The analysis of the run the scope and the configuration describe: the run read with its
|
|
1701
|
-
* suppressions bound, every
|
|
1676
|
+
* suppressions bound, every emitter's findings with the suppressions applied and the
|
|
1702
1677
|
* declared edges evaluated, the projects and test-file rules the report names, and the sweep
|
|
1703
1678
|
* an explanation reads.
|
|
1704
1679
|
*/
|
package/src/automatic-types.ts
CHANGED
|
@@ -26,8 +26,10 @@ import {
|
|
|
26
26
|
type Statement,
|
|
27
27
|
} from "@typescript/native/ast";
|
|
28
28
|
import type { ParseFile } from "./configuration-files.ts";
|
|
29
|
-
import type {
|
|
30
|
-
import { dirnamePath, joinPath, resolvePath } from "./paths.ts";
|
|
29
|
+
import type { Host } from "./host.ts";
|
|
30
|
+
import { ancestors, dirnamePath, joinPath, resolvePath } from "./paths.ts";
|
|
31
|
+
import { entriesOf } from "./fs/tree.ts";
|
|
32
|
+
import { manifestAt } from "./fs/manifest.ts";
|
|
31
33
|
|
|
32
34
|
const TYPE_ROOT = "node_modules/@types";
|
|
33
35
|
|
|
@@ -122,19 +124,11 @@ function fileDeclarations(file: SourceFile, globals: string[], modules: string[]
|
|
|
122
124
|
|
|
123
125
|
/** The entry declaration file of one installed type package's directory. */
|
|
124
126
|
function entryOf(host: Host, dir: string): string {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
(manifest as Record<string, unknown>)["typings"])
|
|
131
|
-
: undefined;
|
|
132
|
-
if (typeof named === "string" && named !== "") {
|
|
133
|
-
const path = resolvePath(dir, named);
|
|
134
|
-
return path.endsWith(".ts") ? path : `${path}.d.ts`;
|
|
135
|
-
}
|
|
136
|
-
} catch {
|
|
137
|
-
// A package with no readable manifest is read from its index, as the compiler reads it.
|
|
127
|
+
const manifest = manifestAt(host, dir);
|
|
128
|
+
const named = manifest?.["types"] ?? manifest?.["typings"];
|
|
129
|
+
if (typeof named === "string" && named !== "") {
|
|
130
|
+
const path = resolvePath(dir, named);
|
|
131
|
+
return path.endsWith(".ts") ? path : `${path}.d.ts`;
|
|
138
132
|
}
|
|
139
133
|
return joinPath(dir, "index.d.ts");
|
|
140
134
|
}
|
|
@@ -201,13 +195,7 @@ export function automaticTypes(
|
|
|
201
195
|
};
|
|
202
196
|
const read = new Set<string>();
|
|
203
197
|
for (const root of typeRoots(configFile, options)) {
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
entries = host.readDirectory(root);
|
|
207
|
-
} catch {
|
|
208
|
-
entries = [];
|
|
209
|
-
}
|
|
210
|
-
for (const entry of entries) {
|
|
198
|
+
for (const entry of entriesOf(host, root)) {
|
|
211
199
|
if (read.has(entry.name) || host.kindOf(joinPath(root, entry.name)) !== "directory") {
|
|
212
200
|
continue;
|
|
213
201
|
}
|
|
@@ -236,11 +224,5 @@ function typeRoots(configFile: string, options: Readonly<Record<string, unknown>
|
|
|
236
224
|
(root): root is string => typeof root === "string" && root.endsWith(`/${TYPE_ROOT}`),
|
|
237
225
|
);
|
|
238
226
|
}
|
|
239
|
-
|
|
240
|
-
for (let dir = dirnamePath(configFile); ; dir = dirnamePath(dir)) {
|
|
241
|
-
roots.push(joinPath(dir, TYPE_ROOT));
|
|
242
|
-
if (dirnamePath(dir) === dir) {
|
|
243
|
-
return roots;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
227
|
+
return ancestors(dirnamePath(configFile)).map((dir) => joinPath(dir, TYPE_ROOT));
|
|
246
228
|
}
|