@savvy-web/silk-effects 1.5.2 → 2.0.0
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/changesets/api/linter.js +9 -7
- package/changesets/errors.js +44 -1
- package/changesets/index.js +20 -16
- package/changesets/markdownlint/rules/dependency-table-format.js +1 -1
- package/changesets/remark/presets.js +1 -1
- package/changesets/schemas/dependency-table.js +12 -2
- package/changesets/services/deps-regen.js +367 -0
- package/changesets/services/release-planner.js +105 -81
- package/changesets/utils/dep-diff.js +81 -47
- package/changesets/utils/git.js +51 -0
- package/changesets/utils/publishability.js +14 -2
- package/index.d.ts +336 -166
- package/package.json +5 -5
- package/changesets/services/workspace-snapshot.js +0 -181
- package/changesets/utils/worktree-snapshot.js +0 -142
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { GitError } from "../errors.js";
|
|
2
|
-
import { Context, Effect, Layer } from "effect";
|
|
3
|
-
import { execFileSync } from "node:child_process";
|
|
4
|
-
|
|
5
|
-
//#region src/changesets/services/workspace-snapshot.ts
|
|
6
|
-
/**
|
|
7
|
-
* Read workspace package snapshots at arbitrary git refs.
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* `WorkspaceDiscovery` reads the **current** workspace state. To compute
|
|
11
|
-
* dependency diffs between two points in time, we also need to read
|
|
12
|
-
* `pnpm-workspace.yaml` and each `package.json` as they existed at a
|
|
13
|
-
* specific commit. This service shells out to `git show <ref>:<path>`
|
|
14
|
-
* for each file, returns a plain-object snapshot per workspace package,
|
|
15
|
-
* and caches per `(cwd, ref)` pair.
|
|
16
|
-
*
|
|
17
|
-
* The snapshot intentionally returns plain objects rather than
|
|
18
|
-
* `WorkspacePackage` instances — `WorkspacePackage` is a `Schema.Class`
|
|
19
|
-
* tightly coupled to the live filesystem, and snapshot consumers only
|
|
20
|
-
* need the declared dependency records to compute a diff.
|
|
21
|
-
*
|
|
22
|
-
* @see {@link WorkspaceSnapshotReader} for the service tag
|
|
23
|
-
* @see {@link WorkspaceSnapshotReaderLive} for the production layer
|
|
24
|
-
*
|
|
25
|
-
*/
|
|
26
|
-
const _tag = Context.Tag("WorkspaceSnapshotReader");
|
|
27
|
-
/**
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
const WorkspaceSnapshotReaderBase = _tag();
|
|
31
|
-
/**
|
|
32
|
-
* Effect service tag for {@link WorkspaceSnapshotReaderShape}.
|
|
33
|
-
*
|
|
34
|
-
* @public
|
|
35
|
-
*/
|
|
36
|
-
var WorkspaceSnapshotReader = class extends WorkspaceSnapshotReaderBase {};
|
|
37
|
-
function runGitShow(cwd, ref, path) {
|
|
38
|
-
return Effect.try({
|
|
39
|
-
try: () => execFileSync("git", ["show", `${ref}:${path}`], {
|
|
40
|
-
cwd,
|
|
41
|
-
encoding: "utf8",
|
|
42
|
-
stdio: [
|
|
43
|
-
"ignore",
|
|
44
|
-
"pipe",
|
|
45
|
-
"pipe"
|
|
46
|
-
]
|
|
47
|
-
}),
|
|
48
|
-
catch: (error) => {
|
|
49
|
-
const stderr = error.stderr;
|
|
50
|
-
const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
|
|
51
|
-
if (/exists on disk, but not in|does not exist|unknown revision|bad object/.test(text)) return new GitError({
|
|
52
|
-
command: `git show ${ref}:${path}`,
|
|
53
|
-
cwd,
|
|
54
|
-
reason: "PATH_NOT_AT_REF"
|
|
55
|
-
});
|
|
56
|
-
return new GitError({
|
|
57
|
-
command: `git show ${ref}:${path}`,
|
|
58
|
-
cwd,
|
|
59
|
-
reason: text.trim() || (error.message ?? String(error))
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}).pipe(Effect.catchTag("GitError", (err) => err.reason === "PATH_NOT_AT_REF" ? Effect.succeed(null) : Effect.fail(err)));
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Parse a minimal `pnpm-workspace.yaml` (`packages:` list only). Tolerant
|
|
66
|
-
* of comments and varied indentation; rejects on missing `packages:` key.
|
|
67
|
-
*/
|
|
68
|
-
function parseWorkspaceGlobs(yamlText) {
|
|
69
|
-
const lines = yamlText.split(/\r?\n/);
|
|
70
|
-
const globs = [];
|
|
71
|
-
let inPackagesBlock = false;
|
|
72
|
-
for (const line of lines) {
|
|
73
|
-
if (/^\s*#/.test(line)) continue;
|
|
74
|
-
if (/^\s*packages\s*:\s*$/.test(line)) {
|
|
75
|
-
inPackagesBlock = true;
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (inPackagesBlock) {
|
|
79
|
-
const match = line.match(/^\s+-\s+["']?(.+?)["']?\s*$/);
|
|
80
|
-
if (match) {
|
|
81
|
-
globs.push(match[1]);
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (line.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) inPackagesBlock = false;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return globs;
|
|
88
|
-
}
|
|
89
|
-
function toSnapshot(pkg, relativePath) {
|
|
90
|
-
if (!pkg.name) return null;
|
|
91
|
-
return {
|
|
92
|
-
name: pkg.name,
|
|
93
|
-
relativePath,
|
|
94
|
-
version: pkg.version ?? "0.0.0",
|
|
95
|
-
dependencies: pkg.dependencies ?? {},
|
|
96
|
-
devDependencies: pkg.devDependencies ?? {},
|
|
97
|
-
peerDependencies: pkg.peerDependencies ?? {},
|
|
98
|
-
optionalDependencies: pkg.optionalDependencies ?? {}
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Expand a workspace glob like `packages/*` or `apps/web` against the
|
|
103
|
-
* directories present at the given git ref. We can't `globSync` here
|
|
104
|
-
* (the directories may not be on disk at this ref); instead we use
|
|
105
|
-
* `git ls-tree` to enumerate paths.
|
|
106
|
-
*/
|
|
107
|
-
function expandGlobAtRef(cwd, ref, glob) {
|
|
108
|
-
return Effect.gen(function* () {
|
|
109
|
-
const cleanGlob = glob.replace(/\/\*\*$/, "/*");
|
|
110
|
-
if (!cleanGlob.includes("*") && !cleanGlob.includes("?")) return [cleanGlob];
|
|
111
|
-
const prefix = cleanGlob.includes("/") ? cleanGlob.slice(0, cleanGlob.lastIndexOf("/") + 1) : "";
|
|
112
|
-
const entries = (yield* Effect.try({
|
|
113
|
-
try: () => execFileSync("git", [
|
|
114
|
-
"ls-tree",
|
|
115
|
-
"--name-only",
|
|
116
|
-
ref,
|
|
117
|
-
prefix
|
|
118
|
-
], {
|
|
119
|
-
cwd,
|
|
120
|
-
encoding: "utf8",
|
|
121
|
-
stdio: [
|
|
122
|
-
"ignore",
|
|
123
|
-
"pipe",
|
|
124
|
-
"pipe"
|
|
125
|
-
]
|
|
126
|
-
}),
|
|
127
|
-
catch: (error) => {
|
|
128
|
-
const stderr = error.stderr;
|
|
129
|
-
const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
|
|
130
|
-
return new GitError({
|
|
131
|
-
command: `git ls-tree ${ref} ${prefix}`,
|
|
132
|
-
cwd,
|
|
133
|
-
reason: text.trim() || (error.message ?? String(error))
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
})).split(/\r?\n/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
137
|
-
const regex = new RegExp(`^${cleanGlob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")}$`);
|
|
138
|
-
return entries.filter((e) => regex.test(e));
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
function makeShape() {
|
|
142
|
-
const cache = /* @__PURE__ */ new Map();
|
|
143
|
-
const snapshotAt = (cwd, ref) => Effect.gen(function* () {
|
|
144
|
-
const cacheKey = `${cwd}::${ref}`;
|
|
145
|
-
const cached = cache.get(cacheKey);
|
|
146
|
-
if (cached) return cached;
|
|
147
|
-
const wsYaml = yield* runGitShow(cwd, ref, "pnpm-workspace.yaml");
|
|
148
|
-
const globs = wsYaml ? parseWorkspaceGlobs(wsYaml) : [];
|
|
149
|
-
const dirs = [];
|
|
150
|
-
for (const glob of globs) {
|
|
151
|
-
const expanded = yield* expandGlobAtRef(cwd, ref, glob);
|
|
152
|
-
for (const d of expanded) if (!dirs.includes(d)) dirs.push(d);
|
|
153
|
-
}
|
|
154
|
-
if (!dirs.includes(".")) dirs.unshift(".");
|
|
155
|
-
const snapshots = [];
|
|
156
|
-
for (const dir of dirs) {
|
|
157
|
-
const pkgText = yield* runGitShow(cwd, ref, dir === "." ? "package.json" : `${dir}/package.json`);
|
|
158
|
-
if (!pkgText) continue;
|
|
159
|
-
let parsed;
|
|
160
|
-
try {
|
|
161
|
-
parsed = JSON.parse(pkgText);
|
|
162
|
-
} catch {
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
const snap = toSnapshot(parsed, dir);
|
|
166
|
-
if (snap) snapshots.push(snap);
|
|
167
|
-
}
|
|
168
|
-
cache.set(cacheKey, snapshots);
|
|
169
|
-
return snapshots;
|
|
170
|
-
});
|
|
171
|
-
return { snapshotAt };
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Production layer for {@link WorkspaceSnapshotReader}.
|
|
175
|
-
*
|
|
176
|
-
* @public
|
|
177
|
-
*/
|
|
178
|
-
const WorkspaceSnapshotReaderLive = Layer.succeed(WorkspaceSnapshotReader, makeShape());
|
|
179
|
-
|
|
180
|
-
//#endregion
|
|
181
|
-
export { WorkspaceSnapshotReader, WorkspaceSnapshotReaderBase, WorkspaceSnapshotReaderLive };
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { GitError } from "../errors.js";
|
|
2
|
-
import { Effect } from "effect";
|
|
3
|
-
import { readFileSync, readdirSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { execFileSync } from "node:child_process";
|
|
6
|
-
|
|
7
|
-
//#region src/changesets/utils/worktree-snapshot.ts
|
|
8
|
-
/**
|
|
9
|
-
* Shared helpers for `deps detect` and `deps regen` — both need to read
|
|
10
|
-
* workspace package snapshots from the live working tree (the "after"
|
|
11
|
-
* side of a dep diff that isn't pinned to a git ref) and to resolve the
|
|
12
|
-
* merge-base for the default `--from` ref.
|
|
13
|
-
*
|
|
14
|
-
* @remarks
|
|
15
|
-
* `WorkspaceSnapshotReader` covers the git-ref side via `git show`. This
|
|
16
|
-
* module is the working-tree counterpart — staged and unstaged
|
|
17
|
-
* `package.json` edits show up here, matching `analyze-branch`'s
|
|
18
|
-
* coverage of the working tree.
|
|
19
|
-
*
|
|
20
|
-
* @internal
|
|
21
|
-
*/
|
|
22
|
-
/**
|
|
23
|
-
* Run `git merge-base <base> HEAD`, returning the SHA. Errors propagate
|
|
24
|
-
* as {@link GitError}.
|
|
25
|
-
*
|
|
26
|
-
* @internal
|
|
27
|
-
*/
|
|
28
|
-
function gitMergeBase(cwd, base) {
|
|
29
|
-
return Effect.try({
|
|
30
|
-
try: () => execFileSync("git", [
|
|
31
|
-
"merge-base",
|
|
32
|
-
base,
|
|
33
|
-
"HEAD"
|
|
34
|
-
], {
|
|
35
|
-
cwd,
|
|
36
|
-
encoding: "utf8",
|
|
37
|
-
stdio: [
|
|
38
|
-
"ignore",
|
|
39
|
-
"pipe",
|
|
40
|
-
"pipe"
|
|
41
|
-
]
|
|
42
|
-
}).trim(),
|
|
43
|
-
catch: (error) => {
|
|
44
|
-
const stderr = error.stderr;
|
|
45
|
-
const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
|
|
46
|
-
return new GitError({
|
|
47
|
-
command: `git merge-base ${base} HEAD`,
|
|
48
|
-
cwd,
|
|
49
|
-
reason: text.trim() || (error.message ?? String(error))
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Normalize a pnpm-workspace.yaml glob entry for filesystem expansion.
|
|
56
|
-
*
|
|
57
|
-
* `packages/**` collapses to `packages/*` (retains the wildcard so the
|
|
58
|
-
* caller hits the directory-listing path); `packages/*` and a literal
|
|
59
|
-
* `packages/foo` are passed through unchanged.
|
|
60
|
-
*
|
|
61
|
-
* Returning the literal-path form for `packages/**` (i.e., `"packages"`)
|
|
62
|
-
* would route the caller through the "no wildcards" branch and silently
|
|
63
|
-
* skip every child workspace.
|
|
64
|
-
*
|
|
65
|
-
* @internal
|
|
66
|
-
*/
|
|
67
|
-
function normalizeWorkspaceGlob(glob) {
|
|
68
|
-
return glob.replace(/\/\*\*$/, "/*");
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Read every workspace package's `package.json` from the live working
|
|
72
|
-
* tree, returning {@link WorkspaceSnapshot} entries matching the shape
|
|
73
|
-
* `WorkspaceSnapshotReader.snapshotAt` produces for git refs.
|
|
74
|
-
*
|
|
75
|
-
* @remarks
|
|
76
|
-
* Falls back to root-only when `pnpm-workspace.yaml` is missing or
|
|
77
|
-
* unparseable. Uses `node:fs.readdirSync` for directory expansion
|
|
78
|
-
* (portable across platforms — `execFileSync("ls")` is not).
|
|
79
|
-
*
|
|
80
|
-
* @internal
|
|
81
|
-
*/
|
|
82
|
-
function snapshotFromWorktree(cwd) {
|
|
83
|
-
const snapshots = [];
|
|
84
|
-
const dirs = /* @__PURE__ */ new Set([cwd]);
|
|
85
|
-
for (const dir of expandWorkspaceDirs(cwd)) dirs.add(dir);
|
|
86
|
-
for (const dir of dirs) try {
|
|
87
|
-
const pkgJson = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
88
|
-
if (!pkgJson.name) continue;
|
|
89
|
-
const rel = dir === cwd ? "." : dir.slice(cwd.length + 1);
|
|
90
|
-
snapshots.push({
|
|
91
|
-
name: pkgJson.name,
|
|
92
|
-
relativePath: rel,
|
|
93
|
-
version: pkgJson.version ?? "0.0.0",
|
|
94
|
-
dependencies: pkgJson.dependencies ?? {},
|
|
95
|
-
devDependencies: pkgJson.devDependencies ?? {},
|
|
96
|
-
peerDependencies: pkgJson.peerDependencies ?? {},
|
|
97
|
-
optionalDependencies: pkgJson.optionalDependencies ?? {}
|
|
98
|
-
});
|
|
99
|
-
} catch {}
|
|
100
|
-
return snapshots;
|
|
101
|
-
}
|
|
102
|
-
function expandWorkspaceDirs(cwd) {
|
|
103
|
-
let yaml;
|
|
104
|
-
try {
|
|
105
|
-
yaml = readFileSync(join(cwd, "pnpm-workspace.yaml"), "utf8");
|
|
106
|
-
} catch {
|
|
107
|
-
return [];
|
|
108
|
-
}
|
|
109
|
-
const dirs = [];
|
|
110
|
-
const lines = yaml.split(/\r?\n/);
|
|
111
|
-
let inPackagesBlock = false;
|
|
112
|
-
for (const line of lines) {
|
|
113
|
-
if (/^\s*#/.test(line)) continue;
|
|
114
|
-
if (/^\s*packages\s*:\s*$/.test(line)) {
|
|
115
|
-
inPackagesBlock = true;
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (!inPackagesBlock) continue;
|
|
119
|
-
const m = line.match(/^\s+-\s+["']?(.+?)["']?\s*$/);
|
|
120
|
-
if (m) {
|
|
121
|
-
const glob = normalizeWorkspaceGlob(m[1]);
|
|
122
|
-
if (glob.includes("*") || glob.includes("?")) {
|
|
123
|
-
const prefix = glob.includes("/") ? glob.slice(0, glob.lastIndexOf("/") + 1) : "";
|
|
124
|
-
let entries = [];
|
|
125
|
-
try {
|
|
126
|
-
entries = readdirSync(join(cwd, prefix || "."));
|
|
127
|
-
} catch {
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
const regex = new RegExp(`^${glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")}$`);
|
|
131
|
-
for (const entry of entries) {
|
|
132
|
-
const candidate = prefix ? `${prefix}${entry}` : entry;
|
|
133
|
-
if (regex.test(candidate)) dirs.push(join(cwd, candidate));
|
|
134
|
-
}
|
|
135
|
-
} else dirs.push(join(cwd, glob));
|
|
136
|
-
} else if (line.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) inPackagesBlock = false;
|
|
137
|
-
}
|
|
138
|
-
return dirs;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
//#endregion
|
|
142
|
-
export { gitMergeBase, normalizeWorkspaceGlob, snapshotFromWorktree };
|