@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.
@@ -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 };