@savvy-web/silk 1.3.7 → 1.3.8
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-changelog.cjs +4429 -7015
- package/changesets-changelog.d.cts +1129 -938
- package/changesets-changelog.d.ts +1129 -938
- package/changesets-markdownlint.cjs +4429 -7015
- package/changesets-markdownlint.d.cts +1129 -938
- package/changesets-markdownlint.d.ts +1129 -938
- package/package.json +4 -4
- package/packages/silk-effects/dist/dev/pkg/changesets/errors.cjs +45 -0
- package/packages/silk-effects/dist/dev/pkg/changesets/errors.js +44 -1
- package/packages/silk-effects/dist/dev/pkg/changesets/index.cjs +16 -21
- package/packages/silk-effects/dist/dev/pkg/changesets/index.js +8 -11
- package/packages/silk-effects/dist/dev/pkg/changesets/services/config-inspector.cjs +4 -4
- package/packages/silk-effects/dist/dev/pkg/changesets/services/config-inspector.js +1 -1
- package/packages/silk-effects/dist/dev/pkg/changesets/services/deps-regen.cjs +159 -115
- package/packages/silk-effects/dist/dev/pkg/changesets/services/deps-regen.js +157 -113
- package/packages/silk-effects/dist/dev/pkg/changesets/services/release-planner.cjs +105 -81
- package/packages/silk-effects/dist/dev/pkg/changesets/services/release-planner.js +105 -81
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/dep-diff.cjs +81 -47
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/dep-diff.js +81 -47
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/git.cjs +51 -0
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/git.js +51 -0
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/publishability.cjs +16 -4
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/publishability.js +15 -3
- package/packages/silk-effects/dist/dev/pkg/services/ChangesetConfig.cjs +2 -1
- package/packages/silk-effects/dist/dev/pkg/services/ChangesetConfig.js +1 -1
- package/packages/silk-effects/dist/dev/pkg/services/ChangesetConfigReader.cjs +2 -1
- package/packages/silk-effects/dist/dev/pkg/services/ChangesetConfigReader.js +1 -1
- package/packages/silk-effects/dist/dev/pkg/services/SilkPublishability.cjs +15 -11
- package/packages/silk-effects/dist/dev/pkg/services/SilkPublishability.js +5 -2
- package/tsconfig/node/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/silk-effects/dist/dev/pkg/changesets/services/workspace-snapshot.cjs +0 -162
- package/packages/silk-effects/dist/dev/pkg/changesets/services/workspace-snapshot.js +0 -160
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/worktree-snapshot.cjs +0 -144
- package/packages/silk-effects/dist/dev/pkg/changesets/utils/worktree-snapshot.js +0 -142
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import { ChangesetIOError } from "../errors.js";
|
|
1
2
|
import { serializeDependencyTableToMarkdown, sortDependencyRows } from "../utils/dependency-table.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { ChangesetConfigReaderLive } from "../../services/ChangesetConfigReader.js";
|
|
4
|
+
import { ChangesetConfig, ChangesetConfigLive } from "../../services/ChangesetConfig.js";
|
|
5
|
+
import { WorkspaceDiscovery } from "../../../../../../../node_modules/.pnpm/workspaces-effect@2.0.0_@effect_platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/services/WorkspaceDiscovery.js";
|
|
6
|
+
import { PointInTimeWorkspace } from "../../../../../../../node_modules/.pnpm/workspaces-effect@2.0.0_@effect_platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/services/PointInTimeWorkspace.js";
|
|
7
|
+
import { PointInTimeWorkspaceLive } from "../../../../../../../node_modules/.pnpm/workspaces-effect@2.0.0_@effect_platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/layers/PointInTimeWorkspaceLive.js";
|
|
8
|
+
import { PublishabilityDetector } from "../../../../../../../node_modules/.pnpm/workspaces-effect@2.0.0_@effect_platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/services/PublishabilityDetector.js";
|
|
9
|
+
import { WorkspaceDiscoveryLive } from "../../../../../../../node_modules/.pnpm/workspaces-effect@2.0.0_@effect_platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/layers/WorkspaceDiscoveryLive.js";
|
|
10
|
+
import { WorkspaceRootLive } from "../../../../../../../node_modules/.pnpm/workspaces-effect@2.0.0_@effect_platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/layers/WorkspaceRootLive.js";
|
|
11
|
+
import { PublishabilityDetectorAdaptiveLive } from "../../services/SilkPublishability.js";
|
|
12
|
+
import { ConfigInspector, ConfigInspectorLive } from "./config-inspector.js";
|
|
4
13
|
import { computeWorkspaceDependencyDiffs } from "../utils/dep-diff.js";
|
|
14
|
+
import { gitMergeBase } from "../utils/git.js";
|
|
5
15
|
import { listPublishablePackageNames } from "../utils/publishability.js";
|
|
6
|
-
import { gitMergeBase, snapshotFromWorktree } from "../utils/worktree-snapshot.js";
|
|
7
|
-
import { WorkspaceSnapshotReader } from "./workspace-snapshot.js";
|
|
8
16
|
import { Context, Effect, Layer, Option } from "effect";
|
|
9
17
|
import { join, resolve } from "node:path";
|
|
10
|
-
import {
|
|
18
|
+
import { FileSystem } from "@effect/platform";
|
|
11
19
|
|
|
12
20
|
//#region ../silk-effects/dist/dev/pkg/changesets/services/deps-regen.js
|
|
13
21
|
/**
|
|
@@ -17,9 +25,11 @@ import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from
|
|
|
17
25
|
*
|
|
18
26
|
* @remarks
|
|
19
27
|
* `plan()` computes the cumulative dependency diff (merge-base → working
|
|
20
|
-
* tree by default, or between two explicit refs)
|
|
21
|
-
*
|
|
22
|
-
*
|
|
28
|
+
* tree by default, or between two explicit refs) by snapshotting both
|
|
29
|
+
* sides through `PointInTimeWorkspace`, which resolves `catalog:` /
|
|
30
|
+
* `workspace:` specifiers against each ref's own catalogs and package
|
|
31
|
+
* versions before diffing. It drops `devDependency` rows (unless
|
|
32
|
+
* `includeDevDeps`), and returns a complete {@link RegenPlan}
|
|
23
33
|
* (target filenames + stale-changeset deletes) WITHOUT touching the
|
|
24
34
|
* filesystem. `execute()` applies a plan — writing the fresh changesets
|
|
25
35
|
* first, then deleting the stale pure-dependency ones (so an interrupted
|
|
@@ -32,41 +42,6 @@ import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from
|
|
|
32
42
|
* @see {@link DepsRegenLive} for the production layer
|
|
33
43
|
*
|
|
34
44
|
*/
|
|
35
|
-
/** The em-dash sentinel (U+2014) used for added ("from") / removed ("to") cells. */
|
|
36
|
-
const EM_DASH = "—";
|
|
37
|
-
/** Whether a From/To cell holds a pnpm protocol specifier that could resolve to a version. */
|
|
38
|
-
const isProtocol = (v) => /^(?:catalog|workspace|npm|jsr|file|link|portal):/.test(v);
|
|
39
|
-
/**
|
|
40
|
-
* Resolve protocol From/To cells to concrete versions (raw-string fallback
|
|
41
|
-
* when unresolved or on resolver error), leave em-dash sentinels untouched,
|
|
42
|
-
* then optionally drop `devDependency` rows, and re-sort.
|
|
43
|
-
*
|
|
44
|
-
* @param diff - One workspace package's dependency-table rows.
|
|
45
|
-
* @param keepDevDeps - When `true`, retain `devDependency` rows; otherwise
|
|
46
|
-
* drop them unconditionally (the regen default).
|
|
47
|
-
* @returns An Effect yielding the transformed {@link WorkspaceDependencyDiff}.
|
|
48
|
-
*
|
|
49
|
-
* @public
|
|
50
|
-
*/
|
|
51
|
-
const resolveDiffRows = (diff, keepDevDeps = false) => Effect.gen(function* () {
|
|
52
|
-
const resolver = yield* CatalogResolver;
|
|
53
|
-
const resolveCell = (dep, value) => isProtocol(value) ? resolver.resolveSpecifier(dep, value).pipe(Effect.map((opt) => Option.getOrElse(opt, () => value)), Effect.catchAll((error) => Effect.logWarning(`DepsRegen: catalog resolution failed for "${dep}" (${value}); keeping raw specifier: ${String(error)}`).pipe(Effect.as(value)))) : Effect.succeed(value);
|
|
54
|
-
const rows = [];
|
|
55
|
-
for (const row of diff.rows) {
|
|
56
|
-
if (!keepDevDeps && row.type === "devDependency") continue;
|
|
57
|
-
const from = row.from === EM_DASH ? EM_DASH : yield* resolveCell(row.dependency, row.from);
|
|
58
|
-
const to = row.to === EM_DASH ? EM_DASH : yield* resolveCell(row.dependency, row.to);
|
|
59
|
-
rows.push({
|
|
60
|
-
...row,
|
|
61
|
-
from,
|
|
62
|
-
to
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
...diff,
|
|
67
|
-
rows: sortDependencyRows(rows)
|
|
68
|
-
};
|
|
69
|
-
});
|
|
70
45
|
const ADJECTIVES = [
|
|
71
46
|
"brave",
|
|
72
47
|
"clever",
|
|
@@ -110,30 +85,35 @@ function pickRandomTriplet() {
|
|
|
110
85
|
* Pick a `<adjective>-<noun>-<verb>` filename slug that does not collide
|
|
111
86
|
* with an existing `.changeset/*.md` OR with a slug already claimed
|
|
112
87
|
* earlier in the same {@link RegenPlan.toWrite} computation. `plan()`
|
|
113
|
-
* never writes to disk, so
|
|
114
|
-
* moments earlier in the same call — the `chosen` set closes
|
|
115
|
-
* The triplet space is 1,000 combinations, so a busy repo can
|
|
116
|
-
* exhaust it across runs; fall back to a timestamp suffix after
|
|
117
|
-
* unlucky picks.
|
|
88
|
+
* never writes to disk, so an on-disk existence check alone cannot see
|
|
89
|
+
* slugs chosen moments earlier in the same call — the `chosen` set closes
|
|
90
|
+
* that gap. The triplet space is 1,000 combinations, so a busy repo can
|
|
91
|
+
* plausibly exhaust it across runs; fall back to a timestamp suffix after
|
|
92
|
+
* 20 unlucky picks.
|
|
118
93
|
*
|
|
119
|
-
* @param
|
|
94
|
+
* @param fileExists - Effectful on-disk existence check (never fails; a
|
|
95
|
+
* filesystem error is treated as "does not exist" so filename selection
|
|
96
|
+
* degrades gracefully rather than blocking the plan).
|
|
97
|
+
* @param changesetDir - Directory checked for on-disk collisions.
|
|
120
98
|
* @param chosen - Basenames (without extension) already picked within this plan;
|
|
121
99
|
* the picked candidate is added to this set before returning.
|
|
122
100
|
* @internal
|
|
123
101
|
*/
|
|
124
|
-
function randomFilename(changesetDir, chosen) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
chosen.
|
|
129
|
-
|
|
102
|
+
function randomFilename(fileExists, changesetDir, chosen) {
|
|
103
|
+
return Effect.gen(function* () {
|
|
104
|
+
for (let i = 0; i < 20; i++) {
|
|
105
|
+
const candidate = pickRandomTriplet();
|
|
106
|
+
if (!chosen.has(candidate) && !(yield* fileExists(join(changesetDir, `${candidate}.md`)))) {
|
|
107
|
+
chosen.add(candidate);
|
|
108
|
+
return candidate;
|
|
109
|
+
}
|
|
130
110
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
111
|
+
let attempt = 0;
|
|
112
|
+
let fallback = `${pickRandomTriplet()}-${Date.now()}`;
|
|
113
|
+
while (chosen.has(fallback) || (yield* fileExists(join(changesetDir, `${fallback}.md`)))) fallback = `${pickRandomTriplet()}-${Date.now()}-${++attempt}`;
|
|
114
|
+
chosen.add(fallback);
|
|
115
|
+
return fallback;
|
|
116
|
+
});
|
|
137
117
|
}
|
|
138
118
|
/**
|
|
139
119
|
* Strict detection of "pure dependency changesets" per the documented
|
|
@@ -184,43 +164,59 @@ function isPureDependencyChangeset(content) {
|
|
|
184
164
|
package: pkg
|
|
185
165
|
};
|
|
186
166
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
167
|
+
/**
|
|
168
|
+
* List `.changeset/*.md` files (excluding `README.md`) in `changesetDir`.
|
|
169
|
+
* A missing directory is today's behavior for "no changesets yet" and
|
|
170
|
+
* resolves to an empty list; any OTHER failure (e.g. a directory that
|
|
171
|
+
* exists but cannot be read) is a loud {@link ChangesetIOError} — an
|
|
172
|
+
* unreadable `.changeset` dir must not silently masquerade as "no
|
|
173
|
+
* changesets".
|
|
174
|
+
*/
|
|
175
|
+
const listChangesetFiles = (fs, changesetDir) => fs.readDirectory(changesetDir).pipe(Effect.map((names) => names.filter((f) => f.endsWith(".md") && f !== "README.md").map((f) => join(changesetDir, f))), Effect.catchAll((e) => e._tag === "SystemError" && e.reason === "NotFound" ? Effect.succeed([]) : Effect.fail(new ChangesetIOError({
|
|
176
|
+
path: changesetDir,
|
|
177
|
+
operation: "list",
|
|
178
|
+
reason: String(e)
|
|
179
|
+
}))));
|
|
180
|
+
/**
|
|
181
|
+
* Pure-dependency changesets found in `changesetDir`. An individual file
|
|
182
|
+
* that cannot be read (e.g. removed mid-scan, permission race) is skipped —
|
|
183
|
+
* today's semantics — while a failure to list the directory itself
|
|
184
|
+
* propagates as a loud {@link ChangesetIOError}.
|
|
185
|
+
*/
|
|
186
|
+
const findPureDependencyChangesets = (fs, changesetDir) => Effect.gen(function* () {
|
|
187
|
+
const files = yield* listChangesetFiles(fs, changesetDir);
|
|
192
188
|
const result = [];
|
|
193
|
-
for (const file of
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
} catch {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
const detection = isPureDependencyChangeset(content);
|
|
189
|
+
for (const file of files) {
|
|
190
|
+
const content = yield* fs.readFileString(file).pipe(Effect.option);
|
|
191
|
+
if (Option.isNone(content)) continue;
|
|
192
|
+
const detection = isPureDependencyChangeset(content.value);
|
|
201
193
|
if (detection.isPure && detection.package) result.push({
|
|
202
194
|
file,
|
|
203
195
|
package: detection.package
|
|
204
196
|
});
|
|
205
197
|
}
|
|
206
198
|
return result;
|
|
207
|
-
}
|
|
208
|
-
|
|
199
|
+
});
|
|
200
|
+
/**
|
|
201
|
+
* Mixed dependency changesets (have a `## Dependencies` heading but fail
|
|
202
|
+
* the strict pure-changeset test) found in `changesetDir`. Same
|
|
203
|
+
* skip-unreadable-file / loud-list-failure semantics as
|
|
204
|
+
* {@link findPureDependencyChangesets}.
|
|
205
|
+
*/
|
|
206
|
+
const findMixedDependencyChangesets = (fs, changesetDir) => Effect.gen(function* () {
|
|
207
|
+
const files = yield* listChangesetFiles(fs, changesetDir);
|
|
209
208
|
const result = [];
|
|
210
|
-
for (const file of
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
} catch {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (/^## Dependencies\b/m.test(content) && !isPureDependencyChangeset(content).isPure) result.push(file);
|
|
209
|
+
for (const file of files) {
|
|
210
|
+
const content = yield* fs.readFileString(file).pipe(Effect.option);
|
|
211
|
+
if (Option.isNone(content)) continue;
|
|
212
|
+
if (/^## Dependencies\b/m.test(content.value) && !isPureDependencyChangeset(content.value).isPure) result.push(file);
|
|
218
213
|
}
|
|
219
214
|
return result;
|
|
220
|
-
}
|
|
215
|
+
});
|
|
221
216
|
/**
|
|
222
|
-
* Render a single-package, patch-bump changeset for a diff whose rows
|
|
223
|
-
* already
|
|
217
|
+
* Render a single-package, patch-bump changeset for a diff whose rows are
|
|
218
|
+
* already resolved (per-side, inside {@link computeWorkspaceDependencyDiffs})
|
|
219
|
+
* and devDep-filtered.
|
|
224
220
|
*/
|
|
225
221
|
function renderChangesetContent(diff) {
|
|
226
222
|
return `${`---\n"${diff.package}": patch\n---`}\n\n## Dependencies\n\n${serializeDependencyTableToMarkdown([...diff.rows])}\n`;
|
|
@@ -252,9 +248,9 @@ var DepsRegen = class extends DepsRegenBase {};
|
|
|
252
248
|
* implementations, keeping the public `plan`/`execute` signatures
|
|
253
249
|
* requirement-free (`R = never`).
|
|
254
250
|
*/
|
|
255
|
-
function makeShape(
|
|
256
|
-
const provideResolver = Layer.succeed(CatalogResolver, resolver);
|
|
251
|
+
function makeShape(pit, inspector, discovery, detector, config, fs) {
|
|
257
252
|
const provideDetector = Layer.succeed(PublishabilityDetector, detector);
|
|
253
|
+
const fileExists = (p) => fs.exists(p).pipe(Effect.orElseSucceed(() => false));
|
|
258
254
|
const plan = (options) => Effect.gen(function* () {
|
|
259
255
|
const resolvedCwd = resolve(options.cwd);
|
|
260
256
|
const changesetDir = join(resolvedCwd, ".changeset");
|
|
@@ -264,41 +260,58 @@ function makeShape(reader, inspector, discovery, resolver, detector) {
|
|
|
264
260
|
if (!baseBranch) baseBranch = (yield* inspector.inspect(resolvedCwd).pipe(Effect.catchTag("ConfigurationError", () => Effect.succeed({ baseBranch: "main" })))).baseBranch;
|
|
265
261
|
fromRef = yield* gitMergeBase(resolvedCwd, baseBranch);
|
|
266
262
|
}
|
|
267
|
-
const rawDiffs = computeWorkspaceDependencyDiffs(yield*
|
|
263
|
+
const rawDiffs = computeWorkspaceDependencyDiffs(yield* pit.at(fromRef, { cwd: resolvedCwd }), options.to ? yield* pit.at(options.to, { cwd: resolvedCwd }) : yield* pit.worktree({ cwd: resolvedCwd }));
|
|
268
264
|
const targetPkg = options.package;
|
|
269
|
-
const
|
|
265
|
+
const livePackages = yield* discovery.listPackages(resolvedCwd);
|
|
266
|
+
const publishable = yield* listPublishablePackageNames(livePackages, resolvedCwd).pipe(Effect.provide(provideDetector));
|
|
267
|
+
const versionPrivate = yield* config.versionPrivate(resolvedCwd);
|
|
268
|
+
const inScope = /* @__PURE__ */ new Set();
|
|
269
|
+
for (const pkg of livePackages) {
|
|
270
|
+
if (yield* config.isIgnored(pkg.name, resolvedCwd)) continue;
|
|
271
|
+
if (publishable.has(pkg.name) || versionPrivate) inScope.add(pkg.name);
|
|
272
|
+
}
|
|
273
|
+
const targetIgnored = targetPkg ? yield* config.isIgnored(targetPkg, resolvedCwd) : false;
|
|
270
274
|
const keepDevDeps = options.includeDevDeps === true;
|
|
271
|
-
const scoped = targetPkg ? rawDiffs.filter((d) => d.package === targetPkg) : rawDiffs.filter((d) =>
|
|
275
|
+
const scoped = targetPkg ? targetIgnored ? [] : rawDiffs.filter((d) => d.package === targetPkg) : rawDiffs.filter((d) => inScope.has(d.package));
|
|
272
276
|
const resolved = [];
|
|
273
277
|
for (const diff of scoped) {
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
278
|
+
const rows = keepDevDeps ? [...diff.rows] : diff.rows.filter((r) => r.type !== "devDependency");
|
|
279
|
+
if (rows.length > 0) resolved.push({
|
|
280
|
+
...diff,
|
|
281
|
+
rows: sortDependencyRows(rows)
|
|
282
|
+
});
|
|
276
283
|
}
|
|
277
|
-
const existingPure = findPureDependencyChangesets(changesetDir);
|
|
278
|
-
const skippedMixed = findMixedDependencyChangesets(changesetDir);
|
|
279
|
-
const toDelete = targetPkg ? existingPure.filter((p) => p.package === targetPkg) : existingPure.filter((p) =>
|
|
284
|
+
const existingPure = yield* findPureDependencyChangesets(fs, changesetDir);
|
|
285
|
+
const skippedMixed = yield* findMixedDependencyChangesets(fs, changesetDir);
|
|
286
|
+
const toDelete = targetPkg ? targetIgnored ? [] : existingPure.filter((p) => p.package === targetPkg) : existingPure.filter((p) => inScope.has(p.package));
|
|
280
287
|
const chosenFilenames = /* @__PURE__ */ new Set();
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
288
|
+
const toWrite = [];
|
|
289
|
+
for (const diff of resolved) {
|
|
290
|
+
const filename = yield* randomFilename(fileExists, changesetDir, chosenFilenames);
|
|
291
|
+
toWrite.push({
|
|
292
|
+
file: join(changesetDir, `${filename}.md`),
|
|
285
293
|
package: diff.package,
|
|
286
294
|
diff
|
|
287
|
-
})
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
toDelete,
|
|
299
|
+
toWrite,
|
|
288
300
|
skippedMixed
|
|
289
301
|
};
|
|
290
302
|
});
|
|
291
|
-
const execute = (plan) => Effect.
|
|
303
|
+
const execute = (plan) => Effect.gen(function* () {
|
|
292
304
|
const deleted = [];
|
|
293
305
|
const written = [];
|
|
294
306
|
for (const entry of plan.toWrite) {
|
|
295
|
-
|
|
307
|
+
yield* fs.writeFileString(entry.file, renderChangesetContent(entry.diff)).pipe(Effect.mapError((e) => new ChangesetIOError({
|
|
308
|
+
path: entry.file,
|
|
309
|
+
operation: "write",
|
|
310
|
+
reason: String(e)
|
|
311
|
+
})));
|
|
296
312
|
written.push(entry.file);
|
|
297
313
|
}
|
|
298
|
-
for (const entry of plan.toDelete)
|
|
299
|
-
unlinkSync(entry.file);
|
|
300
|
-
deleted.push(entry.file);
|
|
301
|
-
} catch {}
|
|
314
|
+
for (const entry of plan.toDelete) if (yield* Effect.isSuccess(fs.remove(entry.file))) deleted.push(entry.file);
|
|
302
315
|
return {
|
|
303
316
|
deleted,
|
|
304
317
|
written,
|
|
@@ -313,15 +326,46 @@ function makeShape(reader, inspector, discovery, resolver, detector) {
|
|
|
313
326
|
/**
|
|
314
327
|
* Live layer for {@link DepsRegen}.
|
|
315
328
|
*
|
|
316
|
-
* Requires
|
|
317
|
-
* `
|
|
318
|
-
*
|
|
329
|
+
* Requires `PointInTimeWorkspace`, `WorkspaceDiscovery`,
|
|
330
|
+
* `PublishabilityDetector` (all from `workspaces-effect`),
|
|
331
|
+
* {@link ConfigInspector}, {@link ChangesetConfig}, and
|
|
332
|
+
* `FileSystem.FileSystem` (resolved once at construction and closed over by
|
|
333
|
+
* the shape, keeping `plan`/`execute` themselves requirement-free).
|
|
319
334
|
*
|
|
320
335
|
* @public
|
|
321
336
|
*/
|
|
322
337
|
const DepsRegenLive = Layer.effect(DepsRegen, Effect.gen(function* () {
|
|
323
|
-
return makeShape(yield*
|
|
338
|
+
return makeShape(yield* PointInTimeWorkspace, yield* ConfigInspector, yield* WorkspaceDiscovery, yield* PublishabilityDetector, yield* ChangesetConfig, yield* FileSystem.FileSystem);
|
|
324
339
|
}));
|
|
340
|
+
const WorkspaceGraph = Layer.mergeAll(WorkspaceRootLive, WorkspaceDiscoveryLive.pipe(Layer.provide(WorkspaceRootLive)));
|
|
341
|
+
const ConfigGraph = ChangesetConfigLive.pipe(Layer.provide(ChangesetConfigReaderLive));
|
|
342
|
+
/**
|
|
343
|
+
* Batteries-included {@link DepsRegen} layer: silk's opinionated default
|
|
344
|
+
* composition of the full dependency graph. Only the platform services
|
|
345
|
+
* remain — note that {@link PointInTimeWorkspace} reads git history, so
|
|
346
|
+
* this layer genuinely requires `CommandExecutor` in addition to
|
|
347
|
+
* `FileSystem`/`Path`: provide a git-capable platform layer
|
|
348
|
+
* (`NodeContext.layer`), not a bare filesystem-only layer.
|
|
349
|
+
*
|
|
350
|
+
* Gating uses silk's adaptive publishability detector
|
|
351
|
+
* ({@link PublishabilityDetectorAdaptiveLive}), so the default semantics
|
|
352
|
+
* are "versionable minus ignored" — identical to the savvy CLI and MCP
|
|
353
|
+
* runtimes. Consumers who need to swap any dependency (test detectors,
|
|
354
|
+
* alternate config sources) should keep composing {@link DepsRegenLive}
|
|
355
|
+
* directly; this layer is purely additive.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```typescript
|
|
359
|
+
* import { NodeContext } from "@effect/platform-node";
|
|
360
|
+
* import { Layer } from "effect";
|
|
361
|
+
* import { Changesets } from "@savvy-web/silk-effects";
|
|
362
|
+
*
|
|
363
|
+
* const depsRegen = Changesets.DepsRegenDefault.pipe(Layer.provide(NodeContext.layer));
|
|
364
|
+
* ```
|
|
365
|
+
*
|
|
366
|
+
* @public
|
|
367
|
+
*/
|
|
368
|
+
const DepsRegenDefault = DepsRegenLive.pipe(Layer.provide(PointInTimeWorkspaceLive.pipe(Layer.provide(WorkspaceGraph))), Layer.provide(ConfigInspectorLive.pipe(Layer.provide(Layer.mergeAll(ChangesetConfigReaderLive, WorkspaceGraph)))), Layer.provide(PublishabilityDetectorAdaptiveLive.pipe(Layer.provide(ConfigGraph))), Layer.provide(ConfigGraph), Layer.provide(WorkspaceGraph));
|
|
325
369
|
|
|
326
370
|
//#endregion
|
|
327
|
-
export { DepsRegen, DepsRegenBase, DepsRegenLive, isPureDependencyChangeset
|
|
371
|
+
export { DepsRegen, DepsRegenBase, DepsRegenDefault, DepsRegenLive, isPureDependencyChangeset };
|
|
@@ -8,8 +8,7 @@ const require_changesets_apply_release_plan_esm = require('../../../../../../../
|
|
|
8
8
|
const require_changesets_get_release_plan_esm = require('../../../../../../../node_modules/.pnpm/@changesets_get-release-plan@4.0.16/node_modules/@changesets/get-release-plan/dist/changesets-get-release-plan.esm.cjs');
|
|
9
9
|
let effect = require("effect");
|
|
10
10
|
let node_path = require("node:path");
|
|
11
|
-
let
|
|
12
|
-
let node_os = require("node:os");
|
|
11
|
+
let _effect_platform = require("@effect/platform");
|
|
13
12
|
|
|
14
13
|
//#region ../silk-effects/dist/dev/pkg/changesets/services/release-planner.js
|
|
15
14
|
var import_get_packages_cjs = require_get_packages_cjs$1.default;
|
|
@@ -37,8 +36,8 @@ const errMsg = (e) => e instanceof Error ? e.message : String(e);
|
|
|
37
36
|
const ReleasePlannerBase = effect.Context.Tag("ReleasePlanner")();
|
|
38
37
|
/** Effect service tag for the release planner. @public */
|
|
39
38
|
var ReleasePlanner = class extends ReleasePlannerBase {};
|
|
40
|
-
/** Build the service shape over a resolved {@link ConfigInspector}. */
|
|
41
|
-
function makeShape(inspector) {
|
|
39
|
+
/** Build the service shape over a resolved {@link ConfigInspector} and {@link FileSystem.FileSystem}. */
|
|
40
|
+
function makeShape(inspector, fs) {
|
|
42
41
|
const plan = (root) => effect.Effect.tryPromise({
|
|
43
42
|
try: () => require_changesets_get_release_plan_esm.default(root),
|
|
44
43
|
catch: (e) => new require_errors.ReleasePlanError({
|
|
@@ -46,23 +45,17 @@ function makeShape(inspector) {
|
|
|
46
45
|
reason: errMsg(e)
|
|
47
46
|
})
|
|
48
47
|
});
|
|
49
|
-
const preview = (root) =>
|
|
50
|
-
|
|
51
|
-
catch: (e) => new require_errors.ReleasePlanError({
|
|
52
|
-
phase: "preview",
|
|
53
|
-
reason: errMsg(e)
|
|
54
|
-
})
|
|
55
|
-
});
|
|
56
|
-
const apply = (root, options) => applyEffect(root, options?.dryRun ?? false, inspector);
|
|
48
|
+
const preview = (root) => previewEffect(root, fs);
|
|
49
|
+
const apply = (root, options) => applyEffect(root, options?.dryRun ?? false, inspector, fs);
|
|
57
50
|
return {
|
|
58
51
|
plan,
|
|
59
52
|
preview,
|
|
60
53
|
apply
|
|
61
54
|
};
|
|
62
55
|
}
|
|
63
|
-
/** Production layer. Requires {@link ConfigInspector} (used by `apply`)
|
|
56
|
+
/** Production layer. Requires {@link ConfigInspector} (used by `apply`) and `FileSystem`. @public */
|
|
64
57
|
const ReleasePlannerLive = effect.Layer.effect(ReleasePlanner, effect.Effect.gen(function* () {
|
|
65
|
-
return makeShape(yield* require_config_inspector.ConfigInspector);
|
|
58
|
+
return makeShape(yield* require_config_inspector.ConfigInspector, yield* _effect_platform.FileSystem.FileSystem);
|
|
66
59
|
}));
|
|
67
60
|
/**
|
|
68
61
|
* Test factory — supply fixed results for any subset of methods. Unsupplied
|
|
@@ -93,31 +86,52 @@ function extractVersionBlock(changelog, version) {
|
|
|
93
86
|
}
|
|
94
87
|
return lines.slice(start, end).join("\n").trim();
|
|
95
88
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Render a non-destructive preview by redirecting every write into a
|
|
91
|
+
* scope-managed temp directory (cleaned up automatically when the scope
|
|
92
|
+
* closes) and reading the generated CHANGELOG blocks back.
|
|
93
|
+
*/
|
|
94
|
+
function previewEffect(root, fs) {
|
|
95
|
+
const program = effect.Effect.gen(function* () {
|
|
96
|
+
const [plan, packages] = yield* effect.Effect.tryPromise({
|
|
97
|
+
try: () => Promise.all([require_changesets_get_release_plan_esm.default(root), buildPackages(root)]),
|
|
98
|
+
catch: (e) => new require_errors.ReleasePlanError({
|
|
99
|
+
phase: "preview",
|
|
100
|
+
reason: errMsg(e)
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
const config = yield* effect.Effect.tryPromise({
|
|
104
|
+
try: () => require_changesets_config_esm.read(root, packages),
|
|
105
|
+
catch: (e) => new require_errors.ReleasePlanError({
|
|
106
|
+
phase: "preview",
|
|
107
|
+
reason: errMsg(e)
|
|
108
|
+
})
|
|
109
|
+
});
|
|
110
|
+
const preMode = plan.preState ? plan.preState.mode : null;
|
|
111
|
+
const changesets = plan.changesets.map((cs) => ({
|
|
112
|
+
id: cs.id,
|
|
113
|
+
summary: cs.summary,
|
|
114
|
+
releases: cs.releases.filter((r) => r.type !== "none").map((r) => ({
|
|
115
|
+
name: r.name,
|
|
116
|
+
type: r.type
|
|
117
|
+
}))
|
|
118
|
+
}));
|
|
119
|
+
const releasesToRender = plan.releases.filter((r) => r.type !== "none");
|
|
120
|
+
if (releasesToRender.length === 0) return {
|
|
121
|
+
preMode,
|
|
122
|
+
releases: [],
|
|
123
|
+
changesets
|
|
124
|
+
};
|
|
125
|
+
const tempRoot = yield* fs.makeTempDirectoryScoped({ prefix: "silk-preview-" });
|
|
116
126
|
const mapDir = (dir) => {
|
|
117
127
|
const rel = (0, node_path.relative)(packages.root.dir, dir);
|
|
118
|
-
if (rel.startsWith("..") || (0, node_path.isAbsolute)(rel))
|
|
119
|
-
|
|
128
|
+
if (rel.startsWith("..") || (0, node_path.isAbsolute)(rel)) return effect.Effect.fail(new require_errors.ReleasePlanError({
|
|
129
|
+
phase: "preview",
|
|
130
|
+
reason: `Package directory is outside the workspace root: ${dir}`
|
|
131
|
+
}));
|
|
132
|
+
return effect.Effect.succeed((0, node_path.join)(tempRoot, rel));
|
|
120
133
|
};
|
|
134
|
+
const tempDirs = yield* effect.Effect.forEach(packages.packages, (p) => mapDir(p.dir));
|
|
121
135
|
const tempPackages = {
|
|
122
136
|
tool: packages.tool,
|
|
123
137
|
root: {
|
|
@@ -125,26 +139,33 @@ async function previewImpl(root) {
|
|
|
125
139
|
dir: tempRoot,
|
|
126
140
|
packageJson: structuredClone(packages.root.packageJson)
|
|
127
141
|
},
|
|
128
|
-
packages: packages.packages.map((p) => ({
|
|
142
|
+
packages: packages.packages.map((p, i) => ({
|
|
129
143
|
...p,
|
|
130
|
-
dir:
|
|
144
|
+
dir: tempDirs[i],
|
|
131
145
|
packageJson: structuredClone(p.packageJson)
|
|
132
146
|
}))
|
|
133
147
|
};
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
yield* fs.makeDirectory((0, node_path.join)(tempRoot, ".changeset"), { recursive: true });
|
|
149
|
+
yield* fs.copyFile((0, node_path.join)(root, "package.json"), (0, node_path.join)(tempRoot, "package.json"));
|
|
136
150
|
const preJson = (0, node_path.join)(root, ".changeset", "pre.json");
|
|
137
|
-
if (
|
|
138
|
-
for (
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
151
|
+
if (yield* fs.exists(preJson)) yield* fs.copyFile(preJson, (0, node_path.join)(tempRoot, ".changeset", "pre.json"));
|
|
152
|
+
for (let i = 0; i < packages.packages.length; i++) {
|
|
153
|
+
const p = packages.packages[i];
|
|
154
|
+
const tDir = tempDirs[i];
|
|
155
|
+
yield* fs.makeDirectory(tDir, { recursive: true });
|
|
156
|
+
yield* fs.copyFile((0, node_path.join)(p.dir, "package.json"), (0, node_path.join)(tDir, "package.json"));
|
|
142
157
|
const realCl = (0, node_path.join)(p.dir, "CHANGELOG.md");
|
|
143
|
-
if (
|
|
158
|
+
if (yield* fs.exists(realCl)) yield* fs.copyFile(realCl, (0, node_path.join)(tDir, "CHANGELOG.md"));
|
|
144
159
|
}
|
|
145
160
|
const rootCl = (0, node_path.join)(packages.root.dir, "CHANGELOG.md");
|
|
146
|
-
if (
|
|
147
|
-
|
|
161
|
+
if (yield* fs.exists(rootCl)) yield* fs.copyFile(rootCl, (0, node_path.join)(tempRoot, "CHANGELOG.md"));
|
|
162
|
+
yield* effect.Effect.tryPromise({
|
|
163
|
+
try: () => require_changesets_apply_release_plan_esm.default(plan, tempPackages, config, void 0, root),
|
|
164
|
+
catch: (e) => new require_errors.ReleasePlanError({
|
|
165
|
+
phase: "preview",
|
|
166
|
+
reason: errMsg(e)
|
|
167
|
+
})
|
|
168
|
+
});
|
|
148
169
|
const dirByName = /* @__PURE__ */ new Map();
|
|
149
170
|
for (const p of tempPackages.packages) dirByName.set(p.packageJson.name, p.dir);
|
|
150
171
|
if (tempPackages.root.packageJson.name) dirByName.set(tempPackages.root.packageJson.name, tempRoot);
|
|
@@ -153,9 +174,15 @@ async function previewImpl(root) {
|
|
|
153
174
|
const dir = dirByName.get(r.name);
|
|
154
175
|
if (!dir) continue;
|
|
155
176
|
const clPath = (0, node_path.join)(dir, "CHANGELOG.md");
|
|
156
|
-
if (!(
|
|
157
|
-
|
|
158
|
-
|
|
177
|
+
if (!(yield* fs.exists(clPath))) continue;
|
|
178
|
+
yield* effect.Effect.try({
|
|
179
|
+
try: () => require_transformer.ChangelogTransformer.transformFile(clPath),
|
|
180
|
+
catch: (e) => new require_errors.ReleasePlanError({
|
|
181
|
+
phase: "preview",
|
|
182
|
+
reason: errMsg(e)
|
|
183
|
+
})
|
|
184
|
+
});
|
|
185
|
+
const content = yield* fs.readFileString(clPath);
|
|
159
186
|
releases.push({
|
|
160
187
|
name: r.name,
|
|
161
188
|
type: r.type,
|
|
@@ -170,22 +197,17 @@ async function previewImpl(root) {
|
|
|
170
197
|
releases,
|
|
171
198
|
changesets
|
|
172
199
|
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
200
|
+
});
|
|
201
|
+
return effect.Effect.scoped(program).pipe(effect.Effect.mapError((e) => e instanceof require_errors.ReleasePlanError ? e : new require_errors.ReleasePlanError({
|
|
202
|
+
phase: "preview",
|
|
203
|
+
reason: errMsg(e)
|
|
204
|
+
})));
|
|
179
205
|
}
|
|
180
|
-
/** Re-read a package's version from disk (post-bump) to feed versionFiles. */
|
|
181
|
-
function diskVersion(workspaceDir, fallback) {
|
|
182
|
-
try
|
|
183
|
-
return JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(workspaceDir, "package.json"), "utf-8")).version ?? fallback;
|
|
184
|
-
} catch {
|
|
185
|
-
return fallback;
|
|
186
|
-
}
|
|
206
|
+
/** Re-read a package's version from disk (post-bump) to feed versionFiles; unreadable/unparseable falls back. */
|
|
207
|
+
function diskVersion(workspaceDir, fallback, fs) {
|
|
208
|
+
return fs.readFileString((0, node_path.join)(workspaceDir, "package.json")).pipe(effect.Effect.flatMap((raw) => effect.Effect.try(() => JSON.parse(raw).version ?? fallback)), effect.Effect.orElseSucceed(() => fallback));
|
|
187
209
|
}
|
|
188
|
-
function applyEffect(root, dryRun, inspector) {
|
|
210
|
+
function applyEffect(root, dryRun, inspector, fs) {
|
|
189
211
|
return effect.Effect.gen(function* () {
|
|
190
212
|
const { plan, packages, config } = yield* effect.Effect.tryPromise({
|
|
191
213
|
try: async () => {
|
|
@@ -222,22 +244,24 @@ function applyEffect(root, dryRun, inspector) {
|
|
|
222
244
|
const newVersionByName = new Map(plan.releases.map((r) => [r.name, r.newVersion]));
|
|
223
245
|
const inspected = yield* inspector.inspect(root).pipe(effect.Effect.catchAll((error) => effect.Effect.logWarning(`Skipping versionFiles update: ${errMsg(error)}`).pipe(effect.Effect.as(null))));
|
|
224
246
|
let versionFileUpdates = [];
|
|
225
|
-
if (inspected)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
if (inspected) {
|
|
248
|
+
const candidates = inspected.packages.filter((p) => p.versionFiles.length > 0);
|
|
249
|
+
const freshVersions = yield* effect.Effect.forEach(candidates, (p) => dryRun ? effect.Effect.succeed(newVersionByName.get(p.name) ?? p.version) : diskVersion(p.workspaceDir, p.version, fs));
|
|
250
|
+
const scopes = candidates.map((p, i) => {
|
|
251
|
+
const fresh = freshVersions[i];
|
|
252
|
+
return fresh !== p.version ? {
|
|
253
|
+
...p,
|
|
254
|
+
version: fresh
|
|
255
|
+
} : p;
|
|
256
|
+
});
|
|
257
|
+
versionFileUpdates = yield* effect.Effect.try({
|
|
258
|
+
try: () => scopes.length > 0 ? require_version_files.VersionFiles.processResolvedVersionFiles(scopes, dryRun) : [],
|
|
259
|
+
catch: (e) => new require_errors.ReleasePlanError({
|
|
260
|
+
phase: "apply",
|
|
261
|
+
reason: errMsg(e)
|
|
262
|
+
})
|
|
263
|
+
});
|
|
264
|
+
}
|
|
241
265
|
return {
|
|
242
266
|
dryRun,
|
|
243
267
|
touchedFiles,
|