@releasekit/version 0.2.0-next.7 → 0.2.0-next.9

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.
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  BaseVersionError
3
3
  } from "./chunk-GQLJ7JQY.js";
4
+ import {
5
+ execAsync,
6
+ execSync
7
+ } from "./chunk-LMPZV35Z.js";
4
8
 
5
9
  // src/config.ts
6
10
  import { loadVersionConfig } from "@releasekit/config";
@@ -202,24 +206,6 @@ import semver3 from "semver";
202
206
  // src/git/repository.ts
203
207
  import { existsSync, statSync } from "fs";
204
208
  import { join } from "path";
205
-
206
- // src/git/commandExecutor.ts
207
- import { execFile, execFileSync } from "child_process";
208
- var execAsync = (file, args, options) => {
209
- const defaultOptions = { maxBuffer: 1024 * 1024 * 10, ...options };
210
- return new Promise((resolve2, reject) => {
211
- execFile(file, args, defaultOptions, (error, stdout, stderr) => {
212
- if (error) {
213
- reject(error);
214
- } else {
215
- resolve2({ stdout: stdout.toString(), stderr: stderr.toString() });
216
- }
217
- });
218
- });
219
- };
220
- var execSync = (file, args, options) => execFileSync(file, args, { maxBuffer: 1024 * 1024 * 10, ...options });
221
-
222
- // src/git/repository.ts
223
209
  function isGitRepository(directory) {
224
210
  const gitDir = join(directory, ".git");
225
211
  if (!existsSync(gitDir)) {
@@ -374,28 +360,37 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
374
360
  `Looking for tags for package ${packageName} with prefix ${versionPrefix || "none"}, packageSpecificTags: ${packageSpecificTags}`,
375
361
  "debug"
376
362
  );
377
- const allTags = await getSemverTags({
378
- tagPrefix: versionPrefix
379
- });
380
- log(`Retrieved ${allTags.length} tags: ${allTags.join(", ")}`, "debug");
363
+ let allTags = [];
364
+ try {
365
+ const { execSync: execSync2 } = await import("./commandExecutor-E44ID5U4.js");
366
+ const tagsOutput = execSync2("git", ["tag", "-l"], { cwd: process.cwd() });
367
+ allTags = tagsOutput.toString().trim().split("\n").filter((tag) => tag.length > 0);
368
+ } catch (err) {
369
+ log(`Error getting tags: ${err instanceof Error ? err.message : String(err)}`, "error");
370
+ }
371
+ log(`Retrieved ${allTags.length} tags`, "debug");
381
372
  if (packageSpecificTags) {
382
373
  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.-]+)?)");
383
374
  log(`Using package tag pattern: ${packageTagPattern}`, "debug");
384
375
  const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
385
376
  let packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
377
+ log(`Found ${packageTags.length} matching tags for ${packageName}`, "debug");
386
378
  if (packageTags.length > 0) {
387
379
  const chronologicalFirst = packageTags[0];
380
+ void chronologicalFirst;
388
381
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
389
382
  let versionA = "";
390
383
  let versionB = "";
391
384
  if (a.includes("@")) {
392
- const afterAt = a.split("@")[1] || "";
385
+ const parts = a.split("@");
386
+ const afterAt = parts[parts.length - 1] || "";
393
387
  versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
394
388
  } else {
395
389
  versionA = a.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
396
390
  }
397
391
  if (b.includes("@")) {
398
- const afterAtB = b.split("@")[1] || "";
392
+ const parts = b.split("@");
393
+ const afterAtB = parts[parts.length - 1] || "";
399
394
  versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
400
395
  } else {
401
396
  versionB = b.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
@@ -406,12 +401,6 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
406
401
  });
407
402
  log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
408
403
  log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
409
- if (sortedPackageTags2[0] !== chronologicalFirst) {
410
- log(
411
- `Package tag ordering differs: chronological first is ${chronologicalFirst}, semantic latest is ${sortedPackageTags2[0]}`,
412
- "debug"
413
- );
414
- }
415
404
  return sortedPackageTags2[0];
416
405
  }
417
406
  if (versionPrefix) {
@@ -419,9 +408,11 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
419
408
  packageTags = allTags.filter((tag) => pattern1.test(tag));
420
409
  if (packageTags.length > 0) {
421
410
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
422
- const afterAt = a.split("@")[1] || "";
411
+ const aParts = a.split("@");
412
+ const afterAt = aParts[aParts.length - 1] || "";
423
413
  const versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
424
- const afterAtB = b.split("@")[1] || "";
414
+ const bParts = b.split("@");
415
+ const afterAtB = bParts[bParts.length - 1] || "";
425
416
  const versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
426
417
  const cleanVersionA = semver.clean(versionA) || "0.0.0";
427
418
  const cleanVersionB = semver.clean(versionB) || "0.0.0";
@@ -437,8 +428,10 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
437
428
  packageTags = allTags.filter((tag) => pattern2.test(tag));
438
429
  if (packageTags.length > 0) {
439
430
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
440
- const versionA = semver.clean(a.split("@")[1] || "") || "0.0.0";
441
- const versionB = semver.clean(b.split("@")[1] || "") || "0.0.0";
431
+ const aParts = a.split("@");
432
+ const versionA = semver.clean(aParts[aParts.length - 1] || "") || "0.0.0";
433
+ const bParts = b.split("@");
434
+ const versionB = semver.clean(bParts[bParts.length - 1] || "") || "0.0.0";
442
435
  return semver.rcompare(versionA, versionB);
443
436
  });
444
437
  log(`Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`, "debug");
@@ -458,8 +451,10 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
458
451
  return "";
459
452
  }
460
453
  const sortedPackageTags = [...packageTags].sort((a, b) => {
461
- const versionA = semver.clean(a.split("@")[1] || "") || "0.0.0";
462
- const versionB = semver.clean(b.split("@")[1] || "") || "0.0.0";
454
+ const aParts = a.split("@");
455
+ const versionA = semver.clean(aParts[aParts.length - 1] || "") || "0.0.0";
456
+ const bParts = b.split("@");
457
+ const versionB = semver.clean(bParts[bParts.length - 1] || "") || "0.0.0";
463
458
  return semver.rcompare(versionA, versionB);
464
459
  });
465
460
  log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
@@ -698,12 +693,17 @@ var VersionMismatchError = class extends Error {
698
693
  this.name = "VersionMismatchError";
699
694
  }
700
695
  };
701
- async function getBestVersionSource(tagName, packageVersion, cwd4, mismatchStrategy = "error") {
696
+ async function getBestVersionSource(tagName, packageVersion, cwd4, mismatchStrategy = "error", strictReachable = false) {
702
697
  if (!tagName?.trim()) {
703
698
  return packageVersion ? { source: "package", version: packageVersion, reason: "No git tag provided" } : { source: "initial", version: "0.1.0", reason: "No git tag or package version available" };
704
699
  }
705
700
  const verification = verifyTag(tagName, cwd4);
706
701
  if (!verification.exists || !verification.reachable) {
702
+ if (strictReachable) {
703
+ throw new Error(
704
+ `Git tag '${tagName}' is not reachable from the current commit. The tag exists but cannot be reached from HEAD, which usually means you're on a different branch or the tag is orphaned. To allow fallback to package version, set strictReachable to false in your configuration.`
705
+ );
706
+ }
707
707
  if (packageVersion) {
708
708
  log(
709
709
  `Git tag '${tagName}' unreachable (${verification.error}), using package version: ${packageVersion}`,
@@ -809,7 +809,8 @@ async function calculateVersion(config, options) {
809
809
  prereleaseIdentifier: configPrereleaseIdentifier,
810
810
  branchPattern,
811
811
  baseBranch,
812
- mismatchStrategy
812
+ mismatchStrategy,
813
+ strictReachable
813
814
  } = config;
814
815
  const {
815
816
  latestTag,
@@ -854,7 +855,13 @@ async function calculateVersion(config, options) {
854
855
  const packageDir = pkgPath || cwd();
855
856
  const manifestResult = getVersionFromManifests(packageDir);
856
857
  const packageVersion = manifestResult.manifestFound && manifestResult.version ? manifestResult.version : void 0;
857
- versionSource = await getBestVersionSource(latestTag, packageVersion, packageDir, mismatchStrategy);
858
+ versionSource = await getBestVersionSource(
859
+ latestTag,
860
+ packageVersion,
861
+ packageDir,
862
+ mismatchStrategy,
863
+ strictReachable
864
+ );
858
865
  log(`Using version source: ${versionSource.source} (${versionSource.reason})`, "info");
859
866
  }
860
867
  const specifiedType = type;
@@ -1396,6 +1403,11 @@ var PackageProcessor = class {
1396
1403
  if (verification.exists && verification.reachable) {
1397
1404
  revisionRange = `${latestTag}..HEAD`;
1398
1405
  } else {
1406
+ if (this.config.strictReachable) {
1407
+ throw new Error(
1408
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1409
+ );
1410
+ }
1399
1411
  log(`Tag ${latestTag} is unreachable (${verification.error}), using all commits for changelog`, "debug");
1400
1412
  revisionRange = "HEAD";
1401
1413
  }
@@ -1713,6 +1725,11 @@ function createSyncStrategy(config) {
1713
1725
  });
1714
1726
  revisionRange = `${latestTag}..HEAD`;
1715
1727
  } catch {
1728
+ if (config.strictReachable) {
1729
+ throw new Error(
1730
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1731
+ );
1732
+ }
1716
1733
  log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
1717
1734
  revisionRange = "HEAD";
1718
1735
  }
@@ -1832,6 +1849,11 @@ function createSingleStrategy(config) {
1832
1849
  });
1833
1850
  revisionRange = `${latestTag}..HEAD`;
1834
1851
  } catch {
1852
+ if (config.strictReachable) {
1853
+ throw new Error(
1854
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1855
+ );
1856
+ }
1835
1857
  log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
1836
1858
  revisionRange = "HEAD";
1837
1859
  }
@@ -0,0 +1,20 @@
1
+ // src/git/commandExecutor.ts
2
+ import { execFile, execFileSync } from "child_process";
3
+ var execAsync = (file, args, options) => {
4
+ const defaultOptions = { maxBuffer: 1024 * 1024 * 10, ...options };
5
+ return new Promise((resolve, reject) => {
6
+ execFile(file, args, defaultOptions, (error, stdout, stderr) => {
7
+ if (error) {
8
+ reject(error);
9
+ } else {
10
+ resolve({ stdout: stdout.toString(), stderr: stderr.toString() });
11
+ }
12
+ });
13
+ });
14
+ };
15
+ var execSync = (file, args, options) => execFileSync(file, args, { maxBuffer: 1024 * 1024 * 10, ...options });
16
+
17
+ export {
18
+ execAsync,
19
+ execSync
20
+ };
package/dist/cli.cjs CHANGED
@@ -57,6 +57,33 @@ var init_baseError = __esm({
57
57
  }
58
58
  });
59
59
 
60
+ // src/git/commandExecutor.ts
61
+ var commandExecutor_exports = {};
62
+ __export(commandExecutor_exports, {
63
+ execAsync: () => execAsync,
64
+ execSync: () => execSync
65
+ });
66
+ var import_node_child_process, execAsync, execSync;
67
+ var init_commandExecutor = __esm({
68
+ "src/git/commandExecutor.ts"() {
69
+ "use strict";
70
+ import_node_child_process = require("child_process");
71
+ execAsync = (file, args, options) => {
72
+ const defaultOptions = { maxBuffer: 1024 * 1024 * 10, ...options };
73
+ return new Promise((resolve2, reject) => {
74
+ (0, import_node_child_process.execFile)(file, args, defaultOptions, (error, stdout, stderr) => {
75
+ if (error) {
76
+ reject(error);
77
+ } else {
78
+ resolve2({ stdout: stdout.toString(), stderr: stderr.toString() });
79
+ }
80
+ });
81
+ });
82
+ };
83
+ execSync = (file, args, options) => (0, import_node_child_process.execFileSync)(file, args, { maxBuffer: 1024 * 1024 * 10, ...options });
84
+ }
85
+ });
86
+
60
87
  // src/cli.ts
61
88
  var cli_exports = {};
62
89
  __export(cli_exports, {
@@ -370,23 +397,8 @@ function matchesPackageNamePattern(packageName, pattern) {
370
397
  var import_node_fs6 = __toESM(require("fs"), 1);
371
398
  var path6 = __toESM(require("path"), 1);
372
399
 
373
- // src/git/commandExecutor.ts
374
- var import_node_child_process = require("child_process");
375
- var execAsync = (file, args, options) => {
376
- const defaultOptions = { maxBuffer: 1024 * 1024 * 10, ...options };
377
- return new Promise((resolve2, reject) => {
378
- (0, import_node_child_process.execFile)(file, args, defaultOptions, (error, stdout, stderr) => {
379
- if (error) {
380
- reject(error);
381
- } else {
382
- resolve2({ stdout: stdout.toString(), stderr: stderr.toString() });
383
- }
384
- });
385
- });
386
- };
387
- var execSync = (file, args, options) => (0, import_node_child_process.execFileSync)(file, args, { maxBuffer: 1024 * 1024 * 10, ...options });
388
-
389
400
  // src/changelog/commitParser.ts
401
+ init_commandExecutor();
390
402
  var CONVENTIONAL_COMMIT_REGEX = /^(\w+)(?:\(([^)]+)\))?(!)?: (.+)(?:\n\n([\s\S]*))?/;
391
403
  var BREAKING_CHANGE_REGEX = /BREAKING CHANGE: ([\s\S]+?)(?:\n\n|$)/;
392
404
  function extractChangelogEntriesFromCommits(projectDir, revisionRange) {
@@ -490,13 +502,16 @@ function extractIssueIds(body) {
490
502
 
491
503
  // src/core/versionStrategies.ts
492
504
  init_baseError();
505
+ init_commandExecutor();
493
506
 
494
507
  // src/git/commands.ts
495
508
  var import_node_process = require("process");
509
+ init_commandExecutor();
496
510
 
497
511
  // src/git/repository.ts
498
512
  var import_node_fs = require("fs");
499
513
  var import_node_path2 = require("path");
514
+ init_commandExecutor();
500
515
  function isGitRepository(directory) {
501
516
  const gitDir = (0, import_node_path2.join)(directory, ".git");
502
517
  if (!(0, import_node_fs.existsSync)(gitDir)) {
@@ -701,6 +716,7 @@ To fix this:
701
716
  }
702
717
 
703
718
  // src/git/tagsAndBranches.ts
719
+ init_commandExecutor();
704
720
  function getCommitsLength(pkgRoot, sinceTag) {
705
721
  try {
706
722
  let amount;
@@ -778,28 +794,37 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
778
794
  `Looking for tags for package ${packageName} with prefix ${versionPrefix || "none"}, packageSpecificTags: ${packageSpecificTags}`,
779
795
  "debug"
780
796
  );
781
- const allTags = await (0, import_git_semver_tags.getSemverTags)({
782
- tagPrefix: versionPrefix
783
- });
784
- log(`Retrieved ${allTags.length} tags: ${allTags.join(", ")}`, "debug");
797
+ let allTags = [];
798
+ try {
799
+ const { execSync: execSync2 } = await Promise.resolve().then(() => (init_commandExecutor(), commandExecutor_exports));
800
+ const tagsOutput = execSync2("git", ["tag", "-l"], { cwd: process.cwd() });
801
+ allTags = tagsOutput.toString().trim().split("\n").filter((tag) => tag.length > 0);
802
+ } catch (err) {
803
+ log(`Error getting tags: ${err instanceof Error ? err.message : String(err)}`, "error");
804
+ }
805
+ log(`Retrieved ${allTags.length} tags`, "debug");
785
806
  if (packageSpecificTags) {
786
807
  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.-]+)?)");
787
808
  log(`Using package tag pattern: ${packageTagPattern}`, "debug");
788
809
  const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
789
810
  let packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
811
+ log(`Found ${packageTags.length} matching tags for ${packageName}`, "debug");
790
812
  if (packageTags.length > 0) {
791
813
  const chronologicalFirst = packageTags[0];
814
+ void chronologicalFirst;
792
815
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
793
816
  let versionA = "";
794
817
  let versionB = "";
795
818
  if (a.includes("@")) {
796
- const afterAt = a.split("@")[1] || "";
819
+ const parts = a.split("@");
820
+ const afterAt = parts[parts.length - 1] || "";
797
821
  versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
798
822
  } else {
799
823
  versionA = a.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
800
824
  }
801
825
  if (b.includes("@")) {
802
- const afterAtB = b.split("@")[1] || "";
826
+ const parts = b.split("@");
827
+ const afterAtB = parts[parts.length - 1] || "";
803
828
  versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
804
829
  } else {
805
830
  versionB = b.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
@@ -810,12 +835,6 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
810
835
  });
811
836
  log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
812
837
  log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
813
- if (sortedPackageTags2[0] !== chronologicalFirst) {
814
- log(
815
- `Package tag ordering differs: chronological first is ${chronologicalFirst}, semantic latest is ${sortedPackageTags2[0]}`,
816
- "debug"
817
- );
818
- }
819
838
  return sortedPackageTags2[0];
820
839
  }
821
840
  if (versionPrefix) {
@@ -823,9 +842,11 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
823
842
  packageTags = allTags.filter((tag) => pattern1.test(tag));
824
843
  if (packageTags.length > 0) {
825
844
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
826
- const afterAt = a.split("@")[1] || "";
845
+ const aParts = a.split("@");
846
+ const afterAt = aParts[aParts.length - 1] || "";
827
847
  const versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
828
- const afterAtB = b.split("@")[1] || "";
848
+ const bParts = b.split("@");
849
+ const afterAtB = bParts[bParts.length - 1] || "";
829
850
  const versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
830
851
  const cleanVersionA = import_semver.default.clean(versionA) || "0.0.0";
831
852
  const cleanVersionB = import_semver.default.clean(versionB) || "0.0.0";
@@ -841,8 +862,10 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
841
862
  packageTags = allTags.filter((tag) => pattern2.test(tag));
842
863
  if (packageTags.length > 0) {
843
864
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
844
- const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
845
- const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
865
+ const aParts = a.split("@");
866
+ const versionA = import_semver.default.clean(aParts[aParts.length - 1] || "") || "0.0.0";
867
+ const bParts = b.split("@");
868
+ const versionB = import_semver.default.clean(bParts[bParts.length - 1] || "") || "0.0.0";
846
869
  return import_semver.default.rcompare(versionA, versionB);
847
870
  });
848
871
  log(`Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`, "debug");
@@ -862,8 +885,10 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
862
885
  return "";
863
886
  }
864
887
  const sortedPackageTags = [...packageTags].sort((a, b) => {
865
- const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
866
- const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
888
+ const aParts = a.split("@");
889
+ const versionA = import_semver.default.clean(aParts[aParts.length - 1] || "") || "0.0.0";
890
+ const bParts = b.split("@");
891
+ const versionB = import_semver.default.clean(bParts[bParts.length - 1] || "") || "0.0.0";
867
892
  return import_semver.default.rcompare(versionA, versionB);
868
893
  });
869
894
  log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
@@ -1040,6 +1065,7 @@ var import_config3 = require("@releasekit/config");
1040
1065
  var import_semver2 = __toESM(require("semver"), 1);
1041
1066
 
1042
1067
  // src/git/tagVerification.ts
1068
+ init_commandExecutor();
1043
1069
  function verifyTag(tagName, cwd4) {
1044
1070
  if (!tagName || tagName.trim() === "") {
1045
1071
  return { exists: false, reachable: false, error: "Empty tag name" };
@@ -1143,12 +1169,17 @@ var VersionMismatchError = class extends Error {
1143
1169
  this.name = "VersionMismatchError";
1144
1170
  }
1145
1171
  };
1146
- async function getBestVersionSource(tagName, packageVersion, cwd4, mismatchStrategy = "error") {
1172
+ async function getBestVersionSource(tagName, packageVersion, cwd4, mismatchStrategy = "error", strictReachable = false) {
1147
1173
  if (!tagName?.trim()) {
1148
1174
  return packageVersion ? { source: "package", version: packageVersion, reason: "No git tag provided" } : { source: "initial", version: "0.1.0", reason: "No git tag or package version available" };
1149
1175
  }
1150
1176
  const verification = verifyTag(tagName, cwd4);
1151
1177
  if (!verification.exists || !verification.reachable) {
1178
+ if (strictReachable) {
1179
+ throw new Error(
1180
+ `Git tag '${tagName}' is not reachable from the current commit. The tag exists but cannot be reached from HEAD, which usually means you're on a different branch or the tag is orphaned. To allow fallback to package version, set strictReachable to false in your configuration.`
1181
+ );
1182
+ }
1152
1183
  if (packageVersion) {
1153
1184
  log(
1154
1185
  `Git tag '${tagName}' unreachable (${verification.error}), using package version: ${packageVersion}`,
@@ -1254,7 +1285,8 @@ async function calculateVersion(config, options) {
1254
1285
  prereleaseIdentifier: configPrereleaseIdentifier,
1255
1286
  branchPattern,
1256
1287
  baseBranch,
1257
- mismatchStrategy
1288
+ mismatchStrategy,
1289
+ strictReachable
1258
1290
  } = config;
1259
1291
  const {
1260
1292
  latestTag,
@@ -1299,7 +1331,13 @@ async function calculateVersion(config, options) {
1299
1331
  const packageDir = pkgPath || (0, import_node_process2.cwd)();
1300
1332
  const manifestResult = getVersionFromManifests(packageDir);
1301
1333
  const packageVersion = manifestResult.manifestFound && manifestResult.version ? manifestResult.version : void 0;
1302
- versionSource = await getBestVersionSource(latestTag, packageVersion, packageDir, mismatchStrategy);
1334
+ versionSource = await getBestVersionSource(
1335
+ latestTag,
1336
+ packageVersion,
1337
+ packageDir,
1338
+ mismatchStrategy,
1339
+ strictReachable
1340
+ );
1303
1341
  log(`Using version source: ${versionSource.source} (${versionSource.reason})`, "info");
1304
1342
  }
1305
1343
  const specifiedType = type;
@@ -1541,6 +1579,11 @@ var PackageProcessor = class {
1541
1579
  if (verification.exists && verification.reachable) {
1542
1580
  revisionRange = `${latestTag}..HEAD`;
1543
1581
  } else {
1582
+ if (this.config.strictReachable) {
1583
+ throw new Error(
1584
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1585
+ );
1586
+ }
1544
1587
  log(`Tag ${latestTag} is unreachable (${verification.error}), using all commits for changelog`, "debug");
1545
1588
  revisionRange = "HEAD";
1546
1589
  }
@@ -1856,6 +1899,11 @@ function createSyncStrategy(config) {
1856
1899
  });
1857
1900
  revisionRange = `${latestTag}..HEAD`;
1858
1901
  } catch {
1902
+ if (config.strictReachable) {
1903
+ throw new Error(
1904
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1905
+ );
1906
+ }
1859
1907
  log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
1860
1908
  revisionRange = "HEAD";
1861
1909
  }
@@ -1975,6 +2023,11 @@ function createSingleStrategy(config) {
1975
2023
  });
1976
2024
  revisionRange = `${latestTag}..HEAD`;
1977
2025
  } catch {
2026
+ if (config.strictReachable) {
2027
+ throw new Error(
2028
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
2029
+ );
2030
+ }
1978
2031
  log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
1979
2032
  revisionRange = "HEAD";
1980
2033
  }
package/dist/cli.js CHANGED
@@ -5,8 +5,9 @@ import {
5
5
  loadConfig,
6
6
  log,
7
7
  printJsonOutput
8
- } from "./chunk-OQ5Z3KXR.js";
8
+ } from "./chunk-7F6RMN2K.js";
9
9
  import "./chunk-GQLJ7JQY.js";
10
+ import "./chunk-LMPZV35Z.js";
10
11
 
11
12
  // src/cli.ts
12
13
  import * as fs from "fs";
@@ -0,0 +1,8 @@
1
+ import {
2
+ execAsync,
3
+ execSync
4
+ } from "./chunk-LMPZV35Z.js";
5
+ export {
6
+ execAsync,
7
+ execSync
8
+ };
package/dist/index.cjs CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,6 +30,33 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/git/commandExecutor.ts
34
+ var commandExecutor_exports = {};
35
+ __export(commandExecutor_exports, {
36
+ execAsync: () => execAsync,
37
+ execSync: () => execSync
38
+ });
39
+ var import_node_child_process, execAsync, execSync;
40
+ var init_commandExecutor = __esm({
41
+ "src/git/commandExecutor.ts"() {
42
+ "use strict";
43
+ import_node_child_process = require("child_process");
44
+ execAsync = (file, args, options) => {
45
+ const defaultOptions = { maxBuffer: 1024 * 1024 * 10, ...options };
46
+ return new Promise((resolve2, reject) => {
47
+ (0, import_node_child_process.execFile)(file, args, defaultOptions, (error, stdout, stderr) => {
48
+ if (error) {
49
+ reject(error);
50
+ } else {
51
+ resolve2({ stdout: stdout.toString(), stderr: stderr.toString() });
52
+ }
53
+ });
54
+ });
55
+ };
56
+ execSync = (file, args, options) => (0, import_node_child_process.execFileSync)(file, args, { maxBuffer: 1024 * 1024 * 10, ...options });
57
+ }
58
+ });
59
+
30
60
  // src/index.ts
31
61
  var index_exports = {};
32
62
  __export(index_exports, {
@@ -100,24 +130,7 @@ var import_semver3 = __toESM(require("semver"), 1);
100
130
  // src/git/repository.ts
101
131
  var import_node_fs = require("fs");
102
132
  var import_node_path = require("path");
103
-
104
- // src/git/commandExecutor.ts
105
- var import_node_child_process = require("child_process");
106
- var execAsync = (file, args, options) => {
107
- const defaultOptions = { maxBuffer: 1024 * 1024 * 10, ...options };
108
- return new Promise((resolve2, reject) => {
109
- (0, import_node_child_process.execFile)(file, args, defaultOptions, (error, stdout, stderr) => {
110
- if (error) {
111
- reject(error);
112
- } else {
113
- resolve2({ stdout: stdout.toString(), stderr: stderr.toString() });
114
- }
115
- });
116
- });
117
- };
118
- var execSync = (file, args, options) => (0, import_node_child_process.execFileSync)(file, args, { maxBuffer: 1024 * 1024 * 10, ...options });
119
-
120
- // src/git/repository.ts
133
+ init_commandExecutor();
121
134
  function isGitRepository(directory) {
122
135
  const gitDir = (0, import_node_path.join)(directory, ".git");
123
136
  if (!(0, import_node_fs.existsSync)(gitDir)) {
@@ -280,6 +293,7 @@ To fix this:
280
293
  }
281
294
 
282
295
  // src/git/tagsAndBranches.ts
296
+ init_commandExecutor();
283
297
  function getCommitsLength(pkgRoot, sinceTag) {
284
298
  try {
285
299
  let amount;
@@ -357,28 +371,37 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
357
371
  `Looking for tags for package ${packageName} with prefix ${versionPrefix || "none"}, packageSpecificTags: ${packageSpecificTags}`,
358
372
  "debug"
359
373
  );
360
- const allTags = await (0, import_git_semver_tags.getSemverTags)({
361
- tagPrefix: versionPrefix
362
- });
363
- log(`Retrieved ${allTags.length} tags: ${allTags.join(", ")}`, "debug");
374
+ let allTags = [];
375
+ try {
376
+ const { execSync: execSync2 } = await Promise.resolve().then(() => (init_commandExecutor(), commandExecutor_exports));
377
+ const tagsOutput = execSync2("git", ["tag", "-l"], { cwd: process.cwd() });
378
+ allTags = tagsOutput.toString().trim().split("\n").filter((tag) => tag.length > 0);
379
+ } catch (err) {
380
+ log(`Error getting tags: ${err instanceof Error ? err.message : String(err)}`, "error");
381
+ }
382
+ log(`Retrieved ${allTags.length} tags`, "debug");
364
383
  if (packageSpecificTags) {
365
384
  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.-]+)?)");
366
385
  log(`Using package tag pattern: ${packageTagPattern}`, "debug");
367
386
  const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
368
387
  let packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
388
+ log(`Found ${packageTags.length} matching tags for ${packageName}`, "debug");
369
389
  if (packageTags.length > 0) {
370
390
  const chronologicalFirst = packageTags[0];
391
+ void chronologicalFirst;
371
392
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
372
393
  let versionA = "";
373
394
  let versionB = "";
374
395
  if (a.includes("@")) {
375
- const afterAt = a.split("@")[1] || "";
396
+ const parts = a.split("@");
397
+ const afterAt = parts[parts.length - 1] || "";
376
398
  versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
377
399
  } else {
378
400
  versionA = a.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
379
401
  }
380
402
  if (b.includes("@")) {
381
- const afterAtB = b.split("@")[1] || "";
403
+ const parts = b.split("@");
404
+ const afterAtB = parts[parts.length - 1] || "";
382
405
  versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
383
406
  } else {
384
407
  versionB = b.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
@@ -389,12 +412,6 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
389
412
  });
390
413
  log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
391
414
  log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
392
- if (sortedPackageTags2[0] !== chronologicalFirst) {
393
- log(
394
- `Package tag ordering differs: chronological first is ${chronologicalFirst}, semantic latest is ${sortedPackageTags2[0]}`,
395
- "debug"
396
- );
397
- }
398
415
  return sortedPackageTags2[0];
399
416
  }
400
417
  if (versionPrefix) {
@@ -402,9 +419,11 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
402
419
  packageTags = allTags.filter((tag) => pattern1.test(tag));
403
420
  if (packageTags.length > 0) {
404
421
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
405
- const afterAt = a.split("@")[1] || "";
422
+ const aParts = a.split("@");
423
+ const afterAt = aParts[aParts.length - 1] || "";
406
424
  const versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
407
- const afterAtB = b.split("@")[1] || "";
425
+ const bParts = b.split("@");
426
+ const afterAtB = bParts[bParts.length - 1] || "";
408
427
  const versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
409
428
  const cleanVersionA = import_semver.default.clean(versionA) || "0.0.0";
410
429
  const cleanVersionB = import_semver.default.clean(versionB) || "0.0.0";
@@ -420,8 +439,10 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
420
439
  packageTags = allTags.filter((tag) => pattern2.test(tag));
421
440
  if (packageTags.length > 0) {
422
441
  const sortedPackageTags2 = [...packageTags].sort((a, b) => {
423
- const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
424
- const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
442
+ const aParts = a.split("@");
443
+ const versionA = import_semver.default.clean(aParts[aParts.length - 1] || "") || "0.0.0";
444
+ const bParts = b.split("@");
445
+ const versionB = import_semver.default.clean(bParts[bParts.length - 1] || "") || "0.0.0";
425
446
  return import_semver.default.rcompare(versionA, versionB);
426
447
  });
427
448
  log(`Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`, "debug");
@@ -441,8 +462,10 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
441
462
  return "";
442
463
  }
443
464
  const sortedPackageTags = [...packageTags].sort((a, b) => {
444
- const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
445
- const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
465
+ const aParts = a.split("@");
466
+ const versionA = import_semver.default.clean(aParts[aParts.length - 1] || "") || "0.0.0";
467
+ const bParts = b.split("@");
468
+ const versionB = import_semver.default.clean(bParts[bParts.length - 1] || "") || "0.0.0";
446
469
  return import_semver.default.rcompare(versionA, versionB);
447
470
  });
448
471
  log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
@@ -578,6 +601,7 @@ var import_config3 = require("@releasekit/config");
578
601
  var import_semver2 = __toESM(require("semver"), 1);
579
602
 
580
603
  // src/git/tagVerification.ts
604
+ init_commandExecutor();
581
605
  function verifyTag(tagName, cwd4) {
582
606
  if (!tagName || tagName.trim() === "") {
583
607
  return { exists: false, reachable: false, error: "Empty tag name" };
@@ -681,12 +705,17 @@ var VersionMismatchError = class extends Error {
681
705
  this.name = "VersionMismatchError";
682
706
  }
683
707
  };
684
- async function getBestVersionSource(tagName, packageVersion, cwd4, mismatchStrategy = "error") {
708
+ async function getBestVersionSource(tagName, packageVersion, cwd4, mismatchStrategy = "error", strictReachable = false) {
685
709
  if (!tagName?.trim()) {
686
710
  return packageVersion ? { source: "package", version: packageVersion, reason: "No git tag provided" } : { source: "initial", version: "0.1.0", reason: "No git tag or package version available" };
687
711
  }
688
712
  const verification = verifyTag(tagName, cwd4);
689
713
  if (!verification.exists || !verification.reachable) {
714
+ if (strictReachable) {
715
+ throw new Error(
716
+ `Git tag '${tagName}' is not reachable from the current commit. The tag exists but cannot be reached from HEAD, which usually means you're on a different branch or the tag is orphaned. To allow fallback to package version, set strictReachable to false in your configuration.`
717
+ );
718
+ }
690
719
  if (packageVersion) {
691
720
  log(
692
721
  `Git tag '${tagName}' unreachable (${verification.error}), using package version: ${packageVersion}`,
@@ -792,7 +821,8 @@ async function calculateVersion(config, options) {
792
821
  prereleaseIdentifier: configPrereleaseIdentifier,
793
822
  branchPattern,
794
823
  baseBranch,
795
- mismatchStrategy
824
+ mismatchStrategy,
825
+ strictReachable
796
826
  } = config;
797
827
  const {
798
828
  latestTag,
@@ -837,7 +867,13 @@ async function calculateVersion(config, options) {
837
867
  const packageDir = pkgPath || (0, import_node_process.cwd)();
838
868
  const manifestResult = getVersionFromManifests(packageDir);
839
869
  const packageVersion = manifestResult.manifestFound && manifestResult.version ? manifestResult.version : void 0;
840
- versionSource = await getBestVersionSource(latestTag, packageVersion, packageDir, mismatchStrategy);
870
+ versionSource = await getBestVersionSource(
871
+ latestTag,
872
+ packageVersion,
873
+ packageDir,
874
+ mismatchStrategy,
875
+ strictReachable
876
+ );
841
877
  log(`Using version source: ${versionSource.source} (${versionSource.reason})`, "info");
842
878
  }
843
879
  const specifiedType = type;
@@ -1129,6 +1165,7 @@ var import_node_fs6 = __toESM(require("fs"), 1);
1129
1165
  var path6 = __toESM(require("path"), 1);
1130
1166
 
1131
1167
  // src/changelog/commitParser.ts
1168
+ init_commandExecutor();
1132
1169
  var CONVENTIONAL_COMMIT_REGEX = /^(\w+)(?:\(([^)]+)\))?(!)?: (.+)(?:\n\n([\s\S]*))?/;
1133
1170
  var BREAKING_CHANGE_REGEX = /BREAKING CHANGE: ([\s\S]+?)(?:\n\n|$)/;
1134
1171
  function extractChangelogEntriesFromCommits(projectDir, revisionRange) {
@@ -1230,8 +1267,12 @@ function extractIssueIds(body) {
1230
1267
  return issueIds;
1231
1268
  }
1232
1269
 
1270
+ // src/core/versionStrategies.ts
1271
+ init_commandExecutor();
1272
+
1233
1273
  // src/git/commands.ts
1234
1274
  var import_node_process2 = require("process");
1275
+ init_commandExecutor();
1235
1276
  async function gitAdd(files) {
1236
1277
  return execAsync("git", ["add", ...files]);
1237
1278
  }
@@ -1536,6 +1577,11 @@ var PackageProcessor = class {
1536
1577
  if (verification.exists && verification.reachable) {
1537
1578
  revisionRange = `${latestTag}..HEAD`;
1538
1579
  } else {
1580
+ if (this.config.strictReachable) {
1581
+ throw new Error(
1582
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1583
+ );
1584
+ }
1539
1585
  log(`Tag ${latestTag} is unreachable (${verification.error}), using all commits for changelog`, "debug");
1540
1586
  revisionRange = "HEAD";
1541
1587
  }
@@ -1851,6 +1897,11 @@ function createSyncStrategy(config) {
1851
1897
  });
1852
1898
  revisionRange = `${latestTag}..HEAD`;
1853
1899
  } catch {
1900
+ if (config.strictReachable) {
1901
+ throw new Error(
1902
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1903
+ );
1904
+ }
1854
1905
  log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
1855
1906
  revisionRange = "HEAD";
1856
1907
  }
@@ -1970,6 +2021,11 @@ function createSingleStrategy(config) {
1970
2021
  });
1971
2022
  revisionRange = `${latestTag}..HEAD`;
1972
2023
  } catch {
2024
+ if (config.strictReachable) {
2025
+ throw new Error(
2026
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
2027
+ );
2028
+ }
1973
2029
  log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
1974
2030
  revisionRange = "HEAD";
1975
2031
  }
package/dist/index.d.cts CHANGED
@@ -11,6 +11,7 @@ interface VersionConfigBase {
11
11
  baseBranch?: string;
12
12
  path?: string;
13
13
  name?: string;
14
+ strictReachable?: boolean;
14
15
  }
15
16
  interface Config extends VersionConfigBase {
16
17
  tagTemplate: string;
@@ -30,6 +31,7 @@ interface Config extends VersionConfigBase {
30
31
  latestTag?: string;
31
32
  isPrerelease?: boolean;
32
33
  mismatchStrategy?: 'error' | 'warn' | 'ignore' | 'prefer-package' | 'prefer-git';
34
+ strictReachable?: boolean;
33
35
  cargo?: {
34
36
  enabled?: boolean;
35
37
  paths?: string[];
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ interface VersionConfigBase {
11
11
  baseBranch?: string;
12
12
  path?: string;
13
13
  name?: string;
14
+ strictReachable?: boolean;
14
15
  }
15
16
  interface Config extends VersionConfigBase {
16
17
  tagTemplate: string;
@@ -30,6 +31,7 @@ interface Config extends VersionConfigBase {
30
31
  latestTag?: string;
31
32
  isPrerelease?: boolean;
32
33
  mismatchStrategy?: 'error' | 'warn' | 'ignore' | 'prefer-package' | 'prefer-git';
34
+ strictReachable?: boolean;
33
35
  cargo?: {
34
36
  enabled?: boolean;
35
37
  paths?: string[];
package/dist/index.js CHANGED
@@ -10,10 +10,11 @@ import {
10
10
  enableJsonOutput,
11
11
  getJsonData,
12
12
  loadConfig
13
- } from "./chunk-OQ5Z3KXR.js";
13
+ } from "./chunk-7F6RMN2K.js";
14
14
  import {
15
15
  BaseVersionError
16
16
  } from "./chunk-GQLJ7JQY.js";
17
+ import "./chunk-LMPZV35Z.js";
17
18
  export {
18
19
  BaseVersionError,
19
20
  PackageProcessor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@releasekit/version",
3
- "version": "0.2.0-next.7",
3
+ "version": "0.2.0-next.9",
4
4
  "description": "Semantic versioning based on Git history and conventional commits",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",