package-versioner 0.7.1 → 0.8.1

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.cjs CHANGED
@@ -138,7 +138,23 @@ function extractChangelogEntriesFromCommits(projectDir, revisionRange) {
138
138
  const commits = output.split("---COMMIT_DELIMITER---").filter((commit) => commit.trim() !== "");
139
139
  return commits.map((commit) => parseCommitMessage(commit)).filter((entry) => entry !== null);
140
140
  } catch (error) {
141
- log(`Error extracting commits: ${error}`, "error");
141
+ const errorMessage = error instanceof Error ? error.message : String(error);
142
+ if (errorMessage.includes("ambiguous argument") && errorMessage.includes("unknown revision")) {
143
+ const tagName = revisionRange.split("..")[0] || revisionRange;
144
+ if (tagName.startsWith("v") && !tagName.includes("@")) {
145
+ log(
146
+ `Error: Tag "${tagName}" not found. If you're using package-specific tags (like "package-name@v1.0.0"), you may need to configure "tagTemplate" in your version.config.json to use: \${packageName}@\${prefix}\${version}`,
147
+ "error"
148
+ );
149
+ } else {
150
+ log(
151
+ `Error: Tag or revision "${tagName}" not found in the repository. Please check if this tag exists or if you need to fetch it from the remote.`,
152
+ "error"
153
+ );
154
+ }
155
+ } else {
156
+ log(`Error extracting commits: ${errorMessage}`, "error");
157
+ }
142
158
  return [];
143
159
  }
144
160
  }
@@ -783,43 +799,48 @@ async function createGitCommitAndTag(files, nextTag, commitMessage, skipHooks, d
783
799
 
784
800
  // src/git/tagsAndBranches.ts
785
801
  var import_git_semver_tags = require("git-semver-tags");
802
+ var import_semver = __toESM(require("semver"), 1);
786
803
 
787
804
  // src/utils/formatting.ts
788
805
  function escapeRegExp(string) {
789
806
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
790
807
  }
791
- function formatTag(version, versionPrefix, packageName, tagTemplate = "${prefix}${version}", packageTagTemplate = "${packageName}@${prefix}${version}") {
792
- const variables = {
793
- version,
794
- prefix: versionPrefix || "",
795
- packageName: packageName || ""
796
- };
797
- const template = packageName ? packageTagTemplate : tagTemplate;
798
- return createTemplateString(template, variables);
799
- }
800
- function formatVersionPrefix(versionPrefix, scope) {
801
- if (!versionPrefix) return "";
802
- const cleanPrefix = versionPrefix.replace(/\/$/, "");
803
- if (scope) {
804
- return `${cleanPrefix}/${scope}`;
805
- }
806
- return cleanPrefix;
807
- }
808
- function formatCommitMessage(template, version, packageName, scope) {
809
- return createTemplateString(template, {
810
- version,
811
- scope,
812
- packageName: packageName || ""
813
- });
808
+ function formatVersionPrefix(prefix) {
809
+ return prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
810
+ }
811
+ function formatTag(version, prefix, packageName, template, packageSpecificTags) {
812
+ if ((template == null ? void 0 : template.includes("${packageName}")) && !packageName) {
813
+ log(
814
+ 'Warning: Your tagTemplate contains ${packageName} but no package name is available.\nThis will result in an empty package name in the tag (e.g., "@v1.0.0" instead of "my-package@v1.0.0").\n\nTo fix this:\n\u2022 If using synced mode: Set "packageSpecificTags": true in your config to enable package names in tags\n\u2022 If you want global tags: Remove ${packageName} from your tagTemplate (e.g., use "${prefix}${version}")\n\u2022 If using single/async mode: Ensure your package.json has a valid "name" field',
815
+ "warning"
816
+ );
817
+ }
818
+ if (template) {
819
+ return template.replace(/\$\{version\}/g, version).replace(/\$\{prefix\}/g, prefix).replace(/\$\{packageName\}/g, packageName || "");
820
+ }
821
+ if (packageSpecificTags && packageName) {
822
+ return `${packageName}@${prefix}${version}`;
823
+ }
824
+ return `${prefix}${version}`;
814
825
  }
815
- function createTemplateString(template, variables) {
816
- return Object.entries(variables).reduce((result, [key, value]) => {
817
- if (value === void 0) {
818
- return result;
826
+ function formatCommitMessage(template, version, packageName, additionalContext) {
827
+ if (template.includes("${packageName}") && !packageName) {
828
+ log(
829
+ 'Warning: Your commitMessage template contains ${packageName} but no package name is available.\nThis will result in an empty package name in the commit message (e.g., "Release @v1.0.0").\n\nTo fix this:\n\u2022 If using synced mode: Set "packageSpecificTags": true to enable package names in commits\n\u2022 If you want generic commit messages: Remove ${packageName} from your commitMessage template\n\u2022 If using single/async mode: Ensure your package.json has a valid "name" field',
830
+ "warning"
831
+ );
832
+ }
833
+ let result = template.replace(/\$\{version\}/g, version).replace(/\$\{packageName\}/g, packageName || "");
834
+ if (additionalContext) {
835
+ for (const [key, value] of Object.entries(additionalContext)) {
836
+ const placeholder = `\${${key}}`;
837
+ result = result.replace(
838
+ new RegExp(placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
839
+ value
840
+ );
819
841
  }
820
- const regex = new RegExp(`\\$\\{${key}\\}`, "g");
821
- return result.replace(regex, value);
822
- }, template);
842
+ }
843
+ return result;
823
844
  }
824
845
 
825
846
  // src/git/tagsAndBranches.ts
@@ -834,10 +855,29 @@ function getCommitsLength(pkgRoot) {
834
855
  return 0;
835
856
  }
836
857
  }
837
- async function getLatestTag() {
858
+ async function getLatestTag(versionPrefix) {
838
859
  try {
839
- const tags = await (0, import_git_semver_tags.getSemverTags)({});
840
- return tags[0] || "";
860
+ const tags = await (0, import_git_semver_tags.getSemverTags)({
861
+ tagPrefix: versionPrefix
862
+ });
863
+ if (tags.length === 0) {
864
+ return "";
865
+ }
866
+ const chronologicalLatest = tags[0];
867
+ const sortedTags = [...tags].sort((a, b) => {
868
+ const versionA = import_semver.default.clean(a) || "0.0.0";
869
+ const versionB = import_semver.default.clean(b) || "0.0.0";
870
+ return import_semver.default.rcompare(versionA, versionB);
871
+ });
872
+ const semanticLatest = sortedTags[0];
873
+ if (semanticLatest !== chronologicalLatest) {
874
+ log(
875
+ `Tag ordering differs: chronological latest is ${chronologicalLatest}, semantic latest is ${semanticLatest}`,
876
+ "debug"
877
+ );
878
+ log(`Using semantic latest (${semanticLatest}) to handle out-of-order tag creation`, "info");
879
+ }
880
+ return semanticLatest;
841
881
  } catch (error) {
842
882
  const errorMessage = error instanceof Error ? error.message : String(error);
843
883
  log(`Failed to get latest tag: ${errorMessage}`, "error");
@@ -862,56 +902,122 @@ async function lastMergeBranchName(branches, baseBranch) {
862
902
  return null;
863
903
  }
864
904
  }
865
- async function getLatestTagForPackage(packageName, versionPrefix) {
905
+ async function getLatestTagForPackage(packageName, versionPrefix, options) {
866
906
  try {
907
+ const tagTemplate = (options == null ? void 0 : options.tagTemplate) || "${prefix}${version}";
908
+ const packageSpecificTags = (options == null ? void 0 : options.packageSpecificTags) ?? false;
867
909
  const escapedPackageName = escapeRegExp(packageName);
910
+ const escapedPrefix = versionPrefix ? escapeRegExp(versionPrefix) : "";
868
911
  log(
869
- `Looking for tags for package ${packageName} with prefix ${versionPrefix || "none"}`,
912
+ `Looking for tags for package ${packageName} with prefix ${versionPrefix || "none"}, packageSpecificTags: ${packageSpecificTags}`,
870
913
  "debug"
871
914
  );
872
915
  const allTags = await (0, import_git_semver_tags.getSemverTags)({
873
916
  tagPrefix: versionPrefix
874
917
  });
875
918
  log(`Retrieved ${allTags.length} tags: ${allTags.join(", ")}`, "debug");
876
- let packageTags = [];
877
- if (versionPrefix) {
878
- const pattern1 = new RegExp(`^${escapedPackageName}@${escapeRegExp(versionPrefix)}`);
879
- packageTags = allTags.filter((tag) => pattern1.test(tag));
919
+ if (packageSpecificTags) {
920
+ const packageTagPattern = escapeRegExp(tagTemplate).replace(/\\\$\\\{packageName\\\}/g, `(?:${escapedPackageName})`).replace(/\\\$\\\{prefix\\\}/g, `(?:${escapedPrefix})`).replace(/\\\$\\\{version\\\}/g, "(?:[0-9]+\\.[0-9]+\\.[0-9]+(?:-[a-zA-Z0-9.-]+)?)");
921
+ log(`Using package tag pattern: ${packageTagPattern}`, "debug");
922
+ const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
923
+ let packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
880
924
  if (packageTags.length > 0) {
881
- log(
882
- `Found ${packageTags.length} package tags using pattern: packageName@${versionPrefix}...`,
883
- "debug"
884
- );
885
- log(`Using tag: ${packageTags[0]}`, "debug");
886
- return packageTags[0];
925
+ const chronologicalFirst = packageTags[0];
926
+ const sortedPackageTags2 = [...packageTags].sort((a, b) => {
927
+ let versionA = "";
928
+ let versionB = "";
929
+ if (a.includes("@")) {
930
+ const afterAt = a.split("@")[1] || "";
931
+ versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
932
+ } else {
933
+ versionA = a.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
934
+ }
935
+ if (b.includes("@")) {
936
+ const afterAtB = b.split("@")[1] || "";
937
+ versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
938
+ } else {
939
+ versionB = b.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
940
+ }
941
+ const cleanVersionA = import_semver.default.clean(versionA) || "0.0.0";
942
+ const cleanVersionB = import_semver.default.clean(versionB) || "0.0.0";
943
+ return import_semver.default.rcompare(cleanVersionA, cleanVersionB);
944
+ });
945
+ log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
946
+ log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
947
+ if (sortedPackageTags2[0] !== chronologicalFirst) {
948
+ log(
949
+ `Package tag ordering differs: chronological first is ${chronologicalFirst}, semantic latest is ${sortedPackageTags2[0]}`,
950
+ "debug"
951
+ );
952
+ }
953
+ return sortedPackageTags2[0];
954
+ }
955
+ if (versionPrefix) {
956
+ const pattern1 = new RegExp(`^${escapedPackageName}@${escapeRegExp(versionPrefix)}`);
957
+ packageTags = allTags.filter((tag) => pattern1.test(tag));
958
+ if (packageTags.length > 0) {
959
+ const sortedPackageTags2 = [...packageTags].sort((a, b) => {
960
+ const afterAt = a.split("@")[1] || "";
961
+ const versionA = afterAt.replace(
962
+ new RegExp(`^${escapeRegExp(versionPrefix || "")}`),
963
+ ""
964
+ );
965
+ const afterAtB = b.split("@")[1] || "";
966
+ const versionB = afterAtB.replace(
967
+ new RegExp(`^${escapeRegExp(versionPrefix || "")}`),
968
+ ""
969
+ );
970
+ const cleanVersionA = import_semver.default.clean(versionA) || "0.0.0";
971
+ const cleanVersionB = import_semver.default.clean(versionB) || "0.0.0";
972
+ return import_semver.default.rcompare(cleanVersionA, cleanVersionB);
973
+ });
974
+ log(
975
+ `Found ${packageTags.length} package tags using pattern: packageName@${versionPrefix}...`,
976
+ "debug"
977
+ );
978
+ log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
979
+ return sortedPackageTags2[0];
980
+ }
887
981
  }
888
- }
889
- if (versionPrefix) {
890
- const pattern2 = new RegExp(`^${escapeRegExp(versionPrefix)}${escapedPackageName}@`);
891
- packageTags = allTags.filter((tag) => pattern2.test(tag));
892
- if (packageTags.length > 0) {
893
- log(
894
- `Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`,
895
- "debug"
896
- );
897
- log(`Using tag: ${packageTags[0]}`, "debug");
898
- return packageTags[0];
982
+ if (versionPrefix) {
983
+ const pattern2 = new RegExp(`^${escapeRegExp(versionPrefix)}${escapedPackageName}@`);
984
+ packageTags = allTags.filter((tag) => pattern2.test(tag));
985
+ if (packageTags.length > 0) {
986
+ const sortedPackageTags2 = [...packageTags].sort((a, b) => {
987
+ const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
988
+ const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
989
+ return import_semver.default.rcompare(versionA, versionB);
990
+ });
991
+ log(
992
+ `Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`,
993
+ "debug"
994
+ );
995
+ log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
996
+ return sortedPackageTags2[0];
997
+ }
899
998
  }
900
- }
901
- const pattern3 = new RegExp(`^${escapedPackageName}@`);
902
- packageTags = allTags.filter((tag) => pattern3.test(tag));
903
- log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
904
- if (packageTags.length === 0) {
905
- log("No matching tags found for pattern: packageName@version", "debug");
906
- if (allTags.length > 0) {
907
- log(`Available tags: ${allTags.join(", ")}`, "debug");
908
- } else {
909
- log("No tags available in the repository", "debug");
999
+ const pattern3 = new RegExp(`^${escapedPackageName}@`);
1000
+ packageTags = allTags.filter((tag) => pattern3.test(tag));
1001
+ if (packageTags.length === 0) {
1002
+ log("No matching tags found for pattern: packageName@version", "debug");
1003
+ if (allTags.length > 0) {
1004
+ log(`Available tags: ${allTags.join(", ")}`, "debug");
1005
+ } else {
1006
+ log("No tags available in the repository", "debug");
1007
+ }
1008
+ return "";
910
1009
  }
911
- } else {
912
- log(`Using tag: ${packageTags[0]}`, "debug");
1010
+ const sortedPackageTags = [...packageTags].sort((a, b) => {
1011
+ const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
1012
+ const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
1013
+ return import_semver.default.rcompare(versionA, versionB);
1014
+ });
1015
+ log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
1016
+ log(`Using semantically latest tag: ${sortedPackageTags[0]}`, "debug");
1017
+ return sortedPackageTags[0];
913
1018
  }
914
- return packageTags[0] || "";
1019
+ log(`Package-specific tags disabled for ${packageName}, falling back to global tags`, "debug");
1020
+ return "";
915
1021
  } catch (error) {
916
1022
  const errorMessage = error instanceof Error ? error.message : String(error);
917
1023
  log(`Failed to get latest tag for package ${packageName}: ${errorMessage}`, "error");
@@ -1014,6 +1120,7 @@ function updatePackageVersion(packagePath, version) {
1014
1120
  }
1015
1121
 
1016
1122
  // src/package/packageProcessor.ts
1123
+ var import_node_child_process4 = require("child_process");
1017
1124
  var fs8 = __toESM(require("fs"), 1);
1018
1125
  var import_node_path6 = __toESM(require("path"), 1);
1019
1126
  var import_node_process4 = require("process");
@@ -1346,7 +1453,7 @@ function capitalizeFirstLetter(input) {
1346
1453
  // src/core/versionCalculator.ts
1347
1454
  var import_node_process3 = require("process");
1348
1455
  var import_conventional_recommended_bump = require("conventional-recommended-bump");
1349
- var import_semver2 = __toESM(require("semver"), 1);
1456
+ var import_semver3 = __toESM(require("semver"), 1);
1350
1457
 
1351
1458
  // src/utils/manifestHelpers.ts
1352
1459
  var import_node_fs5 = __toESM(require("fs"), 1);
@@ -1407,7 +1514,7 @@ function throwIfNoManifestsFound(packageDir) {
1407
1514
 
1408
1515
  // src/utils/versionUtils.ts
1409
1516
  var import_node_fs6 = __toESM(require("fs"), 1);
1410
- var import_semver = __toESM(require("semver"), 1);
1517
+ var import_semver2 = __toESM(require("semver"), 1);
1411
1518
  var TOML2 = __toESM(require("smol-toml"), 1);
1412
1519
  var STANDARD_BUMP_TYPES = ["major", "minor", "patch"];
1413
1520
  function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
@@ -1420,58 +1527,116 @@ function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
1420
1527
  return void 0;
1421
1528
  }
1422
1529
  function bumpVersion(currentVersion, bumpType, prereleaseIdentifier) {
1423
- if (prereleaseIdentifier && STANDARD_BUMP_TYPES.includes(bumpType) && !import_semver.default.prerelease(currentVersion)) {
1530
+ if (prereleaseIdentifier && STANDARD_BUMP_TYPES.includes(bumpType) && !import_semver2.default.prerelease(currentVersion)) {
1424
1531
  const preBumpType = `pre${bumpType}`;
1425
1532
  log(
1426
1533
  `Creating prerelease version with identifier '${prereleaseIdentifier}' using ${preBumpType}`,
1427
1534
  "debug"
1428
1535
  );
1429
- return import_semver.default.inc(currentVersion, preBumpType, prereleaseIdentifier) || "";
1536
+ return import_semver2.default.inc(currentVersion, preBumpType, prereleaseIdentifier) || "";
1430
1537
  }
1431
- if (import_semver.default.prerelease(currentVersion) && STANDARD_BUMP_TYPES.includes(bumpType)) {
1432
- const parsed = import_semver.default.parse(currentVersion);
1538
+ if (import_semver2.default.prerelease(currentVersion) && STANDARD_BUMP_TYPES.includes(bumpType)) {
1539
+ const parsed = import_semver2.default.parse(currentVersion);
1433
1540
  if (!parsed) {
1434
- return import_semver.default.inc(currentVersion, bumpType) || "";
1541
+ return import_semver2.default.inc(currentVersion, bumpType) || "";
1435
1542
  }
1436
1543
  if (bumpType === "major" && parsed.minor === 0 && parsed.patch === 0 || bumpType === "minor" && parsed.patch === 0 || bumpType === "patch") {
1437
1544
  log(`Cleaning prerelease identifier from ${currentVersion} for ${bumpType} bump`, "debug");
1438
1545
  return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
1439
1546
  }
1440
1547
  log(`Standard increment for ${currentVersion} with ${bumpType} bump`, "debug");
1441
- return import_semver.default.inc(currentVersion, bumpType) || "";
1548
+ return import_semver2.default.inc(currentVersion, bumpType) || "";
1442
1549
  }
1443
- return import_semver.default.inc(currentVersion, bumpType, prereleaseIdentifier) || "";
1550
+ return import_semver2.default.inc(currentVersion, bumpType, prereleaseIdentifier) || "";
1444
1551
  }
1445
1552
 
1446
1553
  // src/core/versionCalculator.ts
1447
1554
  async function calculateVersion(config, options) {
1448
1555
  const {
1449
- latestTag = "",
1450
- type,
1451
- versionPrefix = "",
1556
+ type: configType,
1557
+ preset = "angular",
1558
+ versionPrefix,
1559
+ prereleaseIdentifier: configPrereleaseIdentifier,
1452
1560
  branchPattern,
1453
- baseBranch,
1454
- prereleaseIdentifier,
1561
+ baseBranch
1562
+ } = config;
1563
+ const {
1564
+ latestTag,
1565
+ name,
1455
1566
  path: pkgPath,
1456
- name
1567
+ type: optionsType,
1568
+ prereleaseIdentifier: optionsPrereleaseIdentifier
1457
1569
  } = options;
1458
- const preset = config.preset || "conventional-commits";
1570
+ const type = optionsType || configType;
1571
+ const prereleaseIdentifier = optionsPrereleaseIdentifier || configPrereleaseIdentifier;
1459
1572
  const initialVersion = "0.1.0";
1573
+ const hasNoTags = !latestTag || latestTag.trim() === "";
1460
1574
  const normalizedPrereleaseId = normalizePrereleaseIdentifier(prereleaseIdentifier, config);
1461
1575
  try {
1462
1576
  let determineTagSearchPattern2 = function(packageName, prefix) {
1463
- if (packageName) {
1464
- const escapedPackageName = escapeRegExp(packageName);
1465
- const escapedPrefix = escapeRegExp(prefix);
1466
- return `${escapedPackageName}[@]?${escapedPrefix}`;
1577
+ if (!packageName) {
1578
+ return prefix;
1467
1579
  }
1468
- return escapeRegExp(prefix);
1580
+ return `${packageName}@${prefix}`;
1581
+ }, escapeRegExp3 = function(string) {
1582
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1469
1583
  };
1470
- var determineTagSearchPattern = determineTagSearchPattern2;
1471
- const hasNoTags = !latestTag;
1472
- const originalPrefix = versionPrefix;
1584
+ var determineTagSearchPattern = determineTagSearchPattern2, escapeRegExp2 = escapeRegExp3;
1585
+ const originalPrefix = versionPrefix || "";
1473
1586
  const tagSearchPattern = determineTagSearchPattern2(name, originalPrefix);
1474
- const escapedTagPattern = escapeRegExp(tagSearchPattern);
1587
+ const escapedTagPattern = escapeRegExp3(tagSearchPattern);
1588
+ if (!hasNoTags && pkgPath) {
1589
+ const packageDir = pkgPath || (0, import_node_process3.cwd)();
1590
+ const manifestResult = getVersionFromManifests(packageDir);
1591
+ if (manifestResult.manifestFound && manifestResult.version) {
1592
+ const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
1593
+ const tagVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1594
+ const packageVersion = manifestResult.version;
1595
+ if (import_semver3.default.gt(packageVersion, tagVersion)) {
1596
+ log(
1597
+ `Warning: Version mismatch detected!
1598
+ \u2022 ${manifestResult.manifestType} version: ${packageVersion}
1599
+ \u2022 Latest Git tag version: ${tagVersion} (from ${latestTag})
1600
+ \u2022 Package version is AHEAD of Git tags
1601
+
1602
+ This usually happens when:
1603
+ \u2022 A version was released but the tag wasn't pushed to the remote repository
1604
+ \u2022 The ${manifestResult.manifestType} was manually updated without creating a corresponding tag
1605
+ \u2022 You're running in CI and the latest tag isn't available yet
1606
+
1607
+ The tool will use the Git tag version (${tagVersion}) as the base for calculation.
1608
+ Expected next version will be based on ${tagVersion}, not ${packageVersion}.
1609
+
1610
+ To fix this mismatch:
1611
+ \u2022 Push missing tags: git push origin --tags
1612
+ \u2022 Or use package version as base by ensuring tags are up to date`,
1613
+ "warning"
1614
+ );
1615
+ } else if (import_semver3.default.gt(tagVersion, packageVersion)) {
1616
+ log(
1617
+ `Warning: Version mismatch detected!
1618
+ \u2022 ${manifestResult.manifestType} version: ${packageVersion}
1619
+ \u2022 Latest Git tag version: ${tagVersion} (from ${latestTag})
1620
+ \u2022 Git tag version is AHEAD of package version
1621
+
1622
+ This usually happens when:
1623
+ \u2022 A release was tagged but the ${manifestResult.manifestType} wasn't updated
1624
+ \u2022 You're on an older branch that hasn't been updated with the latest version
1625
+ \u2022 Automated release process created tags but didn't update manifest files
1626
+ \u2022 You pulled tags but not the corresponding commits that update the package version
1627
+
1628
+ The tool will use the Git tag version (${tagVersion}) as the base for calculation.
1629
+ This will likely result in a version that's already been released.
1630
+
1631
+ To fix this mismatch:
1632
+ \u2022 Update ${manifestResult.manifestType}: Set version to ${tagVersion} or higher
1633
+ \u2022 Or checkout the branch/commit that corresponds to the tag
1634
+ \u2022 Or ensure your branch is up to date with the latest changes`,
1635
+ "warning"
1636
+ );
1637
+ }
1638
+ }
1639
+ }
1475
1640
  const specifiedType = type;
1476
1641
  if (specifiedType) {
1477
1642
  if (hasNoTags) {
@@ -1483,9 +1648,9 @@ async function calculateVersion(config, options) {
1483
1648
  initialVersion
1484
1649
  );
1485
1650
  }
1486
- const cleanedTag = import_semver2.default.clean(latestTag) || latestTag;
1487
- const currentVersion = import_semver2.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1488
- if (STANDARD_BUMP_TYPES.includes(specifiedType) && (import_semver2.default.prerelease(currentVersion) || normalizedPrereleaseId)) {
1651
+ const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
1652
+ const currentVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1653
+ if (STANDARD_BUMP_TYPES.includes(specifiedType) && (import_semver3.default.prerelease(currentVersion) || normalizedPrereleaseId)) {
1489
1654
  log(
1490
1655
  normalizedPrereleaseId ? `Creating prerelease version with identifier '${normalizedPrereleaseId}' using ${specifiedType}` : `Cleaning prerelease identifier from ${currentVersion} for ${specifiedType} bump`,
1491
1656
  "debug"
@@ -1523,8 +1688,8 @@ async function calculateVersion(config, options) {
1523
1688
  initialVersion
1524
1689
  );
1525
1690
  }
1526
- const cleanedTag = import_semver2.default.clean(latestTag) || latestTag;
1527
- const currentVersion = import_semver2.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1691
+ const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
1692
+ const currentVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1528
1693
  log(`Applying ${branchVersionType} bump based on branch pattern`, "debug");
1529
1694
  return bumpVersion(currentVersion, branchVersionType, normalizedPrereleaseId);
1530
1695
  }
@@ -1562,7 +1727,7 @@ async function calculateVersion(config, options) {
1562
1727
  );
1563
1728
  return "";
1564
1729
  }
1565
- const currentVersion = import_semver2.default.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1730
+ const currentVersion = import_semver3.default.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1566
1731
  return bumpVersion(currentVersion, releaseTypeFromCommits, normalizedPrereleaseId);
1567
1732
  } catch (error) {
1568
1733
  log(`Failed to calculate version for ${name || "project"}`, "error");
@@ -1607,7 +1772,7 @@ function calculateNextVersion(version, manifestType, name, releaseType, prerelea
1607
1772
  `No tags found for ${name || "package"}, using ${manifestType} version: ${version} as base`,
1608
1773
  "info"
1609
1774
  );
1610
- if (STANDARD_BUMP_TYPES.includes(releaseType) && (import_semver2.default.prerelease(version) || prereleaseIdentifier)) {
1775
+ if (STANDARD_BUMP_TYPES.includes(releaseType) && (import_semver3.default.prerelease(version) || prereleaseIdentifier)) {
1611
1776
  log(
1612
1777
  prereleaseIdentifier ? `Creating prerelease version with identifier '${prereleaseIdentifier}' using ${releaseType}` : `Cleaning prerelease identifier from ${version} for ${releaseType} bump`,
1613
1778
  "debug"
@@ -1618,13 +1783,39 @@ function calculateNextVersion(version, manifestType, name, releaseType, prerelea
1618
1783
  return result || initialVersion;
1619
1784
  }
1620
1785
 
1786
+ // src/utils/packageMatching.ts
1787
+ function matchesPackageTarget(packageName, target) {
1788
+ if (packageName === target) {
1789
+ return true;
1790
+ }
1791
+ if (target.endsWith("/*")) {
1792
+ const scope = target.slice(0, -2);
1793
+ if (scope.startsWith("@")) {
1794
+ return packageName.startsWith(`${scope}/`);
1795
+ }
1796
+ return packageName.startsWith(`${scope}/`);
1797
+ }
1798
+ if (target === "*") {
1799
+ return true;
1800
+ }
1801
+ return false;
1802
+ }
1803
+ function shouldProcessPackage(packageName, targets = [], skip = []) {
1804
+ if (skip.includes(packageName)) {
1805
+ return false;
1806
+ }
1807
+ if (targets.length === 0) {
1808
+ return true;
1809
+ }
1810
+ return targets.some((target) => matchesPackageTarget(packageName, target));
1811
+ }
1812
+
1621
1813
  // src/package/packageProcessor.ts
1622
1814
  var PackageProcessor = class {
1623
1815
  skip;
1624
1816
  targets;
1625
1817
  versionPrefix;
1626
1818
  tagTemplate;
1627
- packageTagTemplate;
1628
1819
  commitMessageTemplate;
1629
1820
  dryRun;
1630
1821
  skipHooks;
@@ -1637,7 +1828,6 @@ var PackageProcessor = class {
1637
1828
  this.targets = options.targets || [];
1638
1829
  this.versionPrefix = options.versionPrefix || "v";
1639
1830
  this.tagTemplate = options.tagTemplate;
1640
- this.packageTagTemplate = options.packageTagTemplate;
1641
1831
  this.commitMessageTemplate = options.commitMessageTemplate || "";
1642
1832
  this.dryRun = options.dryRun || false;
1643
1833
  this.skipHooks = options.skipHooks || false;
@@ -1665,18 +1855,15 @@ var PackageProcessor = class {
1665
1855
  const pkgsToConsider = packages.filter((pkg) => {
1666
1856
  var _a2;
1667
1857
  const pkgName = pkg.packageJson.name;
1668
- if ((_a2 = this.skip) == null ? void 0 : _a2.includes(pkgName)) {
1669
- log(`Skipping package ${pkgName} as it's in the skip list.`, "info");
1670
- return false;
1671
- }
1672
- if (!this.targets || this.targets.length === 0) {
1673
- return true;
1674
- }
1675
- const isTargeted = this.targets.includes(pkgName);
1676
- if (!isTargeted) {
1677
- log(`Package ${pkgName} not in target list, skipping.`, "info");
1858
+ const shouldProcess = shouldProcessPackage(pkgName, this.targets, this.skip);
1859
+ if (!shouldProcess) {
1860
+ if ((_a2 = this.skip) == null ? void 0 : _a2.includes(pkgName)) {
1861
+ log(`Skipping package ${pkgName} as it's in the skip list.`, "info");
1862
+ } else {
1863
+ log(`Package ${pkgName} not in target list, skipping.`, "info");
1864
+ }
1678
1865
  }
1679
- return isTargeted;
1866
+ return shouldProcess;
1680
1867
  });
1681
1868
  log(`Found ${pkgsToConsider.length} targeted package(s) to process after filtering.`, "info");
1682
1869
  if (pkgsToConsider.length === 0) {
@@ -1689,7 +1876,10 @@ var PackageProcessor = class {
1689
1876
  const formattedPrefix = formatVersionPrefix(this.versionPrefix);
1690
1877
  let latestTagResult = "";
1691
1878
  try {
1692
- latestTagResult = await getLatestTagForPackage(name, this.versionPrefix);
1879
+ latestTagResult = await getLatestTagForPackage(name, this.versionPrefix, {
1880
+ tagTemplate: this.tagTemplate,
1881
+ packageSpecificTags: this.fullConfig.packageSpecificTags
1882
+ });
1693
1883
  } catch (error) {
1694
1884
  const errorMessage = error instanceof Error ? error.message : String(error);
1695
1885
  log(
@@ -1743,7 +1933,24 @@ var PackageProcessor = class {
1743
1933
  if (this.fullConfig.updateChangelog !== false) {
1744
1934
  let changelogEntries = [];
1745
1935
  try {
1746
- changelogEntries = extractChangelogEntriesFromCommits(pkgPath, latestTag);
1936
+ let revisionRange = latestTag;
1937
+ if (latestTag) {
1938
+ try {
1939
+ (0, import_node_child_process4.execSync)(`git rev-parse --verify "${latestTag}"`, {
1940
+ cwd: pkgPath,
1941
+ stdio: "ignore"
1942
+ });
1943
+ } catch {
1944
+ log(
1945
+ `Tag ${latestTag} doesn't exist, using recent commits from HEAD for changelog`,
1946
+ "debug"
1947
+ );
1948
+ revisionRange = "HEAD~10..HEAD";
1949
+ }
1950
+ } else {
1951
+ revisionRange = "HEAD~10..HEAD";
1952
+ }
1953
+ changelogEntries = extractChangelogEntriesFromCommits(pkgPath, revisionRange);
1747
1954
  if (changelogEntries.length === 0) {
1748
1955
  changelogEntries = [
1749
1956
  {
@@ -1821,7 +2028,7 @@ var PackageProcessor = class {
1821
2028
  this.versionPrefix,
1822
2029
  name,
1823
2030
  this.tagTemplate,
1824
- this.packageTagTemplate
2031
+ this.fullConfig.packageSpecificTags
1825
2032
  );
1826
2033
  const tagMessage = `chore(release): ${name} ${nextVersion}`;
1827
2034
  addTag(packageTag);
@@ -1907,16 +2114,9 @@ var PackageProcessor = class {
1907
2114
  };
1908
2115
 
1909
2116
  // src/core/versionStrategies.ts
1910
- function shouldProcessPackage(pkg, config, targets = []) {
1911
- var _a;
2117
+ function shouldProcessPackage2(pkg, config, targets = []) {
1912
2118
  const pkgName = pkg.packageJson.name;
1913
- if ((_a = config.skip) == null ? void 0 : _a.includes(pkgName)) {
1914
- return false;
1915
- }
1916
- if (!targets || targets.length === 0) {
1917
- return true;
1918
- }
1919
- return targets.includes(pkgName);
2119
+ return shouldProcessPackage(pkgName, targets, config.skip);
1920
2120
  }
1921
2121
  function createSyncedStrategy(config) {
1922
2122
  return async (packages) => {
@@ -1972,6 +2172,7 @@ function createSyncedStrategy(config) {
1972
2172
  }
1973
2173
  const files = [];
1974
2174
  const updatedPackages = [];
2175
+ const processedPaths = /* @__PURE__ */ new Set();
1975
2176
  try {
1976
2177
  if (packages.root) {
1977
2178
  const rootPkgPath = path7.join(packages.root, "package.json");
@@ -1979,6 +2180,7 @@ function createSyncedStrategy(config) {
1979
2180
  updatePackageVersion(rootPkgPath, nextVersion);
1980
2181
  files.push(rootPkgPath);
1981
2182
  updatedPackages.push("root");
2183
+ processedPaths.add(rootPkgPath);
1982
2184
  }
1983
2185
  } else {
1984
2186
  log("Root package path is undefined, skipping root package.json update", "warning");
@@ -1988,13 +2190,17 @@ function createSyncedStrategy(config) {
1988
2190
  log(`Failed to update root package.json: ${errMessage}`, "error");
1989
2191
  }
1990
2192
  for (const pkg of packages.packages) {
1991
- if (!shouldProcessPackage(pkg, config)) {
2193
+ if (!shouldProcessPackage2(pkg, config)) {
1992
2194
  continue;
1993
2195
  }
1994
2196
  const packageJsonPath = path7.join(pkg.dir, "package.json");
2197
+ if (processedPaths.has(packageJsonPath)) {
2198
+ continue;
2199
+ }
1995
2200
  updatePackageVersion(packageJsonPath, nextVersion);
1996
2201
  files.push(packageJsonPath);
1997
2202
  updatedPackages.push(pkg.packageJson.name);
2203
+ processedPaths.add(packageJsonPath);
1998
2204
  }
1999
2205
  if (updatedPackages.length > 0) {
2000
2206
  log(`Updated ${updatedPackages.length} package(s) to version ${nextVersion}`, "success");
@@ -2002,8 +2208,25 @@ function createSyncedStrategy(config) {
2002
2208
  log("No packages were updated", "warning");
2003
2209
  return;
2004
2210
  }
2005
- const nextTag = formatTag(nextVersion, formattedPrefix, null, tagTemplate);
2006
- const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
2211
+ let tagPackageName = null;
2212
+ let commitPackageName = void 0;
2213
+ if (config.packageSpecificTags && packages.packages.length === 1) {
2214
+ tagPackageName = packages.packages[0].packageJson.name;
2215
+ commitPackageName = packages.packages[0].packageJson.name;
2216
+ }
2217
+ const nextTag = formatTag(
2218
+ nextVersion,
2219
+ formattedPrefix,
2220
+ tagPackageName,
2221
+ tagTemplate,
2222
+ config.packageSpecificTags || false
2223
+ );
2224
+ const formattedCommitMessage = formatCommitMessage(
2225
+ commitMessage,
2226
+ nextVersion,
2227
+ commitPackageName,
2228
+ void 0
2229
+ );
2007
2230
  await createGitCommitAndTag(files, nextTag, formattedCommitMessage, skipHooks, dryRun);
2008
2231
  } catch (error) {
2009
2232
  if (error instanceof VersionError || error instanceof GitError) {
@@ -2024,7 +2247,6 @@ function createSingleStrategy(config) {
2024
2247
  mainPackage,
2025
2248
  versionPrefix,
2026
2249
  tagTemplate,
2027
- packageTagTemplate,
2028
2250
  commitMessage = "chore(release): ${version}",
2029
2251
  dryRun,
2030
2252
  skipHooks
@@ -2046,7 +2268,10 @@ function createSingleStrategy(config) {
2046
2268
  }
2047
2269
  const pkgPath = pkg.dir;
2048
2270
  const formattedPrefix = formatVersionPrefix(versionPrefix || "v");
2049
- let latestTagResult = await getLatestTagForPackage(packageName, formattedPrefix);
2271
+ let latestTagResult = await getLatestTagForPackage(packageName, formattedPrefix, {
2272
+ tagTemplate,
2273
+ packageSpecificTags: config.packageSpecificTags
2274
+ });
2050
2275
  if (!latestTagResult) {
2051
2276
  const globalTagResult = await getLatestTag();
2052
2277
  latestTagResult = globalTagResult || "";
@@ -2077,7 +2302,7 @@ function createSingleStrategy(config) {
2077
2302
  formattedPrefix,
2078
2303
  packageName,
2079
2304
  tagTemplate,
2080
- packageTagTemplate
2305
+ config.packageSpecificTags
2081
2306
  );
2082
2307
  const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion, packageName);
2083
2308
  await createGitCommitAndTag(
@@ -2110,7 +2335,6 @@ function createAsyncStrategy(config) {
2110
2335
  targets: config.packages || [],
2111
2336
  versionPrefix: config.versionPrefix || "v",
2112
2337
  tagTemplate: config.tagTemplate,
2113
- packageTagTemplate: config.packageTagTemplate,
2114
2338
  commitMessageTemplate: config.commitMessage || "",
2115
2339
  dryRun: config.dryRun || false,
2116
2340
  skipHooks: config.skipHooks || false,