@ucdjs/release-scripts 0.1.0-beta.32 → 0.1.0-beta.34

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.
Files changed (2) hide show
  1. package/dist/index.mjs +321 -199
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,21 +1,98 @@
1
1
  import { t as Eta } from "./eta-BV8TCRDW.mjs";
2
2
  import { Console, Context, Data, Effect, Layer, Schema } from "effect";
3
+ import process from "node:process";
3
4
  import path from "node:path";
4
5
  import { Command, CommandExecutor } from "@effect/platform";
5
6
  import { NodeCommandExecutor, NodeFileSystem } from "@effect/platform-node";
6
7
  import * as CommitParser from "commit-parser";
7
- import process from "node:process";
8
8
  import semver from "semver";
9
9
  import fs from "node:fs/promises";
10
10
  import prompts from "prompts";
11
11
 
12
+ //#region src/options.ts
13
+ const DEFAULT_PR_BODY_TEMPLATE = `## Summary\n\nThis PR contains the following changes:\n\n- Updated package versions\n- Updated changelogs\n\n## Packages\n\nThe following packages will be released:\n\n{{packages}}`;
14
+ const DEFAULT_CHANGELOG_TEMPLATE = `# Changelog\n\n{{releases}}`;
15
+ const DEFAULT_TYPES = {
16
+ feat: {
17
+ title: "šŸš€ Features",
18
+ color: "green"
19
+ },
20
+ fix: {
21
+ title: "šŸž Bug Fixes",
22
+ color: "red"
23
+ },
24
+ refactor: {
25
+ title: "šŸ”§ Code Refactoring",
26
+ color: "blue"
27
+ },
28
+ perf: {
29
+ title: "šŸŽ Performance",
30
+ color: "orange"
31
+ },
32
+ docs: {
33
+ title: "šŸ“š Documentation",
34
+ color: "purple"
35
+ },
36
+ style: {
37
+ title: "šŸŽØ Styles",
38
+ color: "pink"
39
+ }
40
+ };
41
+ function normalizeReleaseScriptsOptions(options) {
42
+ const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, branch = {}, globalCommitMode = "dependencies", pullRequest = {}, changelog = {}, types = {}, dryRun = false, npm = {}, prompts = {} } = options;
43
+ const token = githubToken.trim();
44
+ if (!token) throw new Error("GitHub token is required. Pass it in via options.");
45
+ if (!fullRepo || !fullRepo.trim() || !fullRepo.includes("/")) throw new Error("Repository (repo) is required. Specify in 'owner/repo' format (e.g., 'octocat/hello-world').");
46
+ const [owner, repo] = fullRepo.split("/");
47
+ if (!owner || !repo) throw new Error(`Invalid repo format: "${fullRepo}". Expected format: "owner/repo" (e.g., "octocat/hello-world").`);
48
+ const normalizedPackages = typeof packages === "object" && !Array.isArray(packages) ? {
49
+ exclude: packages.exclude ?? [],
50
+ include: packages.include ?? [],
51
+ excludePrivate: packages.excludePrivate ?? false
52
+ } : packages;
53
+ const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
54
+ return {
55
+ dryRun,
56
+ workspaceRoot,
57
+ githubToken: token,
58
+ owner,
59
+ repo,
60
+ packages: normalizedPackages,
61
+ branch: {
62
+ release: branch.release ?? "release/next",
63
+ default: branch.default ?? "main"
64
+ },
65
+ globalCommitMode,
66
+ pullRequest: {
67
+ title: pullRequest.title ?? "chore: release new version",
68
+ body: pullRequest.body ?? DEFAULT_PR_BODY_TEMPLATE
69
+ },
70
+ changelog: {
71
+ enabled: changelog.enabled ?? true,
72
+ template: changelog.template ?? DEFAULT_CHANGELOG_TEMPLATE,
73
+ emojis: changelog.emojis ?? true
74
+ },
75
+ types: options.types ? {
76
+ ...DEFAULT_TYPES,
77
+ ...types
78
+ } : DEFAULT_TYPES,
79
+ npm: {
80
+ otp: npm.otp,
81
+ provenance: npm.provenance ?? true
82
+ },
83
+ prompts: { versions: prompts.versions ?? !isCI }
84
+ };
85
+ }
86
+ var ReleaseScriptsOptions = class extends Context.Tag("@ucdjs/release-scripts/ReleaseScriptsOptions")() {};
87
+
88
+ //#endregion
12
89
  //#region src/utils/changelog-formatters.ts
13
90
  const eta$1 = new Eta();
14
91
  /**
15
92
  * Pure function to parse commits into changelog entries
16
93
  */
17
94
  function parseCommits(commits) {
18
- return commits.filter((commit) => commit.isConventional).map((commit) => ({
95
+ return commits.filter((commit) => commit.isConventional).filter((commit) => commit.type !== "chore").map((commit) => ({
19
96
  type: commit.type || "other",
20
97
  scope: commit.scope,
21
98
  description: commit.description,
@@ -25,6 +102,11 @@ function parseCommits(commits) {
25
102
  references: commit.references.map((ref) => ({
26
103
  type: ref.type,
27
104
  value: ref.value
105
+ })),
106
+ authors: commit.authors.map((author) => ({
107
+ name: author.name,
108
+ email: author.email,
109
+ profile: author.profile
28
110
  }))
29
111
  }));
30
112
  }
@@ -43,10 +125,7 @@ function groupByType(entries) {
43
125
  /**
44
126
  * Changelog template for Eta rendering
45
127
  */
46
- const CHANGELOG_TEMPLATE = `# <%= it.packageName %> v<%= it.version %>
47
-
48
- **Previous version**: \`<%= it.previousVersion %>\`
49
- **New version**: \`<%= it.version %>\`
128
+ const CHANGELOG_ENTRY_TEMPLATE = `## <%= it.version %>
50
129
 
51
130
  <% if (it.entries.length === 0) { %>
52
131
  *No conventional commits found.*
@@ -55,25 +134,58 @@ const CHANGELOG_TEMPLATE = `# <%= it.packageName %> v<%= it.version %>
55
134
  <% const typeOrder = ["breaking", "feat", "fix", "perf", "docs", "style", "refactor", "test", "build", "ci", "chore"]; %>
56
135
  <% const typeLabels = {
57
136
  breaking: "šŸ’„ Breaking Changes",
58
- feat: "✨ Features",
59
- fix: "šŸ› Bug Fixes",
137
+ feat: "šŸš€ Features",
138
+ fix: "šŸž Bug Fixes",
60
139
  perf: "⚔ Performance",
61
- docs: "šŸ“ Documentation",
62
- style: "šŸ’„ Styling",
63
- refactor: "ā™»ļø Refactoring",
64
- test: "āœ… Tests",
65
- build: "šŸ“¦ Build",
66
- ci: "šŸ‘· CI",
67
- chore: "šŸ”§ Chores"
140
+ docs: "Documentation",
141
+ style: "Styling",
142
+ refactor: "Refactoring",
143
+ test: "Tests",
144
+ build: "Build",
145
+ ci: "CI",
146
+ chore: "Chores"
147
+ }; %>
148
+
149
+ <% const formatAuthor = (entry) => {
150
+ const author = entry.authors && entry.authors.length > 0 ? entry.authors[0] : null;
151
+ if (!author) return "unknown";
152
+ if (author.profile && author.profile.includes("github.com/")) {
153
+ const username = author.profile.split("github.com/")[1];
154
+ return "@" + username;
155
+ }
156
+ return author.name || "unknown";
157
+ }; %>
158
+
159
+ <% const commitUrl = (hash) => it.repo ? "https://github.com/" + it.repo + "/commit/" + hash : ""; %>
160
+
161
+ <% const formatLine = (entry) => {
162
+ const authorText = formatAuthor(entry);
163
+ const commitLink = commitUrl(entry.hash);
164
+ const hashPart = commitLink
165
+ ? " [<samp>(" + entry.shortHash + ")</samp>](" + commitLink + ")"
166
+ : " <samp>(" + entry.shortHash + ")</samp>";
167
+ return entry.description + " &nbsp;-&nbsp; by " + authorText + hashPart;
68
168
  }; %>
69
169
 
70
170
  <% for (const type of typeOrder) { %>
71
171
  <% const entries = groups.get(type); %>
72
172
  <% if (entries && entries.length > 0) { %>
73
- ## <%= typeLabels[type] || type.charAt(0).toUpperCase() + type.slice(1) %>
173
+ ### &nbsp;&nbsp;&nbsp;<%= typeLabels[type] || type.charAt(0).toUpperCase() + type.slice(1) %>
74
174
 
75
- <% for (const entry of entries) { %>
76
- - <% if (entry.scope) { %>**<%= entry.scope %>**: <% } %><%= entry.description %><% if (entry.references.length > 0) { %> (<%= entry.references.map(r => "#" + r.value).join(", ") %>)<% } %> (\`<%= entry.shortHash %>\`)
175
+ <% const unscoped = entries.filter(e => !e.scope); %>
176
+ <% const scoped = entries.filter(e => e.scope); %>
177
+
178
+ <% for (const entry of unscoped) { %>
179
+ - <%= formatLine(entry) %>
180
+ <% } %>
181
+
182
+ <% const scopes = [...new Set(scoped.map(e => e.scope))]; %>
183
+ <% for (const scope of scopes) { %>
184
+ - **<%= scope %>**:
185
+ <% const scopeEntries = scoped.filter(e => e.scope === scope); %>
186
+ <% for (const entry of scopeEntries) { %>
187
+ - <%= formatLine(entry) %>
188
+ <% } %>
77
189
  <% } %>
78
190
 
79
191
  <% } %>
@@ -81,37 +193,56 @@ const CHANGELOG_TEMPLATE = `# <%= it.packageName %> v<%= it.version %>
81
193
 
82
194
  <% for (const [type, entries] of groups) { %>
83
195
  <% if (!typeOrder.includes(type)) { %>
84
- ## <%= type.charAt(0).toUpperCase() + type.slice(1) %>
196
+ ### &nbsp;&nbsp;&nbsp;<%= type.charAt(0).toUpperCase() + type.slice(1) %>
85
197
 
86
198
  <% for (const entry of entries) { %>
87
- - <% if (entry.scope) { %>**<%= entry.scope %>**: <% } %><%= entry.description %> (\`<%= entry.shortHash %>\`)
199
+ - <%= formatLine(entry) %>
88
200
  <% } %>
89
201
 
90
202
  <% } %>
91
203
  <% } %>
204
+
205
+ <% if (it.repo) { %>
206
+ ##### &nbsp;&nbsp;&nbsp;&nbsp;[View changes on GitHub](https://github.com/<%= it.repo %>/compare/v<%= it.previousVersion %>...v<%= it.version %>)
207
+ <% } %>
92
208
  <% } %>`;
93
209
  /**
94
210
  * Pure function to format changelog as markdown
95
211
  */
96
- function formatChangelogMarkdown(changelog) {
212
+ function formatChangelogEntryMarkdown(changelog) {
97
213
  const groups = groupByType(changelog.entries);
98
- return eta$1.renderString(CHANGELOG_TEMPLATE, {
214
+ return eta$1.renderString(CHANGELOG_ENTRY_TEMPLATE, {
99
215
  packageName: changelog.packageName,
100
216
  version: changelog.version,
101
217
  previousVersion: changelog.previousVersion,
102
218
  entries: changelog.entries,
103
- groupedEntries: groups
219
+ groupedEntries: groups,
220
+ repo: changelog.repo
104
221
  });
105
222
  }
223
+ function appendChangelogEntry(existingContent, changelogEntry, packageName) {
224
+ const entry = changelogEntry.trim();
225
+ if (!entry) return existingContent ?? `# ${packageName}\n`;
226
+ if (!existingContent || existingContent.trim() === "") return `# ${packageName}\n\n${entry}\n`;
227
+ const lines = existingContent.split("\n");
228
+ const firstLine = lines[0]?.trim() ?? "";
229
+ if (!firstLine.startsWith("# ")) return `# ${packageName}\n\n${entry}\n\n${existingContent.trim()}\n`;
230
+ let insertIndex = 1;
231
+ while (insertIndex < lines.length && lines[insertIndex]?.trim() === "") insertIndex++;
232
+ const rest = lines.slice(insertIndex).join("\n").trim();
233
+ if (rest) return `${firstLine}\n\n${entry}\n\n${rest}\n`;
234
+ return `${firstLine}\n\n${entry}\n`;
235
+ }
106
236
  /**
107
237
  * Pure function to create a changelog object
108
238
  */
109
- function createChangelog(packageName, version, previousVersion, commits) {
239
+ function createChangelog(packageName, version, previousVersion, commits, repo) {
110
240
  return {
111
241
  packageName,
112
242
  version,
113
243
  previousVersion,
114
- entries: parseCommits(commits)
244
+ entries: parseCommits(commits),
245
+ repo
115
246
  };
116
247
  }
117
248
 
@@ -119,12 +250,19 @@ function createChangelog(packageName, version, previousVersion, commits) {
119
250
  //#region src/services/changelog.service.ts
120
251
  var ChangelogService = class extends Effect.Service()("@ucdjs/release-scripts/ChangelogService", {
121
252
  effect: Effect.gen(function* () {
253
+ const config = yield* ReleaseScriptsOptions;
122
254
  function generateChangelog(pkg, newVersion, commits) {
123
255
  return Effect.gen(function* () {
124
- const changelog = createChangelog(pkg.name, newVersion, pkg.version, commits);
256
+ const changelog = createChangelog(pkg.name, newVersion, pkg.version, commits, `${config.owner}/${config.repo}`);
257
+ const entryMarkdown = formatChangelogEntryMarkdown(changelog);
125
258
  return {
126
259
  changelog,
127
- markdown: formatChangelogMarkdown(changelog),
260
+ markdown: appendChangelogEntry(yield* Effect.tryPromise({
261
+ try: async () => {
262
+ return await (await import("node:fs/promises")).readFile(`${pkg.path}/CHANGELOG.md`, "utf-8");
263
+ },
264
+ catch: (err) => err
265
+ }).pipe(Effect.catchAll(() => Effect.succeed(""))), entryMarkdown, pkg.name),
128
266
  filePath: `${pkg.path}/CHANGELOG.md`
129
267
  };
130
268
  });
@@ -213,83 +351,6 @@ var NPMError = class extends Data.TaggedError("NPMError") {};
213
351
  var PublishError = class extends Data.TaggedError("PublishError") {};
214
352
  var TagError = class extends Data.TaggedError("TagError") {};
215
353
 
216
- //#endregion
217
- //#region src/options.ts
218
- const DEFAULT_PR_BODY_TEMPLATE = `## Summary\n\nThis PR contains the following changes:\n\n- Updated package versions\n- Updated changelogs\n\n## Packages\n\nThe following packages will be released:\n\n{{packages}}`;
219
- const DEFAULT_CHANGELOG_TEMPLATE = `# Changelog\n\n{{releases}}`;
220
- const DEFAULT_TYPES = {
221
- feat: {
222
- title: "šŸš€ Features",
223
- color: "green"
224
- },
225
- fix: {
226
- title: "šŸž Bug Fixes",
227
- color: "red"
228
- },
229
- refactor: {
230
- title: "šŸ”§ Code Refactoring",
231
- color: "blue"
232
- },
233
- perf: {
234
- title: "šŸŽ Performance",
235
- color: "orange"
236
- },
237
- docs: {
238
- title: "šŸ“š Documentation",
239
- color: "purple"
240
- },
241
- style: {
242
- title: "šŸŽØ Styles",
243
- color: "pink"
244
- }
245
- };
246
- function normalizeReleaseScriptsOptions(options) {
247
- const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, branch = {}, globalCommitMode = "dependencies", pullRequest = {}, changelog = {}, types = {}, dryRun = false, npm = {}, prompts = {} } = options;
248
- const token = githubToken.trim();
249
- if (!token) throw new Error("GitHub token is required. Pass it in via options.");
250
- if (!fullRepo || !fullRepo.trim() || !fullRepo.includes("/")) throw new Error("Repository (repo) is required. Specify in 'owner/repo' format (e.g., 'octocat/hello-world').");
251
- const [owner, repo] = fullRepo.split("/");
252
- if (!owner || !repo) throw new Error(`Invalid repo format: "${fullRepo}". Expected format: "owner/repo" (e.g., "octocat/hello-world").`);
253
- const normalizedPackages = typeof packages === "object" && !Array.isArray(packages) ? {
254
- exclude: packages.exclude ?? [],
255
- include: packages.include ?? [],
256
- excludePrivate: packages.excludePrivate ?? false
257
- } : packages;
258
- const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
259
- return {
260
- dryRun,
261
- workspaceRoot,
262
- githubToken: token,
263
- owner,
264
- repo,
265
- packages: normalizedPackages,
266
- branch: {
267
- release: branch.release ?? "release/next",
268
- default: branch.default ?? "main"
269
- },
270
- globalCommitMode,
271
- pullRequest: {
272
- title: pullRequest.title ?? "chore: release new version",
273
- body: pullRequest.body ?? DEFAULT_PR_BODY_TEMPLATE
274
- },
275
- changelog: {
276
- enabled: changelog.enabled ?? true,
277
- template: changelog.template ?? DEFAULT_CHANGELOG_TEMPLATE,
278
- emojis: changelog.emojis ?? true
279
- },
280
- types: options.types ? {
281
- ...DEFAULT_TYPES,
282
- ...types
283
- } : DEFAULT_TYPES,
284
- npm: {
285
- otp: npm.otp,
286
- provenance: npm.provenance ?? true
287
- },
288
- prompts: { versions: prompts.versions ?? !isCI }
289
- };
290
- }
291
- var ReleaseScriptsOptions = class extends Context.Tag("@ucdjs/release-scripts/ReleaseScriptsOptions")() {};
292
-
293
354
  //#endregion
294
355
  //#region src/services/git.service.ts
295
356
  var GitService = class extends Effect.Service()("@ucdjs/release-scripts/GitService", {
@@ -784,6 +845,9 @@ var WorkspaceService = class extends Effect.Service()("@ucdjs/release-scripts/Wo
784
845
  message: `Invalid package.json for ${pkgPath}`,
785
846
  cause: e,
786
847
  operation: "readPackageJson"
848
+ })), Effect.map((validated) => ({
849
+ ...json,
850
+ ...validated
787
851
  })))));
788
852
  }
789
853
  function writePackageJson(pkgPath, json) {
@@ -1009,32 +1073,32 @@ var VersionCalculatorService = class extends Effect.Service()("@ucdjs/release-sc
1009
1073
 
1010
1074
  //#endregion
1011
1075
  //#region src/services/version-prompt.service.ts
1076
+ const GREY = "\x1B[90m";
1077
+ const RESET = "\x1B[0m";
1078
+ const NON_VERSIONING_TYPES = new Set([
1079
+ "chore",
1080
+ "docs",
1081
+ "style",
1082
+ "test",
1083
+ "ci",
1084
+ "build",
1085
+ "refactor"
1086
+ ]);
1087
+ function isVersioningCommit(commit) {
1088
+ return !NON_VERSIONING_TYPES.has(commit.type) || commit.isBreaking;
1089
+ }
1012
1090
  function formatCommit(commit) {
1013
- const typeEmoji = getTypeEmoji(commit.type);
1091
+ const isGreyed = !isVersioningCommit(commit);
1014
1092
  const scope = commit.scope ? `(${commit.scope})` : "";
1015
- const breaking = commit.isBreaking ? "!" : "";
1016
- const header = commit.isConventional ? `${typeEmoji} ${commit.type}${scope}${breaking}: ${commit.description}` : commit.message.split("\n")[0] ?? commit.message;
1017
- const refs = commit.references.map((r) => r.type === "pull-request" ? `#${r.value}` : `#${r.value}`).join(" ");
1018
- return refs ? `${header} (${refs})` : header;
1019
- }
1020
- function getTypeEmoji(type) {
1021
- return {
1022
- feat: "✨",
1023
- fix: "šŸ›",
1024
- docs: "šŸ“š",
1025
- style: "šŸ’Ž",
1026
- refactor: "šŸ”§",
1027
- perf: "šŸŽļø",
1028
- test: "🧪",
1029
- build: "šŸ“¦",
1030
- ci: "šŸ‘·",
1031
- chore: "šŸ”§",
1032
- revert: "āŖ"
1033
- }[type] || "šŸ“";
1093
+ const description = commit.isConventional ? commit.description : commit.message.split("\n")[0] ?? commit.message;
1094
+ const line = `${commit.shortHash} ${commit.type.padEnd(12)}${scope.padEnd(10)}: ${description}`;
1095
+ return isGreyed ? `${GREY}${line}${RESET}` : line;
1034
1096
  }
1035
1097
  function formatCommits(commits) {
1036
1098
  if (commits.length === 0) return " No commits since the last version";
1037
- return commits.slice(0, 10).map((c) => ` ${formatCommit(c)}`).join("\n") + (commits.length > 10 ? `\n ... and ${commits.length - 10} more` : "");
1099
+ const lines = commits.slice(0, 10).map((c) => ` ${formatCommit(c)}`);
1100
+ if (commits.length > 10) lines.push(` ${GREY}... and ${commits.length - 10} more${RESET}`);
1101
+ return lines.join("\n");
1038
1102
  }
1039
1103
  function getPrereleaseInfo(version) {
1040
1104
  const parsed = semver.parse(version);
@@ -1081,8 +1145,8 @@ function generateVersionOptions(currentVersion, conventionalBump, prereleaseInfo
1081
1145
  }
1082
1146
  });
1083
1147
  }
1084
- const conventionalVersion = conventionalBump !== "none" ? semver.inc(currentVersion, conventionalBump) : currentVersion;
1085
- if (conventionalVersion && conventionalVersion !== currentVersion) options.push({
1148
+ const conventionalVersion = conventionalBump !== "none" ? semver.inc(currentVersion, conventionalBump) : null;
1149
+ if (conventionalVersion) options.push({
1086
1150
  title: `conventional ${conventionalVersion}`,
1087
1151
  value: {
1088
1152
  version: conventionalVersion,
@@ -1156,6 +1220,11 @@ function generateVersionOptions(currentVersion, conventionalBump, prereleaseInfo
1156
1220
  });
1157
1221
  return options;
1158
1222
  }
1223
+ function findDefaultIndex(options, conventionalBump) {
1224
+ if (conventionalBump === "none") return 0;
1225
+ const conventionalIndex = options.findIndex((o) => o.title.startsWith("conventional"));
1226
+ return conventionalIndex >= 0 ? conventionalIndex : 0;
1227
+ }
1159
1228
  async function promptForCustomVersion(currentVersion) {
1160
1229
  return (await prompts({
1161
1230
  type: "text",
@@ -1171,27 +1240,37 @@ var VersionPromptService = class extends Effect.Service()("@ucdjs/release-script
1171
1240
  effect: Effect.gen(function* () {
1172
1241
  const config = yield* ReleaseScriptsOptions;
1173
1242
  let applyToAllRemainingChoice = null;
1243
+ let isCancelled = false;
1174
1244
  function promptForVersion(pkg, conventionalBump, remainingCount) {
1175
1245
  return Effect.async((resume) => {
1246
+ if (isCancelled) {
1247
+ resume(Effect.succeed({
1248
+ newVersion: pkg.version,
1249
+ bumpType: "none",
1250
+ applyToAllRemaining: false,
1251
+ cancelled: true
1252
+ }));
1253
+ return;
1254
+ }
1176
1255
  const allCommits = [...pkg.commits, ...pkg.globalCommits];
1177
1256
  const prereleaseInfo = getPrereleaseInfo(pkg.version);
1257
+ const commitCount = allCommits.length;
1178
1258
  console.log("");
1179
- console.log(`\x1B[1m${pkg.name}\x1B[0m`);
1180
- console.log(`Current version: ${pkg.version}`);
1181
- console.log("");
1182
- console.log("Commits:");
1259
+ console.log(`${commitCount} commit${commitCount === 1 ? "" : "s"} since the last version:`);
1183
1260
  console.log(formatCommits(allCommits));
1184
1261
  console.log("");
1185
1262
  if (applyToAllRemainingChoice) {
1186
1263
  const result = {
1187
1264
  newVersion: applyToAllRemainingChoice.version === "custom" ? pkg.version : applyToAllRemainingChoice.version,
1188
1265
  bumpType: applyToAllRemainingChoice.bumpType,
1189
- applyToAllRemaining: false
1266
+ applyToAllRemaining: false,
1267
+ cancelled: false
1190
1268
  };
1191
1269
  resume(Effect.succeed(result));
1192
1270
  return;
1193
1271
  }
1194
1272
  const options = generateVersionOptions(pkg.version, conventionalBump, prereleaseInfo);
1273
+ const defaultIndex = findDefaultIndex(options, conventionalBump);
1195
1274
  if (remainingCount > 1) options.push({
1196
1275
  title: "apply-to-all ›",
1197
1276
  value: {
@@ -1202,18 +1281,21 @@ var VersionPromptService = class extends Effect.Service()("@ucdjs/release-script
1202
1281
  prompts({
1203
1282
  type: "select",
1204
1283
  name: "choice",
1205
- message: `Select version`,
1284
+ message: `Current version ${pkg.version}`,
1206
1285
  choices: options.map((o) => ({
1207
1286
  title: o.title,
1208
1287
  value: o.value
1209
1288
  })),
1289
+ initial: defaultIndex,
1210
1290
  hint: "Use arrow keys to navigate, enter to select"
1211
1291
  }).then(async (response) => {
1212
1292
  if (!response.choice) {
1293
+ isCancelled = true;
1213
1294
  const result = {
1214
1295
  newVersion: pkg.version,
1215
1296
  bumpType: "none",
1216
- applyToAllRemaining: false
1297
+ applyToAllRemaining: false,
1298
+ cancelled: true
1217
1299
  };
1218
1300
  resume(Effect.succeed(result));
1219
1301
  return;
@@ -1227,39 +1309,66 @@ var VersionPromptService = class extends Effect.Service()("@ucdjs/release-script
1227
1309
  choices: applyOptions.map((o) => ({
1228
1310
  title: o.title,
1229
1311
  value: o.value
1230
- }))
1312
+ })),
1313
+ initial: findDefaultIndex(applyOptions, conventionalBump)
1231
1314
  });
1232
- if (applyResponse.choice) {
1233
- if (applyResponse.choice.version === "custom") {
1234
- const customVersion = await promptForCustomVersion(pkg.version);
1235
- if (customVersion) applyToAllRemainingChoice = {
1236
- version: customVersion,
1237
- bumpType: applyResponse.choice.bumpType
1238
- };
1239
- } else applyToAllRemainingChoice = applyResponse.choice;
1240
- const result = {
1241
- newVersion: applyToAllRemainingChoice?.version || pkg.version,
1242
- bumpType: applyToAllRemainingChoice?.bumpType || "none",
1243
- applyToAllRemaining: true
1315
+ if (!applyResponse.choice) {
1316
+ isCancelled = true;
1317
+ resume(Effect.succeed({
1318
+ newVersion: pkg.version,
1319
+ bumpType: "none",
1320
+ applyToAllRemaining: false,
1321
+ cancelled: true
1322
+ }));
1323
+ return;
1324
+ }
1325
+ if (applyResponse.choice.version === "custom") {
1326
+ const customVersion = await promptForCustomVersion(pkg.version);
1327
+ if (!customVersion) {
1328
+ isCancelled = true;
1329
+ resume(Effect.succeed({
1330
+ newVersion: pkg.version,
1331
+ bumpType: "none",
1332
+ applyToAllRemaining: false,
1333
+ cancelled: true
1334
+ }));
1335
+ return;
1336
+ }
1337
+ applyToAllRemainingChoice = {
1338
+ version: customVersion,
1339
+ bumpType: applyResponse.choice.bumpType
1244
1340
  };
1245
- resume(Effect.succeed(result));
1246
- } else promptForVersion(pkg, conventionalBump, remainingCount).pipe(Effect.runPromise).then((r) => resume(Effect.succeed(r)));
1341
+ } else applyToAllRemainingChoice = applyResponse.choice;
1342
+ const result = {
1343
+ newVersion: applyToAllRemainingChoice?.version || pkg.version,
1344
+ bumpType: applyToAllRemainingChoice?.bumpType || "none",
1345
+ applyToAllRemaining: true,
1346
+ cancelled: false
1347
+ };
1348
+ resume(Effect.succeed(result));
1247
1349
  return;
1248
1350
  }
1249
1351
  let selectedVersion = response.choice.version;
1250
- let selectedBumpType = response.choice.bumpType;
1352
+ const selectedBumpType = response.choice.bumpType;
1251
1353
  if (selectedVersion === "custom") {
1252
1354
  const customVersion = await promptForCustomVersion(pkg.version);
1253
- if (customVersion) selectedVersion = customVersion;
1254
- else {
1255
- selectedVersion = pkg.version;
1256
- selectedBumpType = "none";
1355
+ if (!customVersion) {
1356
+ isCancelled = true;
1357
+ resume(Effect.succeed({
1358
+ newVersion: pkg.version,
1359
+ bumpType: "none",
1360
+ applyToAllRemaining: false,
1361
+ cancelled: true
1362
+ }));
1363
+ return;
1257
1364
  }
1365
+ selectedVersion = customVersion;
1258
1366
  }
1259
1367
  const result = {
1260
1368
  newVersion: selectedVersion,
1261
1369
  bumpType: selectedBumpType,
1262
- applyToAllRemaining: false
1370
+ applyToAllRemaining: false,
1371
+ cancelled: false
1263
1372
  };
1264
1373
  resume(Effect.succeed(result));
1265
1374
  });
@@ -1270,6 +1379,7 @@ var VersionPromptService = class extends Effect.Service()("@ucdjs/release-script
1270
1379
  isEnabled: config.prompts.versions,
1271
1380
  resetApplyToAll: () => {
1272
1381
  applyToAllRemainingChoice = null;
1382
+ isCancelled = false;
1273
1383
  }
1274
1384
  };
1275
1385
  }),
@@ -1473,36 +1583,38 @@ function constructPrepareProgram(config) {
1473
1583
  const versionPrompt = yield* VersionPromptService;
1474
1584
  const workspace = yield* WorkspaceService;
1475
1585
  yield* git.workspace.assertWorkspaceReady;
1586
+ const startingBranch = yield* git.branches.get;
1587
+ if (startingBranch !== config.branch.default) return yield* Effect.fail(/* @__PURE__ */ new Error(`Prepare must be run on the default branch "${config.branch.default}". Current branch: "${startingBranch}"`));
1476
1588
  let releasePullRequest = yield* github.getPullRequestByBranch(config.branch.release);
1477
1589
  const isNewRelease = !releasePullRequest;
1478
1590
  const branchExists = yield* git.branches.exists(config.branch.release);
1479
1591
  if (!branchExists) {
1480
- yield* Console.log(`🌿 Creating release branch "${config.branch.release}" from "${config.branch.default}"...`);
1592
+ yield* Console.log(`Creating release branch "${config.branch.release}" from "${config.branch.default}"...`);
1481
1593
  yield* git.branches.create(config.branch.release, config.branch.default);
1482
- yield* Console.log(`āœ… Release branch created.`);
1594
+ yield* Console.log(`Release branch created.`);
1483
1595
  }
1484
1596
  if ((yield* git.branches.get) !== config.branch.release) {
1485
1597
  yield* git.branches.checkout(config.branch.release);
1486
- yield* Console.log(`āœ… Checked out to release branch "${config.branch.release}".`);
1598
+ yield* Console.log(`Checked out to release branch "${config.branch.release}".`);
1487
1599
  }
1488
1600
  if (!isNewRelease || branchExists) {
1489
- yield* Console.log(`šŸ”„ Rebasing "${config.branch.release}" onto "${config.branch.default}"...`);
1601
+ yield* Console.log(`Rebasing "${config.branch.release}" onto "${config.branch.default}"...`);
1490
1602
  yield* git.branches.rebase(config.branch.default);
1491
- yield* Console.log(`āœ… Rebase complete.`);
1603
+ yield* Console.log(`Rebase complete.`);
1492
1604
  }
1493
1605
  const overrides = yield* loadOverrides({
1494
1606
  sha: config.branch.default,
1495
1607
  overridesPath: ".github/ucdjs-release.overrides.json"
1496
1608
  });
1497
- if (Object.keys(overrides).length > 0) yield* Console.log("šŸ“‹ Loaded version overrides:", overrides);
1609
+ if (Object.keys(overrides).length > 0) yield* Console.log("Loaded version overrides:", overrides);
1498
1610
  const originalBranch = yield* git.branches.get;
1499
1611
  yield* git.branches.checkout(config.branch.default);
1500
1612
  const packages = yield* workspace.discoverWorkspacePackages.pipe(Effect.flatMap(mergePackageCommitsIntoPackages), Effect.flatMap((pkgs) => mergeCommitsAffectingGloballyIntoPackage(pkgs, config.globalCommitMode)));
1501
- yield* Console.log(`šŸ“¦ Discovered ${packages.length} packages with commits.`);
1613
+ yield* Console.log(`Discovered ${packages.length} packages with commits.`);
1502
1614
  yield* dependencyGraph.topologicalOrder(packages);
1503
1615
  const releases = [];
1504
1616
  if (versionPrompt.isEnabled) {
1505
- yield* Console.log("\nšŸŽÆ Interactive version selection enabled.\n");
1617
+ yield* Console.log("\nInteractive version selection enabled.\n");
1506
1618
  versionPrompt.resetApplyToAll();
1507
1619
  for (let i = 0; i < packages.length; i++) {
1508
1620
  const pkg = packages[i];
@@ -1528,6 +1640,14 @@ function constructPrepareProgram(config) {
1528
1640
  continue;
1529
1641
  }
1530
1642
  const result = yield* versionPrompt.promptForVersion(pkg, conventionalBump, remainingCount);
1643
+ if (result.cancelled) {
1644
+ yield* Console.log("\nCancelled by user.");
1645
+ if (startingBranch !== (yield* git.branches.get)) {
1646
+ yield* git.branches.checkout(startingBranch);
1647
+ yield* Console.log(`Switched back to "${startingBranch}".`);
1648
+ }
1649
+ return yield* Effect.fail(/* @__PURE__ */ new Error("Release preparation cancelled."));
1650
+ }
1531
1651
  releases.push({
1532
1652
  package: {
1533
1653
  name: pkg.name,
@@ -1548,12 +1668,12 @@ function constructPrepareProgram(config) {
1548
1668
  releases.push(...calculatedReleases);
1549
1669
  }
1550
1670
  const releasesCount = releases.length;
1551
- yield* Console.log(`\nšŸ“Š ${releasesCount} package${releasesCount === 1 ? "" : "s"} will be released.`);
1671
+ yield* Console.log(`\n${releasesCount} package${releasesCount === 1 ? "" : "s"} will be released.`);
1552
1672
  yield* git.branches.checkout(originalBranch);
1553
- yield* Console.log("āœļø Updating package.json files...");
1673
+ yield* Console.log("Updating package.json files...");
1554
1674
  yield* packageUpdater.applyReleases(packages, releases);
1555
- yield* Console.log("āœ… package.json files updated.");
1556
- yield* Console.log("šŸ“ Generating changelogs...");
1675
+ yield* Console.log("package.json files updated.");
1676
+ yield* Console.log("Generating changelogs...");
1557
1677
  const changelogFiles = [];
1558
1678
  for (const release of releases) {
1559
1679
  const pkg = packages.find((p) => p.name === release.package.name);
@@ -1567,28 +1687,28 @@ function constructPrepareProgram(config) {
1567
1687
  });
1568
1688
  changelogFiles.push(result.filePath);
1569
1689
  }
1570
- yield* Console.log(`āœ… Generated ${changelogFiles.length} changelog file${changelogFiles.length === 1 ? "" : "s"}.`);
1690
+ yield* Console.log(`Generated ${changelogFiles.length} changelog file${changelogFiles.length === 1 ? "" : "s"}.`);
1571
1691
  const filesToStage = [...releases.map((r) => `${r.package.path}/package.json`), ...changelogFiles];
1572
- yield* Console.log(`šŸ“Œ Staging ${filesToStage.length} file${filesToStage.length === 1 ? "" : "s"}...`);
1692
+ yield* Console.log(`Staging ${filesToStage.length} file${filesToStage.length === 1 ? "" : "s"}...`);
1573
1693
  yield* git.commits.stage(filesToStage);
1574
1694
  const commitMessage = `chore(release): prepare release
1575
1695
 
1576
1696
  ${releasesCount} package${releasesCount === 1 ? "" : "s"} updated:
1577
1697
  ${releases.map((r) => ` - ${r.package.name}@${r.newVersion}`).join("\n")}`;
1578
- yield* Console.log("šŸ’¾ Creating commit...");
1698
+ yield* Console.log("Creating commit...");
1579
1699
  yield* git.commits.write(commitMessage);
1580
- yield* Console.log("āœ… Commit created.");
1581
- yield* Console.log(`ā¬†ļø Pushing to "${config.branch.release}"...`);
1700
+ yield* Console.log("Commit created.");
1701
+ yield* Console.log(`Pushing to "${config.branch.release}"...`);
1582
1702
  if (isNewRelease && !branchExists) yield* git.commits.push(config.branch.release);
1583
1703
  else yield* git.commits.forcePush(config.branch.release);
1584
- yield* Console.log(`āœ… Push complete.`);
1704
+ yield* Console.log(`Push complete.`);
1585
1705
  const prBody = yield* github.generateReleasePRBody(releases.map((r) => ({
1586
1706
  packageName: r.package.name,
1587
1707
  version: r.newVersion,
1588
1708
  previousVersion: r.package.version
1589
1709
  })));
1590
1710
  if (isNewRelease) {
1591
- yield* Console.log("šŸ“‹ Creating release pull request...");
1711
+ yield* Console.log("Creating release pull request...");
1592
1712
  releasePullRequest = yield* github.createPullRequest({
1593
1713
  title: config.pullRequest.title,
1594
1714
  body: prBody,
@@ -1596,15 +1716,17 @@ ${releases.map((r) => ` - ${r.package.name}@${r.newVersion}`).join("\n")}`;
1596
1716
  base: config.branch.default,
1597
1717
  draft: true
1598
1718
  });
1599
- yield* Console.log(`āœ… Release pull request #${releasePullRequest.number} created.`);
1719
+ yield* Console.log(`Release pull request #${releasePullRequest.number} created.`);
1600
1720
  } else {
1601
- yield* Console.log("šŸ“„ Updating pull request...");
1721
+ yield* Console.log("Updating pull request...");
1602
1722
  yield* github.updatePullRequest(releasePullRequest.number, { body: prBody });
1603
- yield* Console.log("āœ… Pull request updated.");
1723
+ yield* Console.log("Pull request updated.");
1724
+ }
1725
+ yield* Console.log(`\nRelease preparation complete! View PR: #${releasePullRequest.number}`);
1726
+ if (startingBranch !== (yield* git.branches.get)) {
1727
+ yield* git.branches.checkout(startingBranch);
1728
+ yield* Console.log(`Switched back to "${startingBranch}".`);
1604
1729
  }
1605
- yield* Console.log(`\nšŸŽ‰ Release preparation complete! View PR: #${releasePullRequest.number}`);
1606
- yield* git.branches.checkout(config.branch.default);
1607
- yield* Console.log(`āœ… Switched back to "${config.branch.default}".`);
1608
1730
  });
1609
1731
  }
1610
1732
 
@@ -1633,9 +1755,9 @@ function constructPublishProgram(config) {
1633
1755
  yield* git.workspace.assertWorkspaceReady;
1634
1756
  const currentBranch = yield* git.branches.get;
1635
1757
  if (currentBranch !== config.branch.default) return yield* Effect.fail(/* @__PURE__ */ new Error(`Publish must be run on the default branch "${config.branch.default}". Current branch: "${currentBranch}"`));
1636
- yield* Console.log(`āœ… On default branch "${config.branch.default}".`);
1758
+ yield* Console.log(`On default branch "${config.branch.default}".`);
1637
1759
  const publicPackages = (yield* workspace.discoverWorkspacePackages).filter((pkg) => !pkg.packageJson.private);
1638
- yield* Console.log(`šŸ“¦ Found ${publicPackages.length} public package${publicPackages.length === 1 ? "" : "s"} to check.`);
1760
+ yield* Console.log(`Found ${publicPackages.length} public package${publicPackages.length === 1 ? "" : "s"} to check.`);
1639
1761
  const orderedPackages = yield* dependencyGraph.topologicalOrder(publicPackages);
1640
1762
  const results = [];
1641
1763
  for (const updateOrder of orderedPackages) {
@@ -1643,7 +1765,7 @@ function constructPublishProgram(config) {
1643
1765
  const version = pkg.version;
1644
1766
  const tagName = `${pkg.name}@${version}`;
1645
1767
  if (yield* npm.versionExists(pkg.name, version)) {
1646
- yield* Console.log(`ā­ļø Skipping ${pkg.name}@${version} - already published.`);
1768
+ yield* Console.log(`Skipping ${pkg.name}@${version} - already published.`);
1647
1769
  results.push({
1648
1770
  packageName: pkg.name,
1649
1771
  version,
@@ -1652,11 +1774,11 @@ function constructPublishProgram(config) {
1652
1774
  });
1653
1775
  continue;
1654
1776
  }
1655
- yield* Console.log(`šŸ”Ø Building ${pkg.name}...`);
1777
+ yield* Console.log(`Building ${pkg.name}...`);
1656
1778
  yield* buildPackage(pkg.path);
1657
- yield* Console.log(`āœ… Build complete for ${pkg.name}.`);
1779
+ yield* Console.log(`Build complete for ${pkg.name}.`);
1658
1780
  const distTag = getDistTag(version);
1659
- yield* Console.log(`šŸš€ Publishing ${pkg.name}@${version} with tag "${distTag}"...`);
1781
+ yield* Console.log(`Publishing ${pkg.name}@${version} with tag "${distTag}"...`);
1660
1782
  const publishResult = yield* npm.publish({
1661
1783
  packagePath: pkg.path,
1662
1784
  tagName: distTag,
@@ -1668,13 +1790,13 @@ function constructPublishProgram(config) {
1668
1790
  error: err
1669
1791
  })));
1670
1792
  if (publishResult.success) {
1671
- yield* Console.log(`āœ… Published ${pkg.name}@${version}.`);
1793
+ yield* Console.log(`Published ${pkg.name}@${version}.`);
1672
1794
  if (!config.dryRun) {
1673
- yield* Console.log(`šŸ·ļø Creating tag ${tagName}...`);
1795
+ yield* Console.log(`Creating tag ${tagName}...`);
1674
1796
  yield* git.tags.create(tagName, `Release ${tagName}`);
1675
1797
  yield* git.tags.push(tagName);
1676
- yield* Console.log(`āœ… Tag ${tagName} created and pushed.`);
1677
- } else yield* Console.log(`šŸ·ļø [Dry Run] Would create and push tag ${tagName}.`);
1798
+ yield* Console.log(`Tag ${tagName} created and pushed.`);
1799
+ } else yield* Console.log(`[Dry Run] Would create and push tag ${tagName}.`);
1678
1800
  results.push({
1679
1801
  packageName: pkg.name,
1680
1802
  version,
@@ -1682,7 +1804,7 @@ function constructPublishProgram(config) {
1682
1804
  });
1683
1805
  } else {
1684
1806
  const error = publishResult.error;
1685
- yield* Console.log(`āŒ Failed to publish ${pkg.name}@${version}: ${error.message}`);
1807
+ yield* Console.log(`Failed to publish ${pkg.name}@${version}: ${error.message}`);
1686
1808
  results.push({
1687
1809
  packageName: pkg.name,
1688
1810
  version,
@@ -1694,17 +1816,17 @@ function constructPublishProgram(config) {
1694
1816
  const published = results.filter((r) => r.status === "published");
1695
1817
  const skipped = results.filter((r) => r.status === "skipped");
1696
1818
  const failed = results.filter((r) => r.status === "failed");
1697
- yield* Console.log("\nšŸ“Š Publish Summary:");
1819
+ yield* Console.log("\nPublish Summary:");
1698
1820
  yield* Console.log(` Published: ${published.length}`);
1699
1821
  yield* Console.log(` Skipped: ${skipped.length}`);
1700
1822
  yield* Console.log(` Failed: ${failed.length}`);
1701
1823
  if (failed.length > 0) {
1702
- yield* Console.log("\nāŒ Failed packages:");
1824
+ yield* Console.log("\nFailed packages:");
1703
1825
  for (const f of failed) yield* Console.log(` - ${f.packageName}@${f.version}: ${f.reason}`);
1704
1826
  return yield* Effect.fail(/* @__PURE__ */ new Error("Some packages failed to publish."));
1705
1827
  }
1706
- if (published.length === 0 && skipped.length > 0) yield* Console.log("\nāœ… All packages were already published.");
1707
- else if (published.length > 0) yield* Console.log("\nšŸŽ‰ Publish complete!");
1828
+ if (published.length === 0 && skipped.length > 0) yield* Console.log("\nAll packages were already published.");
1829
+ else if (published.length > 0) yield* Console.log("\nPublish complete!");
1708
1830
  });
1709
1831
  }
1710
1832
 
@@ -1790,10 +1912,10 @@ function constructVerifyProgram(config) {
1790
1912
  yield* git.workspace.assertWorkspaceReady;
1791
1913
  const releasePullRequest = yield* github.getPullRequestByBranch(config.branch.release);
1792
1914
  if (!releasePullRequest || !releasePullRequest.head) return yield* Effect.fail(/* @__PURE__ */ new Error(`Release pull request for branch "${config.branch.release}" does not exist.`));
1793
- yield* Console.log(`āœ… Release pull request #${releasePullRequest.number} exists.`);
1915
+ yield* Console.log(`Release pull request #${releasePullRequest.number} exists.`);
1794
1916
  if ((yield* git.branches.get) !== config.branch.default) {
1795
1917
  yield* git.branches.checkout(config.branch.default);
1796
- yield* Console.log(`āœ… Checked out to default branch "${config.branch.default}".`);
1918
+ yield* Console.log(`Checked out to default branch "${config.branch.default}".`);
1797
1919
  }
1798
1920
  const overrides = yield* loadOverrides({
1799
1921
  sha: releasePullRequest.head.sha,
@@ -1813,8 +1935,8 @@ function constructVerifyProgram(config) {
1813
1935
  branchSnapshots.set(pkg.name, snapshot);
1814
1936
  }
1815
1937
  const drift = findDrift(packages, releases, branchSnapshots);
1816
- if (drift.length === 0) yield* Console.log("āœ… Release branch is in sync with expected releases.");
1817
- else yield* Console.log("āŒ Release branch is out of sync:", drift);
1938
+ if (drift.length === 0) yield* Console.log("Release branch is in sync with expected releases.");
1939
+ else yield* Console.log("Release branch is out of sync:", drift);
1818
1940
  const status = drift.length === 0 ? {
1819
1941
  state: "success",
1820
1942
  description: "Release artifacts in sync",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ucdjs/release-scripts",
3
- "version": "0.1.0-beta.32",
3
+ "version": "0.1.0-beta.34",
4
4
  "description": "@ucdjs release scripts",
5
5
  "type": "module",
6
6
  "license": "MIT",