@ucdjs/release-scripts 0.1.0-beta.45 → 0.1.0-beta.47

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
@@ -90,6 +90,10 @@ interface PackageRelease {
90
90
  * Whether this package has direct changes (vs being updated due to dependency changes)
91
91
  */
92
92
  hasDirectChanges: boolean;
93
+ /**
94
+ * Why/how this release entry exists.
95
+ */
96
+ changeKind: "auto" | "manual" | "as-is" | "dependent";
93
97
  }
94
98
  //#endregion
95
99
  //#region src/types.d.ts
package/dist/index.mjs CHANGED
@@ -8,8 +8,8 @@ import farver from "farver";
8
8
  import mri from "mri";
9
9
  import { exec } from "tinyexec";
10
10
  import { dedent } from "@luxass/utils";
11
+ import semver, { compare, gt } from "semver";
11
12
  import prompts from "prompts";
12
- import { compare, gt } from "semver";
13
13
 
14
14
  //#region src/operations/changelog-format.ts
15
15
  function formatCommitLine({ commit, owner, repo, authors }) {
@@ -1003,42 +1003,38 @@ function parseChangelog(content) {
1003
1003
  //#endregion
1004
1004
  //#region src/operations/semver.ts
1005
1005
  function isValidSemver(version) {
1006
- return /^\d+\.\d+\.\d+(?:[-+].+)?$/.test(version);
1006
+ return semver.valid(version) != null;
1007
1007
  }
1008
1008
  function getNextVersion(currentVersion, bump) {
1009
1009
  if (bump === "none") return currentVersion;
1010
1010
  if (!isValidSemver(currentVersion)) throw new Error(`Cannot bump version for invalid semver: ${currentVersion}`);
1011
- const match = currentVersion.match(/^(\d+)\.(\d+)\.(\d+)(.*)$/);
1012
- if (!match) throw new Error(`Invalid semver version: ${currentVersion}`);
1013
- const [, major, minor, patch] = match;
1014
- let newMajor = Number.parseInt(major, 10);
1015
- let newMinor = Number.parseInt(minor, 10);
1016
- let newPatch = Number.parseInt(patch, 10);
1017
- switch (bump) {
1018
- case "major":
1019
- newMajor += 1;
1020
- newMinor = 0;
1021
- newPatch = 0;
1022
- break;
1023
- case "minor":
1024
- newMinor += 1;
1025
- newPatch = 0;
1026
- break;
1027
- case "patch":
1028
- newPatch += 1;
1029
- break;
1030
- }
1031
- return `${newMajor}.${newMinor}.${newPatch}`;
1011
+ const next = semver.inc(currentVersion, bump);
1012
+ if (!next) throw new Error(`Failed to bump version ${currentVersion} with bump ${bump}`);
1013
+ return next;
1032
1014
  }
1033
1015
  function calculateBumpType(oldVersion, newVersion) {
1034
1016
  if (!isValidSemver(oldVersion) || !isValidSemver(newVersion)) throw new Error(`Cannot calculate bump type for invalid semver: ${oldVersion} or ${newVersion}`);
1035
- const oldParts = oldVersion.split(".").map(Number);
1036
- const newParts = newVersion.split(".").map(Number);
1037
- if (newParts[0] > oldParts[0]) return "major";
1038
- if (newParts[1] > oldParts[1]) return "minor";
1039
- if (newParts[2] > oldParts[2]) return "patch";
1017
+ const diff = semver.diff(oldVersion, newVersion);
1018
+ if (!diff) return "none";
1019
+ if (diff === "major" || diff === "premajor") return "major";
1020
+ if (diff === "minor" || diff === "preminor") return "minor";
1021
+ if (diff === "patch" || diff === "prepatch" || diff === "prerelease") return "patch";
1022
+ if (semver.gt(newVersion, oldVersion)) return "patch";
1040
1023
  return "none";
1041
1024
  }
1025
+ function getPrereleaseIdentifier(version) {
1026
+ const parsed = semver.parse(version);
1027
+ if (!parsed || parsed.prerelease.length === 0) return;
1028
+ const identifier = parsed.prerelease[0];
1029
+ return typeof identifier === "string" ? identifier : void 0;
1030
+ }
1031
+ function getNextPrereleaseVersion(currentVersion, mode, identifier) {
1032
+ if (!isValidSemver(currentVersion)) throw new Error(`Cannot bump prerelease for invalid semver: ${currentVersion}`);
1033
+ const releaseType = mode === "next" ? "prerelease" : mode;
1034
+ const next = identifier ? semver.inc(currentVersion, releaseType, identifier) : semver.inc(currentVersion, releaseType);
1035
+ if (!next) throw new Error(`Failed to compute prerelease version for ${currentVersion}`);
1036
+ return next;
1037
+ }
1042
1038
 
1043
1039
  //#endregion
1044
1040
  //#region src/core/prompts.ts
@@ -1059,7 +1055,20 @@ async function selectPackagePrompt(packages) {
1059
1055
  if (!response.selectedPackages || response.selectedPackages.length === 0) return [];
1060
1056
  return response.selectedPackages;
1061
1057
  }
1062
- async function selectVersionPrompt(workspaceRoot, pkg, currentVersion, suggestedVersion) {
1058
+ async function selectVersionPrompt(workspaceRoot, pkg, currentVersion, suggestedVersion, options) {
1059
+ const defaultChoice = options?.defaultChoice ?? "auto";
1060
+ const suggestedSuffix = options?.suggestedHint ? farver.dim(` (${options.suggestedHint})`) : "";
1061
+ const prereleaseIdentifier = getPrereleaseIdentifier(currentVersion);
1062
+ const nextDefaultPrerelease = getNextPrereleaseVersion(currentVersion, "next", prereleaseIdentifier === "alpha" || prereleaseIdentifier === "beta" ? prereleaseIdentifier : "beta");
1063
+ const nextBeta = getNextPrereleaseVersion(currentVersion, "next", "beta");
1064
+ const nextAlpha = getNextPrereleaseVersion(currentVersion, "next", "alpha");
1065
+ const prePatchBeta = getNextPrereleaseVersion(currentVersion, "prepatch", "beta");
1066
+ const preMinorBeta = getNextPrereleaseVersion(currentVersion, "preminor", "beta");
1067
+ const preMajorBeta = getNextPrereleaseVersion(currentVersion, "premajor", "beta");
1068
+ const prePatchAlpha = getNextPrereleaseVersion(currentVersion, "prepatch", "alpha");
1069
+ const preMinorAlpha = getNextPrereleaseVersion(currentVersion, "preminor", "alpha");
1070
+ const preMajorAlpha = getNextPrereleaseVersion(currentVersion, "premajor", "alpha");
1071
+ const initial = defaultChoice === "skip" ? 0 : defaultChoice === "suggested" ? 4 : defaultChoice === "as-is" ? 5 : suggestedVersion === currentVersion ? 0 : 4;
1063
1072
  const answers = await prompts([{
1064
1073
  type: "autocomplete",
1065
1074
  name: "version",
@@ -1081,9 +1090,45 @@ async function selectVersionPrompt(workspaceRoot, pkg, currentVersion, suggested
1081
1090
  value: "patch",
1082
1091
  title: `patch ${farver.bold(getNextVersion(pkg.version, "patch"))}`
1083
1092
  },
1093
+ {
1094
+ value: "next",
1095
+ title: `next ${farver.bold(nextDefaultPrerelease)}`
1096
+ },
1097
+ {
1098
+ value: "prepatch-beta",
1099
+ title: `pre-patch (beta) ${farver.bold(prePatchBeta)}`
1100
+ },
1101
+ {
1102
+ value: "preminor-beta",
1103
+ title: `pre-minor (beta) ${farver.bold(preMinorBeta)}`
1104
+ },
1105
+ {
1106
+ value: "premajor-beta",
1107
+ title: `pre-major (beta) ${farver.bold(preMajorBeta)}`
1108
+ },
1109
+ {
1110
+ value: "prepatch-alpha",
1111
+ title: `pre-patch (alpha) ${farver.bold(prePatchAlpha)}`
1112
+ },
1113
+ {
1114
+ value: "preminor-alpha",
1115
+ title: `pre-minor (alpha) ${farver.bold(preMinorAlpha)}`
1116
+ },
1117
+ {
1118
+ value: "premajor-alpha",
1119
+ title: `pre-major (alpha) ${farver.bold(preMajorAlpha)}`
1120
+ },
1121
+ {
1122
+ value: "next-beta",
1123
+ title: `next beta ${farver.bold(nextBeta)}`
1124
+ },
1125
+ {
1126
+ value: "next-alpha",
1127
+ title: `next alpha ${farver.bold(nextAlpha)}`
1128
+ },
1084
1129
  {
1085
1130
  value: "suggested",
1086
- title: `suggested ${farver.bold(suggestedVersion)}`
1131
+ title: `suggested ${farver.bold(suggestedVersion)}${suggestedSuffix}`
1087
1132
  },
1088
1133
  {
1089
1134
  value: "as-is",
@@ -1094,7 +1139,7 @@ async function selectVersionPrompt(workspaceRoot, pkg, currentVersion, suggested
1094
1139
  title: "custom"
1095
1140
  }
1096
1141
  ],
1097
- initial: suggestedVersion === currentVersion ? 0 : 4
1142
+ initial
1098
1143
  }, {
1099
1144
  type: (prev) => prev === "custom" ? "text" : null,
1100
1145
  name: "custom",
@@ -1112,6 +1157,15 @@ async function selectVersionPrompt(workspaceRoot, pkg, currentVersion, suggested
1112
1157
  if (!answers.custom) return null;
1113
1158
  return answers.custom;
1114
1159
  } else if (answers.version === "as-is") return currentVersion;
1160
+ else if (answers.version === "next") return nextDefaultPrerelease;
1161
+ else if (answers.version === "next-beta") return nextBeta;
1162
+ else if (answers.version === "next-alpha") return nextAlpha;
1163
+ else if (answers.version === "prepatch-beta") return prePatchBeta;
1164
+ else if (answers.version === "preminor-beta") return preMinorBeta;
1165
+ else if (answers.version === "premajor-beta") return preMajorBeta;
1166
+ else if (answers.version === "prepatch-alpha") return prePatchAlpha;
1167
+ else if (answers.version === "preminor-alpha") return preMinorAlpha;
1168
+ else if (answers.version === "premajor-alpha") return preMajorAlpha;
1115
1169
  else return getNextVersion(pkg.version, answers.version);
1116
1170
  }
1117
1171
 
@@ -1415,7 +1469,8 @@ function createVersionUpdate(pkg, bump, hasDirectChanges) {
1415
1469
  currentVersion: pkg.version,
1416
1470
  newVersion,
1417
1471
  bumpType: bump,
1418
- hasDirectChanges
1472
+ hasDirectChanges,
1473
+ changeKind: "dependent"
1419
1474
  };
1420
1475
  }
1421
1476
  function determineBumpType(commit) {
@@ -1637,40 +1692,50 @@ async function calculateVersionUpdates({ workspacePackages, packageCommits, work
1637
1692
  logger.section(`📝 Commits for ${farver.cyan(pkg.name)}`);
1638
1693
  formatCommitsForDisplay(allCommitsForPackage).split("\n").forEach((line) => logger.item(line));
1639
1694
  logger.emptyLine();
1640
- const selectedVersion = await selectVersionPrompt(workspaceRoot, pkg, pkg.version, newVersion);
1695
+ const selectedVersion = await selectVersionPrompt(workspaceRoot, pkg, pkg.version, newVersion, {
1696
+ defaultChoice: override ? "suggested" : "auto",
1697
+ suggestedHint: override ? "from override" : void 0
1698
+ });
1641
1699
  if (selectedVersion === null) continue;
1642
1700
  const userBump = calculateBumpType(pkg.version, selectedVersion);
1643
1701
  finalBumpType = userBump;
1644
1702
  if (selectedVersion === pkg.version) {
1645
1703
  excludedPackages.add(pkgName);
1646
1704
  if (determinedBump !== "none") {
1647
- newOverrides[pkgName] = {
1705
+ const nextOverride = {
1648
1706
  type: "none",
1649
1707
  version: pkg.version
1650
1708
  };
1651
- logger.info(`Version override recorded for ${pkgName}: ${determinedBump} none`);
1709
+ if (!override || override.type !== nextOverride.type || override.version !== nextOverride.version) {
1710
+ newOverrides[pkgName] = nextOverride;
1711
+ logger.info(`Override set for ${pkgName}: suggested as-is (${pkg.version}) from auto ${determinedBump}`);
1712
+ }
1652
1713
  } else if (newOverrides[pkgName]) {
1653
1714
  delete newOverrides[pkgName];
1654
- logger.info(`Version override removed for ${pkgName}.`);
1715
+ logger.info(`Override cleared for ${pkgName}.`);
1655
1716
  }
1656
1717
  versionUpdates.push({
1657
1718
  package: pkg,
1658
1719
  currentVersion: pkg.version,
1659
1720
  newVersion: pkg.version,
1660
1721
  bumpType: "none",
1661
- hasDirectChanges: allCommitsForPackage.length > 0
1722
+ hasDirectChanges: allCommitsForPackage.length > 0,
1723
+ changeKind: "as-is"
1662
1724
  });
1663
1725
  continue;
1664
1726
  }
1665
1727
  if (bumpRanks[userBump] < bumpRanks[determinedBump]) {
1666
- newOverrides[pkgName] = {
1728
+ const nextOverride = {
1667
1729
  type: userBump,
1668
1730
  version: selectedVersion
1669
1731
  };
1670
- logger.info(`Version override recorded for ${pkgName}: ${determinedBump} ${userBump}`);
1732
+ if (!override || override.type !== nextOverride.type || override.version !== nextOverride.version) {
1733
+ newOverrides[pkgName] = nextOverride;
1734
+ logger.info(`Override set for ${pkgName}: suggested ${userBump} (${selectedVersion}) from auto ${determinedBump}`);
1735
+ }
1671
1736
  } else if (newOverrides[pkgName] && bumpRanks[userBump] >= bumpRanks[determinedBump]) {
1672
1737
  delete newOverrides[pkgName];
1673
- logger.info(`Version override removed for ${pkgName}.`);
1738
+ logger.info(`Override cleared for ${pkgName}.`);
1674
1739
  }
1675
1740
  newVersion = selectedVersion;
1676
1741
  }
@@ -1679,7 +1744,8 @@ async function calculateVersionUpdates({ workspacePackages, packageCommits, work
1679
1744
  currentVersion: pkg.version,
1680
1745
  newVersion,
1681
1746
  bumpType: finalBumpType,
1682
- hasDirectChanges: allCommitsForPackage.length > 0
1747
+ hasDirectChanges: allCommitsForPackage.length > 0,
1748
+ changeKind: canPrompt ? "manual" : "auto"
1683
1749
  });
1684
1750
  }
1685
1751
  if (!isCI && showPrompt) for (const pkg of workspacePackages) {
@@ -1699,7 +1765,8 @@ async function calculateVersionUpdates({ workspacePackages, packageCommits, work
1699
1765
  currentVersion: pkg.version,
1700
1766
  newVersion,
1701
1767
  bumpType,
1702
- hasDirectChanges: false
1768
+ hasDirectChanges: false,
1769
+ changeKind: "manual"
1703
1770
  });
1704
1771
  }
1705
1772
  return {
@@ -1896,8 +1963,9 @@ async function prepareWorkflow(options) {
1896
1963
  });
1897
1964
  if (!updatesResult.ok) exitWithError("Failed to calculate package updates.", void 0, updatesResult.error);
1898
1965
  const { allUpdates, applyUpdates, overrides: newOverrides } = updatesResult.value;
1899
- if (Object.keys(newOverrides).length > 0) {
1900
- logger.info("Writing version overrides file...");
1966
+ const hasOverrideChanges = JSON.stringify(existingOverrides) !== JSON.stringify(newOverrides);
1967
+ if (Object.keys(newOverrides).length > 0 && hasOverrideChanges) {
1968
+ logger.step("Writing version overrides file...");
1901
1969
  try {
1902
1970
  await mkdir(join(options.workspaceRoot, ".github"), { recursive: true });
1903
1971
  await writeFile(overridesPath, JSON.stringify(newOverrides, null, 2), "utf-8");
@@ -1905,7 +1973,7 @@ async function prepareWorkflow(options) {
1905
1973
  } catch (e) {
1906
1974
  logger.error("Failed to write version overrides file:", e);
1907
1975
  }
1908
- }
1976
+ } else if (Object.keys(newOverrides).length > 0) logger.step("Version overrides unchanged. Skipping write.");
1909
1977
  if (Object.keys(newOverrides).length === 0 && Object.keys(existingOverrides).length > 0) {
1910
1978
  let shouldRemoveOverrides = false;
1911
1979
  for (const update of allUpdates) {
@@ -1931,7 +1999,7 @@ async function prepareWorkflow(options) {
1931
1999
  logger.section("🔄 Version Updates");
1932
2000
  logger.item(`Updating ${allUpdates.length} packages (including dependents)`);
1933
2001
  for (const update of allUpdates) {
1934
- const suffix = update.currentVersion === update.newVersion ? farver.dim(" (as-is)") : "";
2002
+ const suffix = update.changeKind === "as-is" ? farver.dim(" (as-is)") : "";
1935
2003
  logger.item(`${update.package.name}: ${update.currentVersion} → ${update.newVersion}${suffix}`);
1936
2004
  }
1937
2005
  await applyUpdates();
@@ -1983,6 +2051,8 @@ async function prepareWorkflow(options) {
1983
2051
  if (!prResult.ok) exitWithError("Failed to sync release pull request.", void 0, prResult.error);
1984
2052
  if (prResult.value.pullRequest) {
1985
2053
  logger.item("No updates needed, PR is already up to date");
2054
+ const checkoutResult = await checkoutBranch(options.branch.default, options.workspaceRoot);
2055
+ if (!checkoutResult.ok) exitWithError(`Failed to checkout branch: ${options.branch.default}`, void 0, checkoutResult.error);
1986
2056
  return {
1987
2057
  updates: allUpdates,
1988
2058
  prUrl: prResult.value.pullRequest.html_url,
@@ -2095,11 +2165,12 @@ async function buildPackage(packageName, workspaceRoot, options) {
2095
2165
  * Publish a package to NPM
2096
2166
  * Uses pnpm to handle workspace protocol and catalog: resolution automatically
2097
2167
  * @param packageName - The package name to publish
2168
+ * @param version - The package version to publish
2098
2169
  * @param workspaceRoot - Path to the workspace root
2099
2170
  * @param options - Normalized release scripts options
2100
2171
  * @returns Result indicating success or failure
2101
2172
  */
2102
- async function publishPackage(packageName, workspaceRoot, options) {
2173
+ async function publishPackage(packageName, version, workspaceRoot, options) {
2103
2174
  const args = [
2104
2175
  "--filter",
2105
2176
  packageName,
@@ -2109,7 +2180,16 @@ async function publishPackage(packageName, workspaceRoot, options) {
2109
2180
  "--no-git-checks"
2110
2181
  ];
2111
2182
  if (options.npm.otp) args.push("--otp", options.npm.otp);
2112
- if (process.env.NPM_CONFIG_TAG) args.push("--tag", process.env.NPM_CONFIG_TAG);
2183
+ const explicitTag = process.env.NPM_CONFIG_TAG;
2184
+ const prereleaseTag = (() => {
2185
+ const prerelease = semver.prerelease(version);
2186
+ if (!prerelease || prerelease.length === 0) return;
2187
+ const identifier = prerelease[0];
2188
+ if (identifier === "alpha" || identifier === "beta") return identifier;
2189
+ return "next";
2190
+ })();
2191
+ const publishTag = explicitTag || prereleaseTag;
2192
+ if (publishTag) args.push("--tag", publishTag);
2113
2193
  const env = { ...process.env };
2114
2194
  if (options.npm.provenance) env.NPM_CONFIG_PROVENANCE = "true";
2115
2195
  try {
@@ -2173,7 +2253,7 @@ async function publishWorkflow(options) {
2173
2253
  }
2174
2254
  }
2175
2255
  logger.step(`Publishing ${farver.cyan(`${packageName}@${version}`)} to NPM...`);
2176
- const publishResult = await publishPackage(packageName, options.workspaceRoot, options);
2256
+ const publishResult = await publishPackage(packageName, version, options.workspaceRoot, options);
2177
2257
  if (!publishResult.ok) {
2178
2258
  logger.error(`Failed to publish: ${publishResult.error.message}`);
2179
2259
  status.failed.push(packageName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ucdjs/release-scripts",
3
- "version": "0.1.0-beta.45",
3
+ "version": "0.1.0-beta.47",
4
4
  "description": "@ucdjs release scripts",
5
5
  "type": "module",
6
6
  "license": "MIT",