@ucdjs/release-scripts 0.1.0-beta.31 โ†’ 0.1.0-beta.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -35,6 +35,9 @@ interface ReleaseScriptsOptionsInput {
35
35
  otp?: string;
36
36
  provenance?: boolean;
37
37
  };
38
+ prompts?: {
39
+ versions?: boolean;
40
+ };
38
41
  }
39
42
  //#endregion
40
43
  //#region src/services/workspace.service.d.ts
package/dist/index.mjs CHANGED
@@ -7,6 +7,7 @@ import * as CommitParser from "commit-parser";
7
7
  import process from "node:process";
8
8
  import semver from "semver";
9
9
  import fs from "node:fs/promises";
10
+ import prompts from "prompts";
10
11
 
11
12
  //#region src/utils/changelog-formatters.ts
12
13
  const eta$1 = new Eta();
@@ -243,23 +244,25 @@ const DEFAULT_TYPES = {
243
244
  }
244
245
  };
245
246
  function normalizeReleaseScriptsOptions(options) {
246
- const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, branch = {}, globalCommitMode = "dependencies", pullRequest = {}, changelog = {}, types = {}, dryRun = false, npm = {} } = options;
247
+ const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, branch = {}, globalCommitMode = "dependencies", pullRequest = {}, changelog = {}, types = {}, dryRun = false, npm = {}, prompts = {} } = options;
247
248
  const token = githubToken.trim();
248
249
  if (!token) throw new Error("GitHub token is required. Pass it in via options.");
249
250
  if (!fullRepo || !fullRepo.trim() || !fullRepo.includes("/")) throw new Error("Repository (repo) is required. Specify in 'owner/repo' format (e.g., 'octocat/hello-world').");
250
251
  const [owner, repo] = fullRepo.split("/");
251
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";
252
259
  return {
253
260
  dryRun,
254
261
  workspaceRoot,
255
262
  githubToken: token,
256
263
  owner,
257
264
  repo,
258
- packages: typeof packages === "object" && !Array.isArray(packages) ? {
259
- exclude: packages.exclude ?? [],
260
- include: packages.include ?? [],
261
- excludePrivate: packages.excludePrivate ?? false
262
- } : packages,
265
+ packages: normalizedPackages,
263
266
  branch: {
264
267
  release: branch.release ?? "release/next",
265
268
  default: branch.default ?? "main"
@@ -281,7 +284,8 @@ function normalizeReleaseScriptsOptions(options) {
281
284
  npm: {
282
285
  otp: npm.otp,
283
286
  provenance: npm.provenance ?? true
284
- }
287
+ },
288
+ prompts: { versions: prompts.versions ?? !isCI }
285
289
  };
286
290
  }
287
291
  var ReleaseScriptsOptions = class extends Context.Tag("@ucdjs/release-scripts/ReleaseScriptsOptions")() {};
@@ -1003,6 +1007,275 @@ var VersionCalculatorService = class extends Effect.Service()("@ucdjs/release-sc
1003
1007
  dependencies: []
1004
1008
  }) {};
1005
1009
 
1010
+ //#endregion
1011
+ //#region src/services/version-prompt.service.ts
1012
+ function formatCommit(commit) {
1013
+ const typeEmoji = getTypeEmoji(commit.type);
1014
+ 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] || "๐Ÿ“";
1034
+ }
1035
+ function formatCommits(commits) {
1036
+ 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` : "");
1038
+ }
1039
+ function getPrereleaseInfo(version) {
1040
+ const parsed = semver.parse(version);
1041
+ if (!parsed) return null;
1042
+ if (parsed.prerelease.length === 0) return null;
1043
+ return {
1044
+ identifier: String(parsed.prerelease[0]),
1045
+ baseVersion: `${parsed.major}.${parsed.minor}.${parsed.patch}`
1046
+ };
1047
+ }
1048
+ function generateVersionOptions(currentVersion, conventionalBump, prereleaseInfo) {
1049
+ const options = [];
1050
+ const majorVersion = semver.inc(currentVersion, "major");
1051
+ const minorVersion = semver.inc(currentVersion, "minor");
1052
+ const patchVersion = semver.inc(currentVersion, "patch");
1053
+ if (majorVersion) options.push({
1054
+ title: `major ${majorVersion}`,
1055
+ value: {
1056
+ version: majorVersion,
1057
+ bumpType: "major"
1058
+ }
1059
+ });
1060
+ if (minorVersion) options.push({
1061
+ title: `minor ${minorVersion}`,
1062
+ value: {
1063
+ version: minorVersion,
1064
+ bumpType: "minor"
1065
+ }
1066
+ });
1067
+ if (patchVersion) options.push({
1068
+ title: `patch ${patchVersion}`,
1069
+ value: {
1070
+ version: patchVersion,
1071
+ bumpType: "patch"
1072
+ }
1073
+ });
1074
+ if (prereleaseInfo) {
1075
+ const nextPrerelease = semver.inc(currentVersion, "prerelease", prereleaseInfo.identifier);
1076
+ if (nextPrerelease) options.push({
1077
+ title: `next ${nextPrerelease}`,
1078
+ value: {
1079
+ version: nextPrerelease,
1080
+ bumpType: "patch"
1081
+ }
1082
+ });
1083
+ }
1084
+ const conventionalVersion = conventionalBump !== "none" ? semver.inc(currentVersion, conventionalBump) : currentVersion;
1085
+ if (conventionalVersion && conventionalVersion !== currentVersion) options.push({
1086
+ title: `conventional ${conventionalVersion}`,
1087
+ value: {
1088
+ version: conventionalVersion,
1089
+ bumpType: conventionalBump
1090
+ }
1091
+ });
1092
+ if (prereleaseInfo) {
1093
+ const prePatch = semver.inc(currentVersion, "prepatch", prereleaseInfo.identifier);
1094
+ const preMinor = semver.inc(currentVersion, "preminor", prereleaseInfo.identifier);
1095
+ const preMajor = semver.inc(currentVersion, "premajor", prereleaseInfo.identifier);
1096
+ if (prePatch) options.push({
1097
+ title: `pre-patch ${prePatch}`,
1098
+ value: {
1099
+ version: prePatch,
1100
+ bumpType: "patch"
1101
+ }
1102
+ });
1103
+ if (preMinor) options.push({
1104
+ title: `pre-minor ${preMinor}`,
1105
+ value: {
1106
+ version: preMinor,
1107
+ bumpType: "minor"
1108
+ }
1109
+ });
1110
+ if (preMajor) options.push({
1111
+ title: `pre-major ${preMajor}`,
1112
+ value: {
1113
+ version: preMajor,
1114
+ bumpType: "major"
1115
+ }
1116
+ });
1117
+ } else {
1118
+ const betaPatch = semver.inc(currentVersion, "prepatch", "beta");
1119
+ const betaMinor = semver.inc(currentVersion, "preminor", "beta");
1120
+ const betaMajor = semver.inc(currentVersion, "premajor", "beta");
1121
+ if (betaPatch) options.push({
1122
+ title: `pre-patch ${betaPatch}`,
1123
+ value: {
1124
+ version: betaPatch,
1125
+ bumpType: "patch"
1126
+ }
1127
+ });
1128
+ if (betaMinor) options.push({
1129
+ title: `pre-minor ${betaMinor}`,
1130
+ value: {
1131
+ version: betaMinor,
1132
+ bumpType: "minor"
1133
+ }
1134
+ });
1135
+ if (betaMajor) options.push({
1136
+ title: `pre-major ${betaMajor}`,
1137
+ value: {
1138
+ version: betaMajor,
1139
+ bumpType: "major"
1140
+ }
1141
+ });
1142
+ }
1143
+ options.push({
1144
+ title: `as-is ${currentVersion}`,
1145
+ value: {
1146
+ version: currentVersion,
1147
+ bumpType: "none"
1148
+ }
1149
+ });
1150
+ options.push({
1151
+ title: "custom ...",
1152
+ value: {
1153
+ version: "custom",
1154
+ bumpType: "none"
1155
+ }
1156
+ });
1157
+ return options;
1158
+ }
1159
+ async function promptForCustomVersion(currentVersion) {
1160
+ return (await prompts({
1161
+ type: "text",
1162
+ name: "version",
1163
+ message: `Enter custom version (current: ${currentVersion})`,
1164
+ validate: (input) => {
1165
+ if (!semver.valid(input)) return "Please enter a valid semver version (e.g., 1.2.3)";
1166
+ return true;
1167
+ }
1168
+ })).version || null;
1169
+ }
1170
+ var VersionPromptService = class extends Effect.Service()("@ucdjs/release-scripts/VersionPromptService", {
1171
+ effect: Effect.gen(function* () {
1172
+ const config = yield* ReleaseScriptsOptions;
1173
+ let applyToAllRemainingChoice = null;
1174
+ function promptForVersion(pkg, conventionalBump, remainingCount) {
1175
+ return Effect.async((resume) => {
1176
+ const allCommits = [...pkg.commits, ...pkg.globalCommits];
1177
+ const prereleaseInfo = getPrereleaseInfo(pkg.version);
1178
+ 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:");
1183
+ console.log(formatCommits(allCommits));
1184
+ console.log("");
1185
+ if (applyToAllRemainingChoice) {
1186
+ const result = {
1187
+ newVersion: applyToAllRemainingChoice.version === "custom" ? pkg.version : applyToAllRemainingChoice.version,
1188
+ bumpType: applyToAllRemainingChoice.bumpType,
1189
+ applyToAllRemaining: false
1190
+ };
1191
+ resume(Effect.succeed(result));
1192
+ return;
1193
+ }
1194
+ const options = generateVersionOptions(pkg.version, conventionalBump, prereleaseInfo);
1195
+ if (remainingCount > 1) options.push({
1196
+ title: "apply-to-all โ€บ",
1197
+ value: {
1198
+ version: "apply-to-all",
1199
+ bumpType: "none"
1200
+ }
1201
+ });
1202
+ prompts({
1203
+ type: "select",
1204
+ name: "choice",
1205
+ message: `Select version`,
1206
+ choices: options.map((o) => ({
1207
+ title: o.title,
1208
+ value: o.value
1209
+ })),
1210
+ hint: "Use arrow keys to navigate, enter to select"
1211
+ }).then(async (response) => {
1212
+ if (!response.choice) {
1213
+ const result = {
1214
+ newVersion: pkg.version,
1215
+ bumpType: "none",
1216
+ applyToAllRemaining: false
1217
+ };
1218
+ resume(Effect.succeed(result));
1219
+ return;
1220
+ }
1221
+ if (response.choice.version === "apply-to-all") {
1222
+ const applyOptions = options.filter((o) => o.value.version !== "custom" && o.value.version !== "apply-to-all");
1223
+ const applyResponse = await prompts({
1224
+ type: "select",
1225
+ name: "choice",
1226
+ message: `Apply to all ${remainingCount} remaining packages`,
1227
+ choices: applyOptions.map((o) => ({
1228
+ title: o.title,
1229
+ value: o.value
1230
+ }))
1231
+ });
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
1244
+ };
1245
+ resume(Effect.succeed(result));
1246
+ } else promptForVersion(pkg, conventionalBump, remainingCount).pipe(Effect.runPromise).then((r) => resume(Effect.succeed(r)));
1247
+ return;
1248
+ }
1249
+ let selectedVersion = response.choice.version;
1250
+ let selectedBumpType = response.choice.bumpType;
1251
+ if (selectedVersion === "custom") {
1252
+ const customVersion = await promptForCustomVersion(pkg.version);
1253
+ if (customVersion) selectedVersion = customVersion;
1254
+ else {
1255
+ selectedVersion = pkg.version;
1256
+ selectedBumpType = "none";
1257
+ }
1258
+ }
1259
+ const result = {
1260
+ newVersion: selectedVersion,
1261
+ bumpType: selectedBumpType,
1262
+ applyToAllRemaining: false
1263
+ };
1264
+ resume(Effect.succeed(result));
1265
+ });
1266
+ });
1267
+ }
1268
+ return {
1269
+ promptForVersion,
1270
+ isEnabled: config.prompts.versions,
1271
+ resetApplyToAll: () => {
1272
+ applyToAllRemainingChoice = null;
1273
+ }
1274
+ };
1275
+ }),
1276
+ dependencies: []
1277
+ }) {};
1278
+
1006
1279
  //#endregion
1007
1280
  //#region src/utils/helpers.ts
1008
1281
  function loadOverrides(options) {
@@ -1197,6 +1470,7 @@ function constructPrepareProgram(config) {
1197
1470
  const dependencyGraph = yield* DependencyGraphService;
1198
1471
  const packageUpdater = yield* PackageUpdaterService;
1199
1472
  const versionCalculator = yield* VersionCalculatorService;
1473
+ const versionPrompt = yield* VersionPromptService;
1200
1474
  const workspace = yield* WorkspaceService;
1201
1475
  yield* git.workspace.assertWorkspaceReady;
1202
1476
  let releasePullRequest = yield* github.getPullRequestByBranch(config.branch.release);
@@ -1225,10 +1499,56 @@ function constructPrepareProgram(config) {
1225
1499
  yield* git.branches.checkout(config.branch.default);
1226
1500
  const packages = yield* workspace.discoverWorkspacePackages.pipe(Effect.flatMap(mergePackageCommitsIntoPackages), Effect.flatMap((pkgs) => mergeCommitsAffectingGloballyIntoPackage(pkgs, config.globalCommitMode)));
1227
1501
  yield* Console.log(`๐Ÿ“ฆ Discovered ${packages.length} packages with commits.`);
1228
- const releases = yield* versionCalculator.calculateBumps(packages, overrides);
1229
1502
  yield* dependencyGraph.topologicalOrder(packages);
1503
+ const releases = [];
1504
+ if (versionPrompt.isEnabled) {
1505
+ yield* Console.log("\n๐ŸŽฏ Interactive version selection enabled.\n");
1506
+ versionPrompt.resetApplyToAll();
1507
+ for (let i = 0; i < packages.length; i++) {
1508
+ const pkg = packages[i];
1509
+ const conventionalBump = determineBump([...pkg.commits, ...pkg.globalCommits]);
1510
+ const remainingCount = packages.length - i;
1511
+ const override = overrides[pkg.name];
1512
+ if (override) {
1513
+ if (!semver.valid(override)) return yield* Effect.fail(/* @__PURE__ */ new Error(`Invalid override version for ${pkg.name}: ${override}`));
1514
+ releases.push({
1515
+ package: {
1516
+ name: pkg.name,
1517
+ version: pkg.version,
1518
+ path: pkg.path,
1519
+ packageJson: pkg.packageJson,
1520
+ workspaceDependencies: pkg.workspaceDependencies,
1521
+ workspaceDevDependencies: pkg.workspaceDevDependencies
1522
+ },
1523
+ currentVersion: pkg.version,
1524
+ newVersion: override,
1525
+ bumpType: "none",
1526
+ hasDirectChanges: pkg.commits.length > 0
1527
+ });
1528
+ continue;
1529
+ }
1530
+ const result = yield* versionPrompt.promptForVersion(pkg, conventionalBump, remainingCount);
1531
+ releases.push({
1532
+ package: {
1533
+ name: pkg.name,
1534
+ version: pkg.version,
1535
+ path: pkg.path,
1536
+ packageJson: pkg.packageJson,
1537
+ workspaceDependencies: pkg.workspaceDependencies,
1538
+ workspaceDevDependencies: pkg.workspaceDevDependencies
1539
+ },
1540
+ currentVersion: pkg.version,
1541
+ newVersion: result.newVersion,
1542
+ bumpType: result.bumpType,
1543
+ hasDirectChanges: pkg.commits.length > 0
1544
+ });
1545
+ }
1546
+ } else {
1547
+ const calculatedReleases = yield* versionCalculator.calculateBumps(packages, overrides);
1548
+ releases.push(...calculatedReleases);
1549
+ }
1230
1550
  const releasesCount = releases.length;
1231
- yield* Console.log(`๐Ÿ“Š ${releasesCount} package${releasesCount === 1 ? "" : "s"} will be released.`);
1551
+ yield* Console.log(`\n๐Ÿ“Š ${releasesCount} package${releasesCount === 1 ? "" : "s"} will be released.`);
1232
1552
  yield* git.branches.checkout(originalBranch);
1233
1553
  yield* Console.log("โœ๏ธ Updating package.json files...");
1234
1554
  yield* packageUpdater.applyReleases(packages, releases);
@@ -1283,6 +1603,8 @@ ${releases.map((r) => ` - ${r.package.name}@${r.newVersion}`).join("\n")}`;
1283
1603
  yield* Console.log("โœ… Pull request updated.");
1284
1604
  }
1285
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}".`);
1286
1608
  });
1287
1609
  }
1288
1610
 
@@ -1511,7 +1833,7 @@ function constructVerifyProgram(config) {
1511
1833
  //#region src/index.ts
1512
1834
  async function createReleaseScripts(options) {
1513
1835
  const config = normalizeReleaseScriptsOptions(options);
1514
- const AppLayer = Layer.mergeAll(ChangelogService.Default, GitService.Default, GitHubService.Default, DependencyGraphService.Default, NPMService.Default, PackageUpdaterService.Default, VersionCalculatorService.Default, WorkspaceService.Default).pipe(Layer.provide(Layer.succeed(ReleaseScriptsOptions, config)), Layer.provide(NodeCommandExecutor.layer), Layer.provide(NodeFileSystem.layer));
1836
+ const AppLayer = Layer.mergeAll(ChangelogService.Default, GitService.Default, GitHubService.Default, DependencyGraphService.Default, NPMService.Default, PackageUpdaterService.Default, VersionCalculatorService.Default, VersionPromptService.Default, WorkspaceService.Default).pipe(Layer.provide(Layer.succeed(ReleaseScriptsOptions, config)), Layer.provide(NodeCommandExecutor.layer), Layer.provide(NodeFileSystem.layer));
1515
1837
  const runProgram = (program) => {
1516
1838
  const provided = program.pipe(Effect.provide(AppLayer));
1517
1839
  return Effect.runPromise(provided);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ucdjs/release-scripts",
3
- "version": "0.1.0-beta.31",
3
+ "version": "0.1.0-beta.32",
4
4
  "description": "@ucdjs release scripts",
5
5
  "type": "module",
6
6
  "license": "MIT",