package-versioner 0.0.2 → 0.1.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/README.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # package-versioner
2
2
 
3
+
4
+ <a href="https://www.npmjs.com/package/package-versioner" alt="NPM Version">
5
+ <img src="https://img.shields.io/npm/v/package-versioner" /></a>
6
+ <a href="https://www.npmjs.com/package/package-versioner" alt="NPM Downloads">
7
+ <img src="https://img.shields.io/npm/dw/package-versioner" /></a>
8
+
3
9
  A powerful CLI tool for automated semantic versioning based on Git history and conventional commits. Simplifies version management in JavaScript/TypeScript projects.
4
10
 
5
11
  ## Features
@@ -32,6 +38,9 @@ npx package-versioner --bump minor
32
38
  # Create a prerelease (e.g., alpha)
33
39
  npx package-versioner --bump patch --prerelease alpha
34
40
 
41
+ # Target specific packages (only in async/independent mode, comma-separated)
42
+ npx package-versioner -t @scope/package-a,@scope/package-b
43
+
35
44
  # Perform a dry run: calculates version, logs actions, but makes no file changes or Git commits/tags
36
45
  npx package-versioner --dry-run
37
46
  ```
@@ -82,7 +91,7 @@ npx package-versioner --help
82
91
 
83
92
  ## Acknowledgements
84
93
 
85
- This project was originally forked from and inspired by `jucian0/turbo-version` ([https://github.com/jucian0/turbo-version](https://github.com/jucian0/turbo-version)). We appreciate the foundational work done by the original authors.
94
+ This project was originally forked from and inspired by [`jucian0/turbo-version`](https://github.com/jucian0/turbo-version). We appreciate the foundational work done by the original authors.
86
95
 
87
96
  ## License
88
97
 
package/dist/index.cjs CHANGED
@@ -15,21 +15,50 @@ var __copyProps = (to, from, except, desc) => {
15
15
  return to;
16
16
  };
17
17
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
18
22
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
19
23
  mod
20
24
  ));
21
25
 
22
26
  // src/index.ts
23
- var import_node_process4 = require("process");
24
- var import_chalk2 = __toESM(require("chalk"), 1);
25
27
  var import_commander = require("commander");
28
+
29
+ // src/config.ts
30
+ var fs = __toESM(require("fs"), 1);
31
+ var import_node_process = require("process");
32
+ function loadConfig(configPath) {
33
+ const localProcess = (0, import_node_process.cwd)();
34
+ const filePath = configPath || `${localProcess}/version.config.json`;
35
+ return new Promise((resolve, reject) => {
36
+ fs.readFile(filePath, "utf-8", (err, data) => {
37
+ if (err) {
38
+ reject(new Error(`Could not locate the config file at ${filePath}: ${err.message}`));
39
+ return;
40
+ }
41
+ try {
42
+ const config = JSON.parse(data);
43
+ resolve(config);
44
+ } catch (err2) {
45
+ const errorMessage = err2 instanceof Error ? err2.message : String(err2);
46
+ reject(new Error(`Failed to parse config file ${filePath}: ${errorMessage}`));
47
+ }
48
+ });
49
+ });
50
+ }
51
+
52
+ // src/utils.ts
53
+ var import_node_fs2 = __toESM(require("fs"), 1);
54
+ var import_chalk = __toESM(require("chalk"), 1);
26
55
  var import_figlet = __toESM(require("figlet"), 1);
27
56
 
28
57
  // package.json
29
58
  var package_default = {
30
59
  name: "package-versioner",
31
60
  description: "A powerful CLI tool for automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies.",
32
- version: "0.0.1",
61
+ version: "0.1.0",
33
62
  type: "module",
34
63
  main: "./dist/index.js",
35
64
  module: "./dist/index.mjs",
@@ -79,12 +108,12 @@ var package_default = {
79
108
  devDependencies: {
80
109
  "@biomejs/biome": "^1.9.4",
81
110
  "@types/figlet": "^1.5.5",
82
- "@types/node": "^18.14.0",
111
+ "@types/node": "^22.14.0",
83
112
  "@types/semver": "^7.3.13",
84
113
  "@vitest/coverage-v8": "^3.1.1",
85
114
  husky: "^9.1.7",
86
115
  "lint-staged": "^15.5.0",
87
- tsup: "^5.10.1",
116
+ tsup: "^8.4.0",
88
117
  typescript: "^5.8.3",
89
118
  vitest: "^3.1.1"
90
119
  },
@@ -101,32 +130,7 @@ var package_default = {
101
130
  packageManager: "pnpm@10.8.0+sha512.0e82714d1b5b43c74610193cb20734897c1d00de89d0e18420aebc5977fa13d780a9cb05734624e81ebd81cc876cd464794850641c48b9544326b5622ca29971"
102
131
  };
103
132
 
104
- // src/config.ts
105
- var fs = __toESM(require("fs"), 1);
106
- var import_node_process = require("process");
107
- function loadConfig(configPath) {
108
- const localProcess = (0, import_node_process.cwd)();
109
- const filePath = configPath || `${localProcess}/version.config.json`;
110
- return new Promise((resolve, reject) => {
111
- fs.readFile(filePath, "utf-8", (err, data) => {
112
- if (err) {
113
- reject(new Error(`Could not locate the config file at ${filePath}: ${err.message}`));
114
- return;
115
- }
116
- try {
117
- const config = JSON.parse(data);
118
- resolve(config);
119
- } catch (err2) {
120
- const errorMessage = err2 instanceof Error ? err2.message : String(err2);
121
- reject(new Error(`Failed to parse config file ${filePath}: ${errorMessage}`));
122
- }
123
- });
124
- });
125
- }
126
-
127
133
  // src/utils.ts
128
- var import_node_fs2 = __toESM(require("fs"), 1);
129
- var import_chalk = __toESM(require("chalk"), 1);
130
134
  var import_git_semver_tags = require("git-semver-tags");
131
135
 
132
136
  // src/git.ts
@@ -213,18 +217,22 @@ async function gitProcess({ files, nextTag, commitMessage, skipHooks, dryRun })
213
217
  message: commitMessage,
214
218
  skipHooks
215
219
  });
216
- const tagMessage = `New Version ${nextTag} generated at ${new Date().toISOString()}`;
217
- await createGitTag({
218
- tag: nextTag,
219
- message: tagMessage
220
- });
220
+ if (nextTag) {
221
+ const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
222
+ await createGitTag({
223
+ tag: nextTag,
224
+ message: tagMessage
225
+ });
226
+ }
221
227
  } else {
222
228
  log("info", "[DRY RUN] Would add files:");
223
229
  for (const file of files) {
224
230
  log("info", ` - ${file}`);
225
231
  }
226
232
  log("info", `[DRY RUN] Would commit with message: "${commitMessage}"`);
227
- log("info", `[DRY RUN] Would create tag: ${nextTag}`);
233
+ if (nextTag) {
234
+ log("info", `[DRY RUN] Would create tag: ${nextTag}`);
235
+ }
228
236
  }
229
237
  } catch (err) {
230
238
  console.log(err);
@@ -252,6 +260,25 @@ function getCurrentBranch() {
252
260
  }
253
261
 
254
262
  // src/utils.ts
263
+ function printFiglet() {
264
+ const font = "Standard";
265
+ import_figlet.default.text(package_default.name, { font }, (err, data) => {
266
+ if (err) {
267
+ log("warning", "Could not print figlet banner: Figlet error");
268
+ console.error(err);
269
+ return;
270
+ }
271
+ if (data) {
272
+ const figletText = data;
273
+ const versionText = `v${package_default.version}`;
274
+ process.stdout.write(`${import_chalk.default.hex("#FF1F57")(figletText)}
275
+ `);
276
+ process.stdout.write(`${import_chalk.default.hex("#0096FF")(versionText)}
277
+
278
+ `);
279
+ }
280
+ });
281
+ }
255
282
  function log(status, message) {
256
283
  const statusColors = {
257
284
  info: import_chalk.default.blue("\u2139"),
@@ -276,10 +303,10 @@ async function getLatestTag() {
276
303
  }
277
304
  }
278
305
  function formatTag(options, props) {
279
- const { name: name2, synced, tagPrefix } = options;
306
+ const { name, synced, tagPrefix } = options;
280
307
  const { version } = props;
281
- if (!synced && name2) {
282
- return `${tagPrefix ? tagPrefix : ""}${name2}@${version}`;
308
+ if (!synced && name) {
309
+ return `${tagPrefix ? tagPrefix : ""}${name}@${version}`;
283
310
  }
284
311
  return `${tagPrefix ? tagPrefix : "v"}${version}`;
285
312
  }
@@ -292,7 +319,7 @@ function createTemplateString(template, data) {
292
319
  function formatCommitMessage(template, version) {
293
320
  return createTemplateString(template, { version });
294
321
  }
295
- function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
322
+ function updatePackageVersion({ path: path2, version, name, dryRun }) {
296
323
  try {
297
324
  const pkgPath = `${path2}/package.json`;
298
325
  const pkg = JSON.parse(import_node_fs2.default.readFileSync(pkgPath, "utf8"));
@@ -300,12 +327,12 @@ function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
300
327
  if (!dryRun) {
301
328
  import_node_fs2.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
302
329
  `);
303
- log("success", `${name2}: ${version}`);
330
+ log("success", `${name}: ${version}`);
304
331
  } else {
305
- log("info", `[DRY RUN] Would update ${name2} package.json to version ${version}`);
332
+ log("info", `[DRY RUN] Would update ${name} package.json to version ${version}`);
306
333
  }
307
334
  } catch (error) {
308
- log("error", `Failed to update ${name2} to version ${version}`);
335
+ log("error", `Failed to update ${name} to version ${version}`);
309
336
  console.error(error);
310
337
  }
311
338
  }
@@ -318,14 +345,18 @@ var import_get_packages = require("@manypkg/get-packages");
318
345
  var import_conventional_recommended_bump = require("conventional-recommended-bump");
319
346
  var import_semver = __toESM(require("semver"), 1);
320
347
  var VersionEngine = class {
348
+ config;
321
349
  constructor(config) {
322
350
  this.config = config;
323
351
  }
352
+ /**
353
+ * Calculate the next version based on options
354
+ */
324
355
  async calculateVersion(options) {
325
- const { latestTag, type, path: path2, name: name2, branchPattern, prereleaseIdentifier } = options;
356
+ const { latestTag, type, path: path2, name, branchPattern, prereleaseIdentifier } = options;
326
357
  const originalPrefix = this.config.tagPrefix || "";
327
358
  const initialVersion = prereleaseIdentifier ? `0.0.1-${prereleaseIdentifier}` : "0.0.1";
328
- const tagSearchPattern = name2 ? originalPrefix ? `${originalPrefix}${name2}@` : `${name2}@` : originalPrefix ? `${originalPrefix}v` : "v";
359
+ const tagSearchPattern = name ? originalPrefix ? `${originalPrefix}${name}@` : `${name}@` : originalPrefix ? `${originalPrefix}v` : "v";
329
360
  let determinedReleaseType = type || null;
330
361
  if (determinedReleaseType) {
331
362
  if (!latestTag) {
@@ -366,21 +397,21 @@ var VersionEngine = class {
366
397
  if (commitsLength === 0) {
367
398
  log(
368
399
  "info",
369
- `No new commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
400
+ `No new commits found for ${name || "project"} since ${latestTag}, skipping version bump`
370
401
  );
371
402
  return "";
372
403
  }
373
404
  if (!releaseTypeFromCommits) {
374
405
  log(
375
406
  "info",
376
- `No relevant commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
407
+ `No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`
377
408
  );
378
409
  return "";
379
410
  }
380
411
  const currentVersion = import_semver.default.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
381
412
  return import_semver.default.inc(currentVersion, releaseTypeFromCommits, prereleaseIdentifier) || "";
382
413
  } catch (error) {
383
- log("error", `Failed to calculate version for ${name2 || "project"}`);
414
+ log("error", `Failed to calculate version for ${name || "project"}`);
384
415
  console.error(error);
385
416
  if (error instanceof Error && error.message.includes("No names found")) {
386
417
  log("info", "No tags found, proceeding with initial version calculation (if applicable).");
@@ -389,30 +420,44 @@ var VersionEngine = class {
389
420
  return "";
390
421
  }
391
422
  }
392
- async processPackages(packages = [], configPackages = []) {
393
- const { tagPrefix } = this.config;
394
- const pkgsResult = packages.length ? { packages } : (0, import_get_packages.getPackagesSync)((0, import_node_process3.cwd)());
423
+ /**
424
+ * Process packages based on discovery, skip list, and optional target list.
425
+ * Returns a list of package.json file paths that were updated (or would be in dry run).
426
+ */
427
+ async processPackages(discoveredPackages = [], targets = []) {
428
+ const { tagPrefix, skip } = this.config;
395
429
  const files = [];
396
- const selectedPackages = pkgsResult.packages.filter((pkg) => {
397
- var _a;
398
- if ((_a = this.config.skip) == null ? void 0 : _a.includes(pkg.packageJson.name)) {
430
+ const pkgsToConsider = discoveredPackages.filter((pkg) => {
431
+ if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
432
+ log("info", `Skipping package ${pkg.packageJson.name} based on config skip list.`);
399
433
  return false;
400
434
  }
401
- return configPackages.length === 0 || configPackages.includes(pkg.packageJson.name);
435
+ if (targets.length > 0) {
436
+ const isTargeted = targets.includes(pkg.packageJson.name);
437
+ if (!isTargeted) {
438
+ }
439
+ return isTargeted;
440
+ }
441
+ return true;
402
442
  });
403
- for (const pkg of selectedPackages) {
404
- const name2 = pkg.packageJson.name;
443
+ log("info", `Found ${pkgsToConsider.length} package(s) to process after filtering.`);
444
+ for (const pkg of pkgsToConsider) {
445
+ const name = pkg.packageJson.name;
405
446
  const pkgPath = pkg.dir;
406
447
  const prefix = formatTagPrefix(tagPrefix);
407
448
  const latestTag = await getLatestTag();
408
449
  const nextVersion = await this.calculateVersion({
409
450
  latestTag,
451
+ // This might need refinement for async based on package-specific tags
410
452
  tagPrefix: prefix,
411
453
  path: pkgPath,
412
- name: name2,
454
+ name,
455
+ // Pass name for potential package-specific tag lookups
413
456
  branchPattern: this.config.branchPattern,
414
457
  baseBranch: this.config.baseBranch,
415
- prereleaseIdentifier: this.config.prereleaseIdentifier
458
+ prereleaseIdentifier: this.config.prereleaseIdentifier,
459
+ type: this.config.forceType
460
+ // Pass forced type if provided
416
461
  });
417
462
  if (!nextVersion) {
418
463
  continue;
@@ -420,13 +465,16 @@ var VersionEngine = class {
420
465
  updatePackageVersion({
421
466
  path: pkgPath,
422
467
  version: nextVersion,
423
- name: name2,
468
+ name,
424
469
  dryRun: this.config.dryRun
425
470
  });
426
471
  files.push(import_node_path2.default.join(pkgPath, "package.json"));
427
472
  }
428
473
  return files;
429
474
  }
475
+ /**
476
+ * Create git commit and tag
477
+ */
430
478
  async createGitCommitAndTag(files, nextTag, commitMessage, dryRun) {
431
479
  try {
432
480
  await gitProcess({
@@ -445,6 +493,9 @@ var VersionEngine = class {
445
493
  (0, import_node_process3.exit)(1);
446
494
  }
447
495
  }
496
+ /**
497
+ * Synced versioning strategy (all packages get the same version)
498
+ */
448
499
  async syncedStrategy() {
449
500
  var _a;
450
501
  const {
@@ -513,6 +564,9 @@ var VersionEngine = class {
513
564
  const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
514
565
  await this.createGitCommitAndTag(files, nextTag, formattedCommitMessage, this.config.dryRun);
515
566
  }
567
+ /**
568
+ * Single package versioning strategy
569
+ */
516
570
  async singleStrategy() {
517
571
  const {
518
572
  packages: configPackages,
@@ -572,11 +626,15 @@ var VersionEngine = class {
572
626
  this.config.dryRun
573
627
  );
574
628
  }
575
- async asyncStrategy() {
629
+ /**
630
+ * Async versioning strategy (each package gets its own version)
631
+ */
632
+ async asyncStrategy(cliTargets = []) {
576
633
  const {
577
- packages: configPackages,
578
634
  commitMessage = "chore(release): ${version}",
635
+ // Align with test expectations
579
636
  skipHooks
637
+ // Add skipHooks here
580
638
  } = this.config;
581
639
  let pkgsResult;
582
640
  try {
@@ -590,9 +648,9 @@ var VersionEngine = class {
590
648
  (0, import_node_process3.exit)(1);
591
649
  return;
592
650
  }
593
- const pkgsToProcess = await this.processPackages(pkgsResult.packages, configPackages);
651
+ const pkgsToProcess = await this.processPackages(pkgsResult.packages, cliTargets);
594
652
  if (pkgsToProcess.length === 0) {
595
- log("info", "No packages to process");
653
+ log("info", "No packages to process based on changes and targets");
596
654
  return;
597
655
  }
598
656
  const formattedCommitMessage = commitMessage;
@@ -605,7 +663,7 @@ var VersionEngine = class {
605
663
  dryRun: this.config.dryRun
606
664
  });
607
665
  if (!this.config.dryRun) {
608
- log("success", "Created version commit");
666
+ log("success", `Created version commit for ${pkgsToProcess.length} package(s)`);
609
667
  }
610
668
  } catch (error) {
611
669
  log("error", "Failed to create version commit");
@@ -616,63 +674,49 @@ var VersionEngine = class {
616
674
  };
617
675
 
618
676
  // src/index.ts
619
- var name = "package-versioner";
620
- var program = new import_commander.Command();
621
- program.name("package-versioner").description("Manages package versions using Git context.").version(package_default.version);
622
- program.option("-t, --target <project>", "specific package to update").option("-b, --bump <version>", "type of version bump to perform", (value) => {
623
- const validBumps = [
624
- "patch",
625
- "minor",
626
- "major",
627
- "premajor",
628
- "preminor",
629
- "prepatch",
630
- "prerelease"
631
- ];
632
- if (!validBumps.includes(value)) {
633
- log("error", `Invalid bump type '${value}'. Valid options are: ${validBumps.join(", ")}`);
634
- process.exit(1);
635
- }
636
- return value;
637
- }).option("--base-branch <branch>", "override the base branch for this operation").option("--synced", "force synced versioning mode").option("--no-synced", "force async versioning mode").option(
638
- "--skip <packages>",
639
- "comma-separated list of packages to skip",
640
- (value) => value.split(",")
641
- ).option("--prerelease <identifier>", "set prerelease identifier (e.g., alpha, beta)").option("--skip-hooks", "skip Git hooks for this operation").option("--config <path>", "specify a custom config file path").option("--dry-run", "Calculate version and log actions without changing files or Git state");
642
- program.description("Version packages based on Git context and conventional commits").action(async (options) => {
643
- const figletText = import_figlet.default.textSync(name);
644
- const versionText = `v${package_default.version}`;
645
- process.stdout.write(`${import_chalk2.default.hex("#FF1F57")(figletText)}
646
- `);
647
- process.stdout.write(`${import_chalk2.default.hex("#0096FF")(versionText)}
648
-
649
- `);
677
+ async function run() {
678
+ printFiglet();
679
+ const program = new import_commander.Command();
680
+ program.name("package-versioner").description(
681
+ "Automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies."
682
+ ).version("0.0.2").option("--config <path>", "Path to the configuration file").option("--dry-run", "Simulate the versioning process without making changes").option("--synced", "Force synced versioning strategy (overrides config)").option("--bump <type>", "Force a specific release type (patch, minor, major)").option(
683
+ "--prerelease <identifier>",
684
+ "Create a prerelease version with the specified identifier"
685
+ ).option(
686
+ "-t, --target <targets>",
687
+ "Comma-separated list of package names to target (only for async strategy)"
688
+ ).parse(process.argv);
689
+ const options = program.opts();
650
690
  try {
651
- const configPath = options.config || void 0;
652
- const config = await loadConfig(configPath);
653
- if (options.baseBranch)
654
- config.baseBranch = options.baseBranch;
655
- if (options.synced !== void 0)
656
- config.synced = options.synced;
657
- if (options.skip)
658
- config.skip = options.skip;
691
+ const config = await loadConfig(options.config);
692
+ log("info", `Loaded configuration from ${options.config || "version.config.json"}`);
693
+ if (options.dryRun) config.dryRun = true;
694
+ if (options.synced) config.synced = true;
695
+ if (options.bump) config.forceType = options.bump;
659
696
  if (options.prerelease)
660
- config.prereleaseIdentifier = options.prerelease;
661
- if (options.skipHooks !== void 0)
662
- config.skipHooks = options.skipHooks;
663
- if (options.dryRun !== void 0)
664
- config.dryRun = options.dryRun;
697
+ config.prereleaseIdentifier = options.prerelease === true ? "rc" : options.prerelease;
698
+ const cliTargets = options.target ? options.target.split(",").map((t) => t.trim()) : [];
665
699
  const engine = new VersionEngine(config);
666
700
  if (config.synced) {
701
+ log("info", "Using synced versioning strategy.");
667
702
  await engine.syncedStrategy();
668
- } else if (options.bump && options.target) {
703
+ } else if (config.packages && config.packages.length === 1) {
704
+ log("info", "Using single package versioning strategy.");
705
+ if (cliTargets.length > 0) {
706
+ log("warning", "--target flag is ignored for single package strategy.");
707
+ }
669
708
  await engine.singleStrategy();
670
709
  } else {
671
- await engine.asyncStrategy();
710
+ log("info", "Using async versioning strategy.");
711
+ if (cliTargets.length > 0) {
712
+ log("info", `Targeting specific packages: ${cliTargets.join(", ")}`);
713
+ }
714
+ await engine.asyncStrategy(cliTargets);
672
715
  }
673
- } catch (err) {
674
- log("error", `${err instanceof Error ? err.message : String(err)}`);
675
- (0, import_node_process4.exit)(1);
716
+ log("success", "Versioning process completed.");
717
+ } catch (error) {
718
+ log("error", error instanceof Error ? error.message : String(error));
719
+ process.exit(1);
676
720
  }
677
- });
678
- program.parse();
721
+ }
722
+ run();
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js CHANGED
@@ -1,16 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { exit as exit2 } from "process";
5
- import chalk2 from "chalk";
6
4
  import { Command } from "commander";
5
+
6
+ // src/config.ts
7
+ import * as fs from "node:fs";
8
+ import { cwd } from "node:process";
9
+ function loadConfig(configPath) {
10
+ const localProcess = cwd();
11
+ const filePath = configPath || `${localProcess}/version.config.json`;
12
+ return new Promise((resolve, reject) => {
13
+ fs.readFile(filePath, "utf-8", (err, data) => {
14
+ if (err) {
15
+ reject(new Error(`Could not locate the config file at ${filePath}: ${err.message}`));
16
+ return;
17
+ }
18
+ try {
19
+ const config = JSON.parse(data);
20
+ resolve(config);
21
+ } catch (err2) {
22
+ const errorMessage = err2 instanceof Error ? err2.message : String(err2);
23
+ reject(new Error(`Failed to parse config file ${filePath}: ${errorMessage}`));
24
+ }
25
+ });
26
+ });
27
+ }
28
+
29
+ // src/utils.ts
30
+ import fs2 from "node:fs";
31
+ import chalk from "chalk";
7
32
  import figlet from "figlet";
8
33
 
9
34
  // package.json
10
35
  var package_default = {
11
36
  name: "package-versioner",
12
37
  description: "A powerful CLI tool for automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies.",
13
- version: "0.0.1",
38
+ version: "0.1.0",
14
39
  type: "module",
15
40
  main: "./dist/index.js",
16
41
  module: "./dist/index.mjs",
@@ -60,12 +85,12 @@ var package_default = {
60
85
  devDependencies: {
61
86
  "@biomejs/biome": "^1.9.4",
62
87
  "@types/figlet": "^1.5.5",
63
- "@types/node": "^18.14.0",
88
+ "@types/node": "^22.14.0",
64
89
  "@types/semver": "^7.3.13",
65
90
  "@vitest/coverage-v8": "^3.1.1",
66
91
  husky: "^9.1.7",
67
92
  "lint-staged": "^15.5.0",
68
- tsup: "^5.10.1",
93
+ tsup: "^8.4.0",
69
94
  typescript: "^5.8.3",
70
95
  vitest: "^3.1.1"
71
96
  },
@@ -82,39 +107,14 @@ var package_default = {
82
107
  packageManager: "pnpm@10.8.0+sha512.0e82714d1b5b43c74610193cb20734897c1d00de89d0e18420aebc5977fa13d780a9cb05734624e81ebd81cc876cd464794850641c48b9544326b5622ca29971"
83
108
  };
84
109
 
85
- // src/config.ts
86
- import * as fs from "fs";
87
- import { cwd } from "process";
88
- function loadConfig(configPath) {
89
- const localProcess = cwd();
90
- const filePath = configPath || `${localProcess}/version.config.json`;
91
- return new Promise((resolve, reject) => {
92
- fs.readFile(filePath, "utf-8", (err, data) => {
93
- if (err) {
94
- reject(new Error(`Could not locate the config file at ${filePath}: ${err.message}`));
95
- return;
96
- }
97
- try {
98
- const config = JSON.parse(data);
99
- resolve(config);
100
- } catch (err2) {
101
- const errorMessage = err2 instanceof Error ? err2.message : String(err2);
102
- reject(new Error(`Failed to parse config file ${filePath}: ${errorMessage}`));
103
- }
104
- });
105
- });
106
- }
107
-
108
110
  // src/utils.ts
109
- import fs2 from "fs";
110
- import chalk from "chalk";
111
111
  import { getSemverTags } from "git-semver-tags";
112
112
 
113
113
  // src/git.ts
114
- import { exec, execSync as syncExec } from "child_process";
115
- import { existsSync, statSync } from "fs";
116
- import { join } from "path";
117
- import { cwd as cwd2 } from "process";
114
+ import { exec, execSync as syncExec } from "node:child_process";
115
+ import { existsSync, statSync } from "node:fs";
116
+ import { join } from "node:path";
117
+ import { cwd as cwd2 } from "node:process";
118
118
  var execAsync = (command) => {
119
119
  return new Promise((resolve, reject) => {
120
120
  const options = { maxBuffer: 1024 * 1024 * 10 };
@@ -194,18 +194,22 @@ async function gitProcess({ files, nextTag, commitMessage, skipHooks, dryRun })
194
194
  message: commitMessage,
195
195
  skipHooks
196
196
  });
197
- const tagMessage = `New Version ${nextTag} generated at ${new Date().toISOString()}`;
198
- await createGitTag({
199
- tag: nextTag,
200
- message: tagMessage
201
- });
197
+ if (nextTag) {
198
+ const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
199
+ await createGitTag({
200
+ tag: nextTag,
201
+ message: tagMessage
202
+ });
203
+ }
202
204
  } else {
203
205
  log("info", "[DRY RUN] Would add files:");
204
206
  for (const file of files) {
205
207
  log("info", ` - ${file}`);
206
208
  }
207
209
  log("info", `[DRY RUN] Would commit with message: "${commitMessage}"`);
208
- log("info", `[DRY RUN] Would create tag: ${nextTag}`);
210
+ if (nextTag) {
211
+ log("info", `[DRY RUN] Would create tag: ${nextTag}`);
212
+ }
209
213
  }
210
214
  } catch (err) {
211
215
  console.log(err);
@@ -233,6 +237,25 @@ function getCurrentBranch() {
233
237
  }
234
238
 
235
239
  // src/utils.ts
240
+ function printFiglet() {
241
+ const font = "Standard";
242
+ figlet.text(package_default.name, { font }, (err, data) => {
243
+ if (err) {
244
+ log("warning", "Could not print figlet banner: Figlet error");
245
+ console.error(err);
246
+ return;
247
+ }
248
+ if (data) {
249
+ const figletText = data;
250
+ const versionText = `v${package_default.version}`;
251
+ process.stdout.write(`${chalk.hex("#FF1F57")(figletText)}
252
+ `);
253
+ process.stdout.write(`${chalk.hex("#0096FF")(versionText)}
254
+
255
+ `);
256
+ }
257
+ });
258
+ }
236
259
  function log(status, message) {
237
260
  const statusColors = {
238
261
  info: chalk.blue("\u2139"),
@@ -257,10 +280,10 @@ async function getLatestTag() {
257
280
  }
258
281
  }
259
282
  function formatTag(options, props) {
260
- const { name: name2, synced, tagPrefix } = options;
283
+ const { name, synced, tagPrefix } = options;
261
284
  const { version } = props;
262
- if (!synced && name2) {
263
- return `${tagPrefix ? tagPrefix : ""}${name2}@${version}`;
285
+ if (!synced && name) {
286
+ return `${tagPrefix ? tagPrefix : ""}${name}@${version}`;
264
287
  }
265
288
  return `${tagPrefix ? tagPrefix : "v"}${version}`;
266
289
  }
@@ -273,7 +296,7 @@ function createTemplateString(template, data) {
273
296
  function formatCommitMessage(template, version) {
274
297
  return createTemplateString(template, { version });
275
298
  }
276
- function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
299
+ function updatePackageVersion({ path: path2, version, name, dryRun }) {
277
300
  try {
278
301
  const pkgPath = `${path2}/package.json`;
279
302
  const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
@@ -281,32 +304,36 @@ function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
281
304
  if (!dryRun) {
282
305
  fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
283
306
  `);
284
- log("success", `${name2}: ${version}`);
307
+ log("success", `${name}: ${version}`);
285
308
  } else {
286
- log("info", `[DRY RUN] Would update ${name2} package.json to version ${version}`);
309
+ log("info", `[DRY RUN] Would update ${name} package.json to version ${version}`);
287
310
  }
288
311
  } catch (error) {
289
- log("error", `Failed to update ${name2} to version ${version}`);
312
+ log("error", `Failed to update ${name} to version ${version}`);
290
313
  console.error(error);
291
314
  }
292
315
  }
293
316
 
294
317
  // src/versionEngine.ts
295
- import fs3 from "fs";
296
- import path from "path";
297
- import { cwd as cwd3, exit } from "process";
318
+ import fs3 from "node:fs";
319
+ import path from "node:path";
320
+ import { cwd as cwd3, exit } from "node:process";
298
321
  import { getPackagesSync } from "@manypkg/get-packages";
299
322
  import { Bumper } from "conventional-recommended-bump";
300
323
  import semver from "semver";
301
324
  var VersionEngine = class {
325
+ config;
302
326
  constructor(config) {
303
327
  this.config = config;
304
328
  }
329
+ /**
330
+ * Calculate the next version based on options
331
+ */
305
332
  async calculateVersion(options) {
306
- const { latestTag, type, path: path2, name: name2, branchPattern, prereleaseIdentifier } = options;
333
+ const { latestTag, type, path: path2, name, branchPattern, prereleaseIdentifier } = options;
307
334
  const originalPrefix = this.config.tagPrefix || "";
308
335
  const initialVersion = prereleaseIdentifier ? `0.0.1-${prereleaseIdentifier}` : "0.0.1";
309
- const tagSearchPattern = name2 ? originalPrefix ? `${originalPrefix}${name2}@` : `${name2}@` : originalPrefix ? `${originalPrefix}v` : "v";
336
+ const tagSearchPattern = name ? originalPrefix ? `${originalPrefix}${name}@` : `${name}@` : originalPrefix ? `${originalPrefix}v` : "v";
310
337
  let determinedReleaseType = type || null;
311
338
  if (determinedReleaseType) {
312
339
  if (!latestTag) {
@@ -347,21 +374,21 @@ var VersionEngine = class {
347
374
  if (commitsLength === 0) {
348
375
  log(
349
376
  "info",
350
- `No new commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
377
+ `No new commits found for ${name || "project"} since ${latestTag}, skipping version bump`
351
378
  );
352
379
  return "";
353
380
  }
354
381
  if (!releaseTypeFromCommits) {
355
382
  log(
356
383
  "info",
357
- `No relevant commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
384
+ `No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`
358
385
  );
359
386
  return "";
360
387
  }
361
388
  const currentVersion = semver.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
362
389
  return semver.inc(currentVersion, releaseTypeFromCommits, prereleaseIdentifier) || "";
363
390
  } catch (error) {
364
- log("error", `Failed to calculate version for ${name2 || "project"}`);
391
+ log("error", `Failed to calculate version for ${name || "project"}`);
365
392
  console.error(error);
366
393
  if (error instanceof Error && error.message.includes("No names found")) {
367
394
  log("info", "No tags found, proceeding with initial version calculation (if applicable).");
@@ -370,30 +397,44 @@ var VersionEngine = class {
370
397
  return "";
371
398
  }
372
399
  }
373
- async processPackages(packages = [], configPackages = []) {
374
- const { tagPrefix } = this.config;
375
- const pkgsResult = packages.length ? { packages } : getPackagesSync(cwd3());
400
+ /**
401
+ * Process packages based on discovery, skip list, and optional target list.
402
+ * Returns a list of package.json file paths that were updated (or would be in dry run).
403
+ */
404
+ async processPackages(discoveredPackages = [], targets = []) {
405
+ const { tagPrefix, skip } = this.config;
376
406
  const files = [];
377
- const selectedPackages = pkgsResult.packages.filter((pkg) => {
378
- var _a;
379
- if ((_a = this.config.skip) == null ? void 0 : _a.includes(pkg.packageJson.name)) {
407
+ const pkgsToConsider = discoveredPackages.filter((pkg) => {
408
+ if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
409
+ log("info", `Skipping package ${pkg.packageJson.name} based on config skip list.`);
380
410
  return false;
381
411
  }
382
- return configPackages.length === 0 || configPackages.includes(pkg.packageJson.name);
412
+ if (targets.length > 0) {
413
+ const isTargeted = targets.includes(pkg.packageJson.name);
414
+ if (!isTargeted) {
415
+ }
416
+ return isTargeted;
417
+ }
418
+ return true;
383
419
  });
384
- for (const pkg of selectedPackages) {
385
- const name2 = pkg.packageJson.name;
420
+ log("info", `Found ${pkgsToConsider.length} package(s) to process after filtering.`);
421
+ for (const pkg of pkgsToConsider) {
422
+ const name = pkg.packageJson.name;
386
423
  const pkgPath = pkg.dir;
387
424
  const prefix = formatTagPrefix(tagPrefix);
388
425
  const latestTag = await getLatestTag();
389
426
  const nextVersion = await this.calculateVersion({
390
427
  latestTag,
428
+ // This might need refinement for async based on package-specific tags
391
429
  tagPrefix: prefix,
392
430
  path: pkgPath,
393
- name: name2,
431
+ name,
432
+ // Pass name for potential package-specific tag lookups
394
433
  branchPattern: this.config.branchPattern,
395
434
  baseBranch: this.config.baseBranch,
396
- prereleaseIdentifier: this.config.prereleaseIdentifier
435
+ prereleaseIdentifier: this.config.prereleaseIdentifier,
436
+ type: this.config.forceType
437
+ // Pass forced type if provided
397
438
  });
398
439
  if (!nextVersion) {
399
440
  continue;
@@ -401,13 +442,16 @@ var VersionEngine = class {
401
442
  updatePackageVersion({
402
443
  path: pkgPath,
403
444
  version: nextVersion,
404
- name: name2,
445
+ name,
405
446
  dryRun: this.config.dryRun
406
447
  });
407
448
  files.push(path.join(pkgPath, "package.json"));
408
449
  }
409
450
  return files;
410
451
  }
452
+ /**
453
+ * Create git commit and tag
454
+ */
411
455
  async createGitCommitAndTag(files, nextTag, commitMessage, dryRun) {
412
456
  try {
413
457
  await gitProcess({
@@ -426,6 +470,9 @@ var VersionEngine = class {
426
470
  exit(1);
427
471
  }
428
472
  }
473
+ /**
474
+ * Synced versioning strategy (all packages get the same version)
475
+ */
429
476
  async syncedStrategy() {
430
477
  var _a;
431
478
  const {
@@ -494,6 +541,9 @@ var VersionEngine = class {
494
541
  const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
495
542
  await this.createGitCommitAndTag(files, nextTag, formattedCommitMessage, this.config.dryRun);
496
543
  }
544
+ /**
545
+ * Single package versioning strategy
546
+ */
497
547
  async singleStrategy() {
498
548
  const {
499
549
  packages: configPackages,
@@ -553,11 +603,15 @@ var VersionEngine = class {
553
603
  this.config.dryRun
554
604
  );
555
605
  }
556
- async asyncStrategy() {
606
+ /**
607
+ * Async versioning strategy (each package gets its own version)
608
+ */
609
+ async asyncStrategy(cliTargets = []) {
557
610
  const {
558
- packages: configPackages,
559
611
  commitMessage = "chore(release): ${version}",
612
+ // Align with test expectations
560
613
  skipHooks
614
+ // Add skipHooks here
561
615
  } = this.config;
562
616
  let pkgsResult;
563
617
  try {
@@ -571,9 +625,9 @@ var VersionEngine = class {
571
625
  exit(1);
572
626
  return;
573
627
  }
574
- const pkgsToProcess = await this.processPackages(pkgsResult.packages, configPackages);
628
+ const pkgsToProcess = await this.processPackages(pkgsResult.packages, cliTargets);
575
629
  if (pkgsToProcess.length === 0) {
576
- log("info", "No packages to process");
630
+ log("info", "No packages to process based on changes and targets");
577
631
  return;
578
632
  }
579
633
  const formattedCommitMessage = commitMessage;
@@ -586,7 +640,7 @@ var VersionEngine = class {
586
640
  dryRun: this.config.dryRun
587
641
  });
588
642
  if (!this.config.dryRun) {
589
- log("success", "Created version commit");
643
+ log("success", `Created version commit for ${pkgsToProcess.length} package(s)`);
590
644
  }
591
645
  } catch (error) {
592
646
  log("error", "Failed to create version commit");
@@ -597,63 +651,49 @@ var VersionEngine = class {
597
651
  };
598
652
 
599
653
  // src/index.ts
600
- var name = "package-versioner";
601
- var program = new Command();
602
- program.name("package-versioner").description("Manages package versions using Git context.").version(package_default.version);
603
- program.option("-t, --target <project>", "specific package to update").option("-b, --bump <version>", "type of version bump to perform", (value) => {
604
- const validBumps = [
605
- "patch",
606
- "minor",
607
- "major",
608
- "premajor",
609
- "preminor",
610
- "prepatch",
611
- "prerelease"
612
- ];
613
- if (!validBumps.includes(value)) {
614
- log("error", `Invalid bump type '${value}'. Valid options are: ${validBumps.join(", ")}`);
615
- process.exit(1);
616
- }
617
- return value;
618
- }).option("--base-branch <branch>", "override the base branch for this operation").option("--synced", "force synced versioning mode").option("--no-synced", "force async versioning mode").option(
619
- "--skip <packages>",
620
- "comma-separated list of packages to skip",
621
- (value) => value.split(",")
622
- ).option("--prerelease <identifier>", "set prerelease identifier (e.g., alpha, beta)").option("--skip-hooks", "skip Git hooks for this operation").option("--config <path>", "specify a custom config file path").option("--dry-run", "Calculate version and log actions without changing files or Git state");
623
- program.description("Version packages based on Git context and conventional commits").action(async (options) => {
624
- const figletText = figlet.textSync(name);
625
- const versionText = `v${package_default.version}`;
626
- process.stdout.write(`${chalk2.hex("#FF1F57")(figletText)}
627
- `);
628
- process.stdout.write(`${chalk2.hex("#0096FF")(versionText)}
629
-
630
- `);
654
+ async function run() {
655
+ printFiglet();
656
+ const program = new Command();
657
+ program.name("package-versioner").description(
658
+ "Automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies."
659
+ ).version("0.0.2").option("--config <path>", "Path to the configuration file").option("--dry-run", "Simulate the versioning process without making changes").option("--synced", "Force synced versioning strategy (overrides config)").option("--bump <type>", "Force a specific release type (patch, minor, major)").option(
660
+ "--prerelease <identifier>",
661
+ "Create a prerelease version with the specified identifier"
662
+ ).option(
663
+ "-t, --target <targets>",
664
+ "Comma-separated list of package names to target (only for async strategy)"
665
+ ).parse(process.argv);
666
+ const options = program.opts();
631
667
  try {
632
- const configPath = options.config || void 0;
633
- const config = await loadConfig(configPath);
634
- if (options.baseBranch)
635
- config.baseBranch = options.baseBranch;
636
- if (options.synced !== void 0)
637
- config.synced = options.synced;
638
- if (options.skip)
639
- config.skip = options.skip;
668
+ const config = await loadConfig(options.config);
669
+ log("info", `Loaded configuration from ${options.config || "version.config.json"}`);
670
+ if (options.dryRun) config.dryRun = true;
671
+ if (options.synced) config.synced = true;
672
+ if (options.bump) config.forceType = options.bump;
640
673
  if (options.prerelease)
641
- config.prereleaseIdentifier = options.prerelease;
642
- if (options.skipHooks !== void 0)
643
- config.skipHooks = options.skipHooks;
644
- if (options.dryRun !== void 0)
645
- config.dryRun = options.dryRun;
674
+ config.prereleaseIdentifier = options.prerelease === true ? "rc" : options.prerelease;
675
+ const cliTargets = options.target ? options.target.split(",").map((t) => t.trim()) : [];
646
676
  const engine = new VersionEngine(config);
647
677
  if (config.synced) {
678
+ log("info", "Using synced versioning strategy.");
648
679
  await engine.syncedStrategy();
649
- } else if (options.bump && options.target) {
680
+ } else if (config.packages && config.packages.length === 1) {
681
+ log("info", "Using single package versioning strategy.");
682
+ if (cliTargets.length > 0) {
683
+ log("warning", "--target flag is ignored for single package strategy.");
684
+ }
650
685
  await engine.singleStrategy();
651
686
  } else {
652
- await engine.asyncStrategy();
687
+ log("info", "Using async versioning strategy.");
688
+ if (cliTargets.length > 0) {
689
+ log("info", `Targeting specific packages: ${cliTargets.join(", ")}`);
690
+ }
691
+ await engine.asyncStrategy(cliTargets);
653
692
  }
654
- } catch (err) {
655
- log("error", `${err instanceof Error ? err.message : String(err)}`);
656
- exit2(1);
693
+ log("success", "Versioning process completed.");
694
+ } catch (error) {
695
+ log("error", error instanceof Error ? error.message : String(error));
696
+ process.exit(1);
657
697
  }
658
- });
659
- program.parse();
698
+ }
699
+ run();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "package-versioner",
3
3
  "description": "A powerful CLI tool for automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies.",
4
- "version": "0.0.2",
4
+ "version": "0.1.1",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -38,12 +38,12 @@
38
38
  "devDependencies": {
39
39
  "@biomejs/biome": "^1.9.4",
40
40
  "@types/figlet": "^1.5.5",
41
- "@types/node": "^18.14.0",
41
+ "@types/node": "^22.14.0",
42
42
  "@types/semver": "^7.3.13",
43
43
  "@vitest/coverage-v8": "^3.1.1",
44
44
  "husky": "^9.1.7",
45
45
  "lint-staged": "^15.5.0",
46
- "tsup": "^5.10.1",
46
+ "tsup": "^8.4.0",
47
47
  "typescript": "^5.8.3",
48
48
  "vitest": "^3.1.1"
49
49
  },