@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
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { EXIT_CLEAN } from "../contract/contract.generated.ts";
|
|
1
2
|
import { runSweep } from "../analysis.ts";
|
|
2
3
|
import { retainedLines } from "../exempt.ts";
|
|
3
4
|
import { partialNotes } from "../partial.ts";
|
|
4
5
|
import { resolvePath } from "../paths.ts";
|
|
5
6
|
import { resolve } from "../resolve.ts";
|
|
6
|
-
import {
|
|
7
|
+
import { type Verb } from "./verb.ts";
|
|
8
|
+
import { diagnose } from "../product.ts";
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Writes every declaration an exemption held back in the production sweep a report is
|
|
@@ -27,7 +29,7 @@ export const printRetainedVerb: Verb = ({ out, err, host, inputs, scope, openCli
|
|
|
27
29
|
unanswered: swept.unanswered,
|
|
28
30
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
29
31
|
})) {
|
|
30
|
-
err
|
|
32
|
+
diagnose(err, note);
|
|
31
33
|
}
|
|
32
34
|
return EXIT_CLEAN;
|
|
33
35
|
};
|
package/src/verbs/print-roots.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { EXIT_CLEAN, EXIT_FINDINGS } from "../contract/contract.generated.ts";
|
|
1
2
|
import { runRoots, type RunRoot } from "../analysis.ts";
|
|
2
3
|
import { partialNotes } from "../partial.ts";
|
|
3
4
|
import { resolvePath } from "../paths.ts";
|
|
4
5
|
import { resolve } from "../resolve.ts";
|
|
5
|
-
import {
|
|
6
|
+
import { type Verb } from "./verb.ts";
|
|
7
|
+
import { diagnose } from "../product.ts";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* One root as a line: the declaration's reference, why it is a root, and the string
|
|
@@ -41,7 +43,7 @@ export const printRootsVerb: Verb = ({ out, err, host, inputs, scope, openClient
|
|
|
41
43
|
unanswered: answer.unanswered,
|
|
42
44
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
43
45
|
})) {
|
|
44
|
-
err
|
|
46
|
+
diagnose(err, note);
|
|
45
47
|
}
|
|
46
48
|
return answer.findings.length > 0 ? EXIT_FINDINGS : EXIT_CLEAN;
|
|
47
49
|
};
|
package/src/verbs/verb.ts
CHANGED
|
@@ -4,12 +4,6 @@ import type { Writer } from "../run.ts";
|
|
|
4
4
|
import type { Scope } from "../scope.ts";
|
|
5
5
|
import type { Engine } from "../session.ts";
|
|
6
6
|
|
|
7
|
-
/** The exit codes the Contract's exit-code table names. */
|
|
8
|
-
export const EXIT_CLEAN = 0;
|
|
9
|
-
export const EXIT_FINDINGS = 1;
|
|
10
|
-
export const EXIT_USAGE = 2;
|
|
11
|
-
export const EXIT_FAILURE = 3;
|
|
12
|
-
|
|
13
7
|
/**
|
|
14
8
|
* What one verb is handed: the streams it writes, the platform it reads, and the
|
|
15
9
|
* options that follow its name, read only when the verb asks for what they name, so
|
package/src/verbs/version.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { CONTRACT_VERSION } from "../
|
|
2
|
-
import {
|
|
1
|
+
import { CONTRACT_VERSION, EXIT_CLEAN } from "../contract/contract.generated.ts";
|
|
2
|
+
import { type Verb } from "./verb.ts";
|
|
3
|
+
import { PRODUCT } from "../product.ts";
|
|
3
4
|
|
|
4
5
|
/** Writes the version the host reports and the Contract version this analyzer implements. */
|
|
5
6
|
export const versionVerb: Verb = ({ out, host }) => {
|
|
6
|
-
out.write(
|
|
7
|
+
out.write(`${PRODUCT} ${host.analyzerVersion()}\ncontract ${CONTRACT_VERSION}\n`);
|
|
7
8
|
return EXIT_CLEAN;
|
|
8
9
|
};
|
package/src/workflow-steps.ts
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import type { SourceFile } from "@typescript/native/ast";
|
|
8
8
|
import type { Host } from "./host.ts";
|
|
9
|
-
import { scriptTokens } from "./manifest.ts";
|
|
10
9
|
import { joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
11
10
|
import type { SourceFiles } from "./source-files.ts";
|
|
11
|
+
import { filesIn } from "./fs/tree.ts";
|
|
12
|
+
import { commandsOf, scriptTokens, unquoted, withoutComment } from "./text/shell.ts";
|
|
12
13
|
|
|
13
14
|
/** One file a workflow step names. */
|
|
14
15
|
interface WorkflowEntry {
|
|
@@ -36,30 +37,16 @@ const HOOK_MANAGERS: readonly (readonly [string, RegExp])[] = [
|
|
|
36
37
|
/** The names GNU make reads a makefile under, in the order it tries them. */
|
|
37
38
|
const MAKEFILES = ["GNUmakefile", "makefile", "Makefile"] as const;
|
|
38
39
|
|
|
39
|
-
/** The operators that end one shell command of a recipe line. */
|
|
40
|
-
const COMMAND_SEPARATORS = /&&|\|\||;|\|/u;
|
|
41
|
-
|
|
42
40
|
const BLOCK_SCALAR = /^[|>][+-]?$/u;
|
|
43
41
|
|
|
44
|
-
const QUOTED = /^(["'])(.*)\1$/su;
|
|
45
|
-
|
|
46
42
|
/** A token holding one of these is computed or a pattern, so it names no file. */
|
|
47
43
|
const COMPUTED = /[${*?[]/u;
|
|
48
44
|
|
|
49
45
|
const INDENT = /^\s*/u;
|
|
50
46
|
|
|
51
|
-
/** The files directly in one directory whose name `accepts`, in name order. */
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
entries = host.readDirectory(dir);
|
|
56
|
-
} catch {
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
return entries
|
|
60
|
-
.filter((entry) => !entry.directory && accepts(entry.name))
|
|
61
|
-
.map((entry) => joinPath(dir, entry.name))
|
|
62
|
-
.sort();
|
|
47
|
+
/** The files directly in one directory whose name `accepts`, by path in name order. */
|
|
48
|
+
function pathsIn(host: Host, dir: string, accepts: (name: string) => boolean): readonly string[] {
|
|
49
|
+
return filesIn(host, dir, accepts).map((name) => joinPath(dir, name));
|
|
63
50
|
}
|
|
64
51
|
|
|
65
52
|
function readText(host: Host, path: string): string | undefined {
|
|
@@ -80,7 +67,7 @@ export function runCommands(text: string, key: RegExp = RUN_LINE): readonly stri
|
|
|
80
67
|
continue;
|
|
81
68
|
}
|
|
82
69
|
const column = (match[1] ?? "").length;
|
|
83
|
-
const value = (match[2] ?? "").trim()
|
|
70
|
+
const value = unquoted((match[2] ?? "").trim());
|
|
84
71
|
if (!BLOCK_SCALAR.test(value)) {
|
|
85
72
|
found.push(value);
|
|
86
73
|
continue;
|
|
@@ -113,7 +100,7 @@ function recipeLines(host: Host, root: string): readonly (readonly string[])[] {
|
|
|
113
100
|
.replace(/\\\r?\n/gu, " ")
|
|
114
101
|
.split(/\r?\n/u)
|
|
115
102
|
.filter((line) => line.startsWith("\t"))
|
|
116
|
-
.map((line) => withoutComment(line.slice(1).replace(/^[@+-]+/u, ""))
|
|
103
|
+
.map((line) => commandsOf(withoutComment(line.slice(1).replace(/^[@+-]+/u, ""))));
|
|
117
104
|
}
|
|
118
105
|
|
|
119
106
|
/**
|
|
@@ -140,28 +127,6 @@ function recipeTokens(host: Host, root: string): readonly { token: string; dir:
|
|
|
140
127
|
return found;
|
|
141
128
|
}
|
|
142
129
|
|
|
143
|
-
/** One shell line up to its comment: a `#` that starts a word outside quotes. */
|
|
144
|
-
function withoutComment(line: string): string {
|
|
145
|
-
let quote = "";
|
|
146
|
-
for (let at = 0; at < line.length; at += 1) {
|
|
147
|
-
const char = line[at];
|
|
148
|
-
if (quote !== "") {
|
|
149
|
-
if (char === quote) {
|
|
150
|
-
quote = "";
|
|
151
|
-
} else if (char === "\\" && quote === '"') {
|
|
152
|
-
at += 1;
|
|
153
|
-
}
|
|
154
|
-
} else if (char === "'" || char === '"') {
|
|
155
|
-
quote = char;
|
|
156
|
-
} else if (char === "\\") {
|
|
157
|
-
at += 1;
|
|
158
|
-
} else if (char === "#" && (at === 0 || /\s/u.test(line[at - 1] ?? ""))) {
|
|
159
|
-
return line.slice(0, at);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return line;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
130
|
/**
|
|
166
131
|
* Every command the target root's workflow steps, git hooks and makefile run: the `run:`
|
|
167
132
|
* steps of the workflow files in `.github/workflows`, every line of each file in `.husky`
|
|
@@ -185,11 +150,11 @@ export function workflowTokens(host: Host, targetRoot: string): readonly string[
|
|
|
185
150
|
/** The commands of the workflow steps, git hooks and hook managers at one root. */
|
|
186
151
|
function stepCommands(host: Host, root: string): readonly string[] {
|
|
187
152
|
const commands: string[] = [];
|
|
188
|
-
for (const path of
|
|
153
|
+
for (const path of pathsIn(host, joinPath(root, WORKFLOWS), (name) => /\.ya?ml$/u.test(name))) {
|
|
189
154
|
commands.push(...runCommands(readText(host, path) ?? ""));
|
|
190
155
|
}
|
|
191
156
|
for (const dir of HOOK_DIRS) {
|
|
192
|
-
for (const path of
|
|
157
|
+
for (const path of pathsIn(host, joinPath(root, dir), () => true)) {
|
|
193
158
|
commands.push(...(readText(host, path) ?? "").split(/\r?\n/u).map(withoutComment));
|
|
194
159
|
}
|
|
195
160
|
}
|
|
@@ -12,6 +12,7 @@ import { readManifest, type Manifest, type ManifestEntry } from "./manifest.ts";
|
|
|
12
12
|
import { joinPath, relativePath } from "./paths.ts";
|
|
13
13
|
import type { WorkspaceMember } from "./workspace.ts";
|
|
14
14
|
import type { WorkspaceResolver } from "./workspace-resolution.ts";
|
|
15
|
+
import { MANIFEST } from "./fs/manifest.ts";
|
|
15
16
|
|
|
16
17
|
/** The one character a subpath pattern of a manifest's `exports` holds. */
|
|
17
18
|
const SUBPATH_WILDCARD = "*";
|
|
@@ -84,8 +85,7 @@ export function memberEntries(
|
|
|
84
85
|
}
|
|
85
86
|
const manifest = readManifest(host, member.dir);
|
|
86
87
|
const where =
|
|
87
|
-
relativePath(targetRoot, joinPath(member.dir,
|
|
88
|
-
joinPath(member.dir, "package.json");
|
|
88
|
+
relativePath(targetRoot, joinPath(member.dir, MANIFEST)) ?? joinPath(member.dir, MANIFEST);
|
|
89
89
|
const kept = manifest.entries.flatMap((entry): ManifestEntry[] => {
|
|
90
90
|
const exported = entry.member.startsWith("exports");
|
|
91
91
|
if (member.private && exported) {
|
|
@@ -21,6 +21,8 @@ import type { Host } from "./host.ts";
|
|
|
21
21
|
import { dirnamePath, resolvePath } from "./paths.ts";
|
|
22
22
|
import type { Engine } from "./session.ts";
|
|
23
23
|
import type { WorkspaceResolver } from "./workspace-resolution.ts";
|
|
24
|
+
import { bytewise } from "./text/compare.ts";
|
|
25
|
+
import { PRODUCT } from "./product.ts";
|
|
24
26
|
|
|
25
27
|
/** One member import no member source answers for. */
|
|
26
28
|
interface UnresolvedImport {
|
|
@@ -129,7 +131,7 @@ function unstatable(reading: Reading): string | undefined {
|
|
|
129
131
|
export function writtenPath(host: Host, configFile: string): string {
|
|
130
132
|
const stem = configFile.replace(/\.json$/u, "");
|
|
131
133
|
for (let counter = 0; ; counter += 1) {
|
|
132
|
-
const path = `${stem}
|
|
134
|
+
const path = `${stem}.${PRODUCT}${counter === 0 ? "" : `-${String(counter)}`}.json`;
|
|
133
135
|
if (host.kindOf(path) === "absent") {
|
|
134
136
|
return path;
|
|
135
137
|
}
|
|
@@ -181,10 +183,10 @@ function configurationOf(
|
|
|
181
183
|
...(exclude === undefined ? {} : { exclude }),
|
|
182
184
|
});
|
|
183
185
|
const file = writtenPath(host, reading.configFile);
|
|
184
|
-
const expected = [...reading.parsed.fileNames].sort().join("\u0000");
|
|
186
|
+
const expected = [...reading.parsed.fileNames].sort(bytewise).join("\u0000");
|
|
185
187
|
const listed = (value: Record<string, unknown>): string | undefined => {
|
|
186
188
|
try {
|
|
187
|
-
return [...engine.parseConfigJson(value, file).fileNames].sort().join("\u0000");
|
|
189
|
+
return [...engine.parseConfigJson(value, file).fileNames].sort(bytewise).join("\u0000");
|
|
188
190
|
} catch {
|
|
189
191
|
return undefined;
|
|
190
192
|
}
|
|
@@ -9,9 +9,18 @@
|
|
|
9
9
|
import { emittedFrom } from "./emit-map.ts";
|
|
10
10
|
import { gitIgnored } from "./git-ignore.ts";
|
|
11
11
|
import type { Host } from "./host.ts";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
ancestors,
|
|
14
|
+
dirnamePath,
|
|
15
|
+
isAbsolutePath,
|
|
16
|
+
joinPath,
|
|
17
|
+
relativePath,
|
|
18
|
+
resolvePath,
|
|
19
|
+
} from "./paths.ts";
|
|
13
20
|
import { satisfies } from "./version-range.ts";
|
|
14
21
|
import type { Workspace, WorkspaceMember } from "./workspace.ts";
|
|
22
|
+
import { isRecord } from "./json-document.ts";
|
|
23
|
+
import { INSTALLED_PACKAGES, MANIFEST, manifestAt } from "./fs/manifest.ts";
|
|
15
24
|
|
|
16
25
|
/** What the compiler's own resolution answered for one import. */
|
|
17
26
|
export interface DefaultResolution {
|
|
@@ -47,10 +56,6 @@ const DEPENDENCY_MEMBERS = [
|
|
|
47
56
|
const SOURCE = /(?<!\.d)\.(?:[mc]?ts|tsx)$/u;
|
|
48
57
|
const OUTPUT = /(?:\.d\.[mc]?ts|\.[mc]?js|\.jsx)$/u;
|
|
49
58
|
|
|
50
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
51
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
59
|
/** The package name a bare specifier names, or `undefined` for any other specifier. */
|
|
55
60
|
export function packageNameOf(specifier: string): string | undefined {
|
|
56
61
|
if (
|
|
@@ -216,7 +221,7 @@ export function workspaceResolver(host: Host, workspace: Workspace): WorkspaceRe
|
|
|
216
221
|
if (links.has(key)) {
|
|
217
222
|
return links.get(key);
|
|
218
223
|
}
|
|
219
|
-
const candidate = joinPath(dir,
|
|
224
|
+
const candidate = joinPath(dir, INSTALLED_PACKAGES, name);
|
|
220
225
|
let found: string | undefined;
|
|
221
226
|
if (host.kindOf(candidate) === "absent") {
|
|
222
227
|
const up = dirnamePath(dir);
|
|
@@ -245,15 +250,14 @@ export function workspaceResolver(host: Host, workspace: Workspace): WorkspaceRe
|
|
|
245
250
|
if (manifests.has(dir)) {
|
|
246
251
|
return manifests.get(dir);
|
|
247
252
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
253
|
+
const manifest = manifestAt(host, dir);
|
|
254
|
+
const up = dirnamePath(dir);
|
|
255
|
+
const found =
|
|
256
|
+
manifest !== undefined
|
|
257
|
+
? { dir, manifest }
|
|
258
|
+
: up === dir || root === undefined || !inside(root, up)
|
|
259
|
+
? undefined
|
|
260
|
+
: nearestManifest(up);
|
|
257
261
|
manifests.set(dir, found);
|
|
258
262
|
return found;
|
|
259
263
|
};
|
|
@@ -265,7 +269,7 @@ export function workspaceResolver(host: Host, workspace: Workspace): WorkspaceRe
|
|
|
265
269
|
const member = candidates.find((one) => one.dir === linked);
|
|
266
270
|
return member === undefined ? { kind: "none" } : { kind: "member", member };
|
|
267
271
|
}
|
|
268
|
-
const scope = workspace.memberOf(joinPath(fromDir,
|
|
272
|
+
const scope = workspace.memberOf(joinPath(fromDir, MANIFEST));
|
|
269
273
|
if (scope?.name === name) {
|
|
270
274
|
return { kind: "member", member: scope };
|
|
271
275
|
}
|
|
@@ -300,7 +304,7 @@ export function workspaceResolver(host: Host, workspace: Workspace): WorkspaceRe
|
|
|
300
304
|
* directory however its configuration writes it.
|
|
301
305
|
*/
|
|
302
306
|
const mirrored = (member: WorkspaceMember, path: string): string | undefined => {
|
|
303
|
-
for (
|
|
307
|
+
for (const dir of ancestors(dirnamePath(path), member.dir).filter((at) => at !== member.dir)) {
|
|
304
308
|
const found = emittedFrom(path, { outDir: dir, sourceDirs: [member.dir] }).find(
|
|
305
309
|
(candidate) =>
|
|
306
310
|
SOURCE.test(candidate) && !ignored(candidate) && host.kindOf(candidate) === "file",
|
package/src/workspace.ts
CHANGED
|
@@ -7,12 +7,23 @@
|
|
|
7
7
|
* manifest, never one below `node_modules`.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { DiscoveryError } from "./discover.ts";
|
|
10
|
+
import { DiscoveryError, isConfigFile } from "./discover.ts";
|
|
11
11
|
import type { EmitLayout } from "./emit-map.ts";
|
|
12
12
|
import { commonDirectory } from "./emit-map.ts";
|
|
13
13
|
import type { Host } from "./host.ts";
|
|
14
|
-
import { dirnamePath, joinPath, relativePath } from "./paths.ts";
|
|
15
|
-
import {
|
|
14
|
+
import { ancestors, dirnamePath, joinPath, relativePath } from "./paths.ts";
|
|
15
|
+
import { bytewise } from "./text/compare.ts";
|
|
16
|
+
import { isRecord } from "./json-document.ts";
|
|
17
|
+
import { directoriesIn, filesIn, repositoryOf } from "./fs/tree.ts";
|
|
18
|
+
import {
|
|
19
|
+
holdsManifest,
|
|
20
|
+
inInstalledPackages,
|
|
21
|
+
INSTALLED_PACKAGES,
|
|
22
|
+
MANIFEST,
|
|
23
|
+
manifestAt,
|
|
24
|
+
} from "./fs/manifest.ts";
|
|
25
|
+
import { braceExpanded, pathMatches, segmentMatches } from "./text/glob.ts";
|
|
26
|
+
import { yamlKey } from "./text/yaml.ts";
|
|
16
27
|
|
|
17
28
|
/** What one compiler configuration of a member says about where it reads and writes. */
|
|
18
29
|
export interface ParsedLayout {
|
|
@@ -56,29 +67,7 @@ export interface Workspace {
|
|
|
56
67
|
memberOf(path: string): WorkspaceMember | undefined;
|
|
57
68
|
}
|
|
58
69
|
|
|
59
|
-
const MANIFEST = "package.json";
|
|
60
70
|
const PNPM_WORKSPACE = "pnpm-workspace.yaml";
|
|
61
|
-
const CONFIG_FILE = /^tsconfig.*\.json$/u;
|
|
62
|
-
|
|
63
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
64
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** One manifest, decoded, or `undefined` where there is none to read. */
|
|
68
|
-
function manifestAt(host: Host, dir: string): Record<string, unknown> | undefined {
|
|
69
|
-
let text: string;
|
|
70
|
-
try {
|
|
71
|
-
text = host.readFile(joinPath(dir, MANIFEST));
|
|
72
|
-
} catch {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
try {
|
|
76
|
-
const value: unknown = JSON.parse(text);
|
|
77
|
-
return isRecord(value) ? value : undefined;
|
|
78
|
-
} catch {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
71
|
|
|
83
72
|
/** The patterns one directory declares a workspace with, or `undefined` where it declares none. */
|
|
84
73
|
function declarationAt(
|
|
@@ -87,14 +76,20 @@ function declarationAt(
|
|
|
87
76
|
): { readonly file: string; readonly patterns: readonly string[] } | undefined {
|
|
88
77
|
const pnpm = joinPath(dir, PNPM_WORKSPACE);
|
|
89
78
|
if (host.kindOf(pnpm) === "file") {
|
|
90
|
-
const read =
|
|
91
|
-
if (read.kind === "
|
|
79
|
+
const read = yamlKey(host.readFile(pnpm), "packages");
|
|
80
|
+
if (read.kind === "absent") {
|
|
81
|
+
return { file: pnpm, patterns: [] };
|
|
82
|
+
}
|
|
83
|
+
const listed = read.kind === "value" ? read.value : undefined;
|
|
84
|
+
if (!Array.isArray(listed) || !listed.every((entry) => typeof entry === "string")) {
|
|
85
|
+
const reason =
|
|
86
|
+
read.kind === "refused" ? read.reason : "packages is not a sequence of strings";
|
|
92
87
|
throw new DiscoveryError(
|
|
93
|
-
`${pnpm}: ${
|
|
88
|
+
`${pnpm}: ${reason}; the workspace's packages are read from a sequence of strings`,
|
|
94
89
|
[],
|
|
95
90
|
);
|
|
96
91
|
}
|
|
97
|
-
return { file: pnpm, patterns:
|
|
92
|
+
return { file: pnpm, patterns: listed };
|
|
98
93
|
}
|
|
99
94
|
const manifest = manifestAt(host, dir);
|
|
100
95
|
if (manifest === undefined || !Object.hasOwn(manifest, "workspaces")) {
|
|
@@ -120,21 +115,6 @@ function declaresWorkspace(host: Host, dir: string): boolean {
|
|
|
120
115
|
return manifest !== undefined && Object.hasOwn(manifest, "workspaces");
|
|
121
116
|
}
|
|
122
117
|
|
|
123
|
-
/** A pattern with every brace list expanded into its alternatives. */
|
|
124
|
-
function braceExpanded(pattern: string): readonly string[] {
|
|
125
|
-
const open = pattern.indexOf("{");
|
|
126
|
-
const close = open < 0 ? -1 : pattern.indexOf("}", open);
|
|
127
|
-
if (close < 0) {
|
|
128
|
-
return [pattern];
|
|
129
|
-
}
|
|
130
|
-
const before = pattern.slice(0, open);
|
|
131
|
-
const after = pattern.slice(close + 1);
|
|
132
|
-
return pattern
|
|
133
|
-
.slice(open + 1, close)
|
|
134
|
-
.split(",")
|
|
135
|
-
.flatMap((member) => braceExpanded(before + member + after));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
118
|
/**
|
|
139
119
|
* The segments of one pattern below the workspace root, `.` and empty segments
|
|
140
120
|
* dropped and `..` taken back, or `undefined` where it leaves the root.
|
|
@@ -156,56 +136,6 @@ function segmentsOf(pattern: string): readonly string[] | undefined {
|
|
|
156
136
|
return segments;
|
|
157
137
|
}
|
|
158
138
|
|
|
159
|
-
/**
|
|
160
|
-
* Whether one directory name matches one pattern segment: `*` stands for any run of
|
|
161
|
-
* characters and `?` for one, and neither matches a leading dot.
|
|
162
|
-
*/
|
|
163
|
-
function segmentMatches(segment: string, name: string): boolean {
|
|
164
|
-
if (name.startsWith(".") && !segment.startsWith(".")) {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
const source = Array.from(segment)
|
|
168
|
-
.map((character) => {
|
|
169
|
-
if (character === "*") {
|
|
170
|
-
return ".*";
|
|
171
|
-
}
|
|
172
|
-
if (character === "?") {
|
|
173
|
-
return ".";
|
|
174
|
-
}
|
|
175
|
-
return character.replaceAll(/[.+^${}()|[\]\\/]/gu, (c) => `\\${c}`);
|
|
176
|
-
})
|
|
177
|
-
.join("");
|
|
178
|
-
return new RegExp(`^${source}$`, "u").test(name);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/** Whether one path's segments match one pattern's, `**` standing for any run of segments. */
|
|
182
|
-
function pathMatches(pattern: readonly string[], path: readonly string[]): boolean {
|
|
183
|
-
const [head, ...rest] = pattern;
|
|
184
|
-
if (head === undefined) {
|
|
185
|
-
return path.length === 0;
|
|
186
|
-
}
|
|
187
|
-
if (head === "**") {
|
|
188
|
-
return (
|
|
189
|
-
pathMatches(rest, path) ||
|
|
190
|
-
(path.length > 0 && !(path[0] ?? "").startsWith(".") && pathMatches(pattern, path.slice(1)))
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
const [name, ...below] = path;
|
|
194
|
-
return name !== undefined && segmentMatches(head, name) && pathMatches(rest, below);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function subdirectories(host: Host, dir: string): readonly string[] {
|
|
198
|
-
try {
|
|
199
|
-
return host
|
|
200
|
-
.readDirectory(dir)
|
|
201
|
-
.filter((entry) => entry.directory && entry.name !== "node_modules" && entry.name !== ".git")
|
|
202
|
-
.map((entry) => entry.name)
|
|
203
|
-
.sort();
|
|
204
|
-
} catch {
|
|
205
|
-
return [];
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
139
|
/**
|
|
210
140
|
* Every directory below `dir` one pattern names. `**` does not descend into a
|
|
211
141
|
* directory that declares a workspace of its own, whose packages are that
|
|
@@ -219,7 +149,7 @@ function expand(host: Host, dir: string, pattern: readonly string[], into: Set<s
|
|
|
219
149
|
}
|
|
220
150
|
if (head === "**") {
|
|
221
151
|
expand(host, dir, rest, into);
|
|
222
|
-
for (const name of
|
|
152
|
+
for (const name of directoriesIn(host, dir)) {
|
|
223
153
|
if (name.startsWith(".")) {
|
|
224
154
|
continue;
|
|
225
155
|
}
|
|
@@ -230,12 +160,12 @@ function expand(host: Host, dir: string, pattern: readonly string[], into: Set<s
|
|
|
230
160
|
}
|
|
231
161
|
if (!head.includes("*") && !head.includes("?")) {
|
|
232
162
|
const child = joinPath(dir, head);
|
|
233
|
-
if (head !==
|
|
163
|
+
if (head !== INSTALLED_PACKAGES && host.kindOf(child) === "directory") {
|
|
234
164
|
expand(host, child, rest, into);
|
|
235
165
|
}
|
|
236
166
|
return;
|
|
237
167
|
}
|
|
238
|
-
for (const name of
|
|
168
|
+
for (const name of directoriesIn(host, dir)) {
|
|
239
169
|
if (segmentMatches(head, name)) {
|
|
240
170
|
expand(host, joinPath(dir, name), rest, into);
|
|
241
171
|
}
|
|
@@ -277,10 +207,10 @@ function namesDirectory(root: string, patterns: readonly string[], dir: string):
|
|
|
277
207
|
if (below === undefined) {
|
|
278
208
|
return false;
|
|
279
209
|
}
|
|
280
|
-
|
|
281
|
-
if (path.includes("node_modules")) {
|
|
210
|
+
if (inInstalledPackages(below)) {
|
|
282
211
|
return false;
|
|
283
212
|
}
|
|
213
|
+
const path = below.split("/");
|
|
284
214
|
let named = false;
|
|
285
215
|
for (const written of patterns) {
|
|
286
216
|
const negated = written.startsWith("!");
|
|
@@ -314,17 +244,7 @@ function configurationsOf(
|
|
|
314
244
|
dir: string,
|
|
315
245
|
parse: (configFile: string) => ParsedLayout | undefined,
|
|
316
246
|
): readonly MemberConfiguration[] {
|
|
317
|
-
|
|
318
|
-
try {
|
|
319
|
-
names = host
|
|
320
|
-
.readDirectory(dir)
|
|
321
|
-
.filter((entry) => !entry.directory && CONFIG_FILE.test(entry.name))
|
|
322
|
-
.map((entry) => entry.name)
|
|
323
|
-
.sort();
|
|
324
|
-
} catch {
|
|
325
|
-
return [];
|
|
326
|
-
}
|
|
327
|
-
return names.flatMap((name) => {
|
|
247
|
+
return filesIn(host, dir, isConfigFile).flatMap((name) => {
|
|
328
248
|
const configFile = joinPath(dir, name);
|
|
329
249
|
const parsed = parse(configFile);
|
|
330
250
|
return parsed === undefined
|
|
@@ -353,22 +273,17 @@ function nearestDeclaration(
|
|
|
353
273
|
readonly highestPackage: string | undefined;
|
|
354
274
|
}
|
|
355
275
|
| undefined {
|
|
356
|
-
let dir = targetRoot;
|
|
357
276
|
let highestPackage: string | undefined;
|
|
358
|
-
for (
|
|
277
|
+
for (const dir of ancestors(targetRoot, repositoryOf(host, targetRoot)?.root)) {
|
|
359
278
|
const declared = declarationAt(host, dir);
|
|
360
279
|
if (declared !== undefined) {
|
|
361
280
|
return { root: dir, ...declared, highestPackage };
|
|
362
281
|
}
|
|
363
|
-
if (host
|
|
282
|
+
if (holdsManifest(host, dir)) {
|
|
364
283
|
highestPackage = dir;
|
|
365
284
|
}
|
|
366
|
-
const up = dirnamePath(dir);
|
|
367
|
-
if (up === dir || host.kindOf(joinPath(dir, ".git")) !== "absent") {
|
|
368
|
-
return undefined;
|
|
369
|
-
}
|
|
370
|
-
dir = up;
|
|
371
285
|
}
|
|
286
|
+
return undefined;
|
|
372
287
|
}
|
|
373
288
|
|
|
374
289
|
/**
|
|
@@ -414,7 +329,7 @@ export function findWorkspace(
|
|
|
414
329
|
return undefined;
|
|
415
330
|
}
|
|
416
331
|
members.sort((a, b) =>
|
|
417
|
-
a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : a.dir
|
|
332
|
+
a.dir === declared.root ? -1 : b.dir === declared.root ? 1 : bytewise(a.dir, b.dir),
|
|
418
333
|
);
|
|
419
334
|
const named = new Map<string, WorkspaceMember[]>();
|
|
420
335
|
for (const member of members) {
|
|
@@ -427,8 +342,7 @@ export function findWorkspace(
|
|
|
427
342
|
return scopes.get(dir);
|
|
428
343
|
}
|
|
429
344
|
const up = dirnamePath(dir);
|
|
430
|
-
const found =
|
|
431
|
-
host.kindOf(joinPath(dir, MANIFEST)) === "file" ? dir : up === dir ? undefined : scopeOf(up);
|
|
345
|
+
const found = holdsManifest(host, dir) ? dir : up === dir ? undefined : scopeOf(up);
|
|
432
346
|
scopes.set(dir, found);
|
|
433
347
|
return found;
|
|
434
348
|
};
|
package/dist/src/glob.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Glob patterns over the paths of the target's own files, as the configuration
|
|
3
|
-
* writes them. Both `ts.test_files` and `ts.entry_files` name files this way, and
|
|
4
|
-
* one grammar answers for both.
|
|
5
|
-
*
|
|
6
|
-
* A configured root is not written in this grammar. It names a symbol reference
|
|
7
|
-
* rather than a path, `*` there spans the solidus and a brace list means nothing,
|
|
8
|
-
* so that rule is written where it is read.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* One literal run of a pattern, with every character the expression syntax claims
|
|
12
|
-
* escaped.
|
|
13
|
-
*
|
|
14
|
-
* The set is the syntax characters and the solidus, and it is closed: an escape of any
|
|
15
|
-
* other character is refused outright by the expression syntax under the unicode flag,
|
|
16
|
-
* so escaping a hyphen, which needs none outside a character class, would refuse every
|
|
17
|
-
* pattern that names a hyphenated path.
|
|
18
|
-
*/
|
|
19
|
-
function quoted(text) {
|
|
20
|
-
return text.replaceAll(/[.*+?^${}()|[\]\\/]/gu, (character) => `\\${character}`);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* The expression one glob pattern denotes. `**` spans path separators while `*` and `?`
|
|
24
|
-
* do not, a brace list is an alternation of its members, and every other character
|
|
25
|
-
* stands for itself.
|
|
26
|
-
*/
|
|
27
|
-
export function globExpression(pattern) {
|
|
28
|
-
let source = "^";
|
|
29
|
-
let at = 0;
|
|
30
|
-
while (at < pattern.length) {
|
|
31
|
-
const rest = pattern.slice(at);
|
|
32
|
-
if (rest.startsWith("**/")) {
|
|
33
|
-
source += "(?:[^/]*/)*";
|
|
34
|
-
at += 3;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (rest.startsWith("**")) {
|
|
38
|
-
source += ".*";
|
|
39
|
-
at += 2;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
const character = pattern[at] ?? "";
|
|
43
|
-
at += 1;
|
|
44
|
-
if (character === "*") {
|
|
45
|
-
source += "[^/]*";
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (character === "?") {
|
|
49
|
-
source += "[^/]";
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (character === "{") {
|
|
53
|
-
const close = pattern.indexOf("}", at);
|
|
54
|
-
if (close < 0) {
|
|
55
|
-
source += "\\{";
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
source += `(?:${pattern.slice(at, close).split(",").map(quoted).join("|")})`;
|
|
59
|
-
at = close + 1;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
source += quoted(character);
|
|
63
|
-
}
|
|
64
|
-
return new RegExp(`${source}$`, "u");
|
|
65
|
-
}
|