package-versioner 0.0.2 → 0.1.0

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.0.2",
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,7 +217,7 @@ 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()}`;
220
+ const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
217
221
  await createGitTag({
218
222
  tag: nextTag,
219
223
  message: tagMessage
@@ -252,6 +256,25 @@ function getCurrentBranch() {
252
256
  }
253
257
 
254
258
  // src/utils.ts
259
+ function printFiglet() {
260
+ const font = "Standard";
261
+ import_figlet.default.text(package_default.name, { font }, (err, data) => {
262
+ if (err) {
263
+ log("warning", "Could not print figlet banner: Figlet error");
264
+ console.error(err);
265
+ return;
266
+ }
267
+ if (data) {
268
+ const figletText = data;
269
+ const versionText = `v${package_default.version}`;
270
+ process.stdout.write(`${import_chalk.default.hex("#FF1F57")(figletText)}
271
+ `);
272
+ process.stdout.write(`${import_chalk.default.hex("#0096FF")(versionText)}
273
+
274
+ `);
275
+ }
276
+ });
277
+ }
255
278
  function log(status, message) {
256
279
  const statusColors = {
257
280
  info: import_chalk.default.blue("\u2139"),
@@ -276,10 +299,10 @@ async function getLatestTag() {
276
299
  }
277
300
  }
278
301
  function formatTag(options, props) {
279
- const { name: name2, synced, tagPrefix } = options;
302
+ const { name, synced, tagPrefix } = options;
280
303
  const { version } = props;
281
- if (!synced && name2) {
282
- return `${tagPrefix ? tagPrefix : ""}${name2}@${version}`;
304
+ if (!synced && name) {
305
+ return `${tagPrefix ? tagPrefix : ""}${name}@${version}`;
283
306
  }
284
307
  return `${tagPrefix ? tagPrefix : "v"}${version}`;
285
308
  }
@@ -292,7 +315,7 @@ function createTemplateString(template, data) {
292
315
  function formatCommitMessage(template, version) {
293
316
  return createTemplateString(template, { version });
294
317
  }
295
- function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
318
+ function updatePackageVersion({ path: path2, version, name, dryRun }) {
296
319
  try {
297
320
  const pkgPath = `${path2}/package.json`;
298
321
  const pkg = JSON.parse(import_node_fs2.default.readFileSync(pkgPath, "utf8"));
@@ -300,12 +323,12 @@ function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
300
323
  if (!dryRun) {
301
324
  import_node_fs2.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
302
325
  `);
303
- log("success", `${name2}: ${version}`);
326
+ log("success", `${name}: ${version}`);
304
327
  } else {
305
- log("info", `[DRY RUN] Would update ${name2} package.json to version ${version}`);
328
+ log("info", `[DRY RUN] Would update ${name} package.json to version ${version}`);
306
329
  }
307
330
  } catch (error) {
308
- log("error", `Failed to update ${name2} to version ${version}`);
331
+ log("error", `Failed to update ${name} to version ${version}`);
309
332
  console.error(error);
310
333
  }
311
334
  }
@@ -318,14 +341,18 @@ var import_get_packages = require("@manypkg/get-packages");
318
341
  var import_conventional_recommended_bump = require("conventional-recommended-bump");
319
342
  var import_semver = __toESM(require("semver"), 1);
320
343
  var VersionEngine = class {
344
+ config;
321
345
  constructor(config) {
322
346
  this.config = config;
323
347
  }
348
+ /**
349
+ * Calculate the next version based on options
350
+ */
324
351
  async calculateVersion(options) {
325
- const { latestTag, type, path: path2, name: name2, branchPattern, prereleaseIdentifier } = options;
352
+ const { latestTag, type, path: path2, name, branchPattern, prereleaseIdentifier } = options;
326
353
  const originalPrefix = this.config.tagPrefix || "";
327
354
  const initialVersion = prereleaseIdentifier ? `0.0.1-${prereleaseIdentifier}` : "0.0.1";
328
- const tagSearchPattern = name2 ? originalPrefix ? `${originalPrefix}${name2}@` : `${name2}@` : originalPrefix ? `${originalPrefix}v` : "v";
355
+ const tagSearchPattern = name ? originalPrefix ? `${originalPrefix}${name}@` : `${name}@` : originalPrefix ? `${originalPrefix}v` : "v";
329
356
  let determinedReleaseType = type || null;
330
357
  if (determinedReleaseType) {
331
358
  if (!latestTag) {
@@ -366,21 +393,21 @@ var VersionEngine = class {
366
393
  if (commitsLength === 0) {
367
394
  log(
368
395
  "info",
369
- `No new commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
396
+ `No new commits found for ${name || "project"} since ${latestTag}, skipping version bump`
370
397
  );
371
398
  return "";
372
399
  }
373
400
  if (!releaseTypeFromCommits) {
374
401
  log(
375
402
  "info",
376
- `No relevant commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
403
+ `No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`
377
404
  );
378
405
  return "";
379
406
  }
380
407
  const currentVersion = import_semver.default.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
381
408
  return import_semver.default.inc(currentVersion, releaseTypeFromCommits, prereleaseIdentifier) || "";
382
409
  } catch (error) {
383
- log("error", `Failed to calculate version for ${name2 || "project"}`);
410
+ log("error", `Failed to calculate version for ${name || "project"}`);
384
411
  console.error(error);
385
412
  if (error instanceof Error && error.message.includes("No names found")) {
386
413
  log("info", "No tags found, proceeding with initial version calculation (if applicable).");
@@ -389,30 +416,44 @@ var VersionEngine = class {
389
416
  return "";
390
417
  }
391
418
  }
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)());
419
+ /**
420
+ * Process packages based on discovery, skip list, and optional target list.
421
+ * Returns a list of package.json file paths that were updated (or would be in dry run).
422
+ */
423
+ async processPackages(discoveredPackages = [], targets = []) {
424
+ const { tagPrefix, skip } = this.config;
395
425
  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)) {
426
+ const pkgsToConsider = discoveredPackages.filter((pkg) => {
427
+ if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
428
+ log("info", `Skipping package ${pkg.packageJson.name} based on config skip list.`);
399
429
  return false;
400
430
  }
401
- return configPackages.length === 0 || configPackages.includes(pkg.packageJson.name);
431
+ if (targets.length > 0) {
432
+ const isTargeted = targets.includes(pkg.packageJson.name);
433
+ if (!isTargeted) {
434
+ }
435
+ return isTargeted;
436
+ }
437
+ return true;
402
438
  });
403
- for (const pkg of selectedPackages) {
404
- const name2 = pkg.packageJson.name;
439
+ log("info", `Found ${pkgsToConsider.length} package(s) to process after filtering.`);
440
+ for (const pkg of pkgsToConsider) {
441
+ const name = pkg.packageJson.name;
405
442
  const pkgPath = pkg.dir;
406
443
  const prefix = formatTagPrefix(tagPrefix);
407
444
  const latestTag = await getLatestTag();
408
445
  const nextVersion = await this.calculateVersion({
409
446
  latestTag,
447
+ // This might need refinement for async based on package-specific tags
410
448
  tagPrefix: prefix,
411
449
  path: pkgPath,
412
- name: name2,
450
+ name,
451
+ // Pass name for potential package-specific tag lookups
413
452
  branchPattern: this.config.branchPattern,
414
453
  baseBranch: this.config.baseBranch,
415
- prereleaseIdentifier: this.config.prereleaseIdentifier
454
+ prereleaseIdentifier: this.config.prereleaseIdentifier,
455
+ type: this.config.forceType
456
+ // Pass forced type if provided
416
457
  });
417
458
  if (!nextVersion) {
418
459
  continue;
@@ -420,13 +461,16 @@ var VersionEngine = class {
420
461
  updatePackageVersion({
421
462
  path: pkgPath,
422
463
  version: nextVersion,
423
- name: name2,
464
+ name,
424
465
  dryRun: this.config.dryRun
425
466
  });
426
467
  files.push(import_node_path2.default.join(pkgPath, "package.json"));
427
468
  }
428
469
  return files;
429
470
  }
471
+ /**
472
+ * Create git commit and tag
473
+ */
430
474
  async createGitCommitAndTag(files, nextTag, commitMessage, dryRun) {
431
475
  try {
432
476
  await gitProcess({
@@ -445,6 +489,9 @@ var VersionEngine = class {
445
489
  (0, import_node_process3.exit)(1);
446
490
  }
447
491
  }
492
+ /**
493
+ * Synced versioning strategy (all packages get the same version)
494
+ */
448
495
  async syncedStrategy() {
449
496
  var _a;
450
497
  const {
@@ -513,6 +560,9 @@ var VersionEngine = class {
513
560
  const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
514
561
  await this.createGitCommitAndTag(files, nextTag, formattedCommitMessage, this.config.dryRun);
515
562
  }
563
+ /**
564
+ * Single package versioning strategy
565
+ */
516
566
  async singleStrategy() {
517
567
  const {
518
568
  packages: configPackages,
@@ -572,11 +622,15 @@ var VersionEngine = class {
572
622
  this.config.dryRun
573
623
  );
574
624
  }
575
- async asyncStrategy() {
625
+ /**
626
+ * Async versioning strategy (each package gets its own version)
627
+ */
628
+ async asyncStrategy(cliTargets = []) {
576
629
  const {
577
- packages: configPackages,
578
630
  commitMessage = "chore(release): ${version}",
631
+ // Align with test expectations
579
632
  skipHooks
633
+ // Add skipHooks here
580
634
  } = this.config;
581
635
  let pkgsResult;
582
636
  try {
@@ -590,9 +644,9 @@ var VersionEngine = class {
590
644
  (0, import_node_process3.exit)(1);
591
645
  return;
592
646
  }
593
- const pkgsToProcess = await this.processPackages(pkgsResult.packages, configPackages);
647
+ const pkgsToProcess = await this.processPackages(pkgsResult.packages, cliTargets);
594
648
  if (pkgsToProcess.length === 0) {
595
- log("info", "No packages to process");
649
+ log("info", "No packages to process based on changes and targets");
596
650
  return;
597
651
  }
598
652
  const formattedCommitMessage = commitMessage;
@@ -605,7 +659,7 @@ var VersionEngine = class {
605
659
  dryRun: this.config.dryRun
606
660
  });
607
661
  if (!this.config.dryRun) {
608
- log("success", "Created version commit");
662
+ log("success", `Created version commit for ${pkgsToProcess.length} package(s)`);
609
663
  }
610
664
  } catch (error) {
611
665
  log("error", "Failed to create version commit");
@@ -616,63 +670,49 @@ var VersionEngine = class {
616
670
  };
617
671
 
618
672
  // 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
- `);
673
+ async function run() {
674
+ printFiglet();
675
+ const program = new import_commander.Command();
676
+ program.name("package-versioner").description(
677
+ "Automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies."
678
+ ).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(
679
+ "--prerelease <identifier>",
680
+ "Create a prerelease version with the specified identifier"
681
+ ).option(
682
+ "-t, --target <targets>",
683
+ "Comma-separated list of package names to target (only for async strategy)"
684
+ ).parse(process.argv);
685
+ const options = program.opts();
650
686
  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;
687
+ const config = await loadConfig(options.config);
688
+ log("info", `Loaded configuration from ${options.config || "version.config.json"}`);
689
+ if (options.dryRun) config.dryRun = true;
690
+ if (options.synced) config.synced = true;
691
+ if (options.bump) config.forceType = options.bump;
659
692
  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;
693
+ config.prereleaseIdentifier = options.prerelease === true ? "rc" : options.prerelease;
694
+ const cliTargets = options.target ? options.target.split(",").map((t) => t.trim()) : [];
665
695
  const engine = new VersionEngine(config);
666
696
  if (config.synced) {
697
+ log("info", "Using synced versioning strategy.");
667
698
  await engine.syncedStrategy();
668
- } else if (options.bump && options.target) {
699
+ } else if (config.packages && config.packages.length === 1) {
700
+ log("info", "Using single package versioning strategy.");
701
+ if (cliTargets.length > 0) {
702
+ log("warning", "--target flag is ignored for single package strategy.");
703
+ }
669
704
  await engine.singleStrategy();
670
705
  } else {
671
- await engine.asyncStrategy();
706
+ log("info", "Using async versioning strategy.");
707
+ if (cliTargets.length > 0) {
708
+ log("info", `Targeting specific packages: ${cliTargets.join(", ")}`);
709
+ }
710
+ await engine.asyncStrategy(cliTargets);
672
711
  }
673
- } catch (err) {
674
- log("error", `${err instanceof Error ? err.message : String(err)}`);
675
- (0, import_node_process4.exit)(1);
712
+ log("success", "Versioning process completed.");
713
+ } catch (error) {
714
+ log("error", error instanceof Error ? error.message : String(error));
715
+ process.exit(1);
676
716
  }
677
- });
678
- program.parse();
717
+ }
718
+ 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.0.2",
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,7 +194,7 @@ 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()}`;
197
+ const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
198
198
  await createGitTag({
199
199
  tag: nextTag,
200
200
  message: tagMessage
@@ -233,6 +233,25 @@ function getCurrentBranch() {
233
233
  }
234
234
 
235
235
  // src/utils.ts
236
+ function printFiglet() {
237
+ const font = "Standard";
238
+ figlet.text(package_default.name, { font }, (err, data) => {
239
+ if (err) {
240
+ log("warning", "Could not print figlet banner: Figlet error");
241
+ console.error(err);
242
+ return;
243
+ }
244
+ if (data) {
245
+ const figletText = data;
246
+ const versionText = `v${package_default.version}`;
247
+ process.stdout.write(`${chalk.hex("#FF1F57")(figletText)}
248
+ `);
249
+ process.stdout.write(`${chalk.hex("#0096FF")(versionText)}
250
+
251
+ `);
252
+ }
253
+ });
254
+ }
236
255
  function log(status, message) {
237
256
  const statusColors = {
238
257
  info: chalk.blue("\u2139"),
@@ -257,10 +276,10 @@ async function getLatestTag() {
257
276
  }
258
277
  }
259
278
  function formatTag(options, props) {
260
- const { name: name2, synced, tagPrefix } = options;
279
+ const { name, synced, tagPrefix } = options;
261
280
  const { version } = props;
262
- if (!synced && name2) {
263
- return `${tagPrefix ? tagPrefix : ""}${name2}@${version}`;
281
+ if (!synced && name) {
282
+ return `${tagPrefix ? tagPrefix : ""}${name}@${version}`;
264
283
  }
265
284
  return `${tagPrefix ? tagPrefix : "v"}${version}`;
266
285
  }
@@ -273,7 +292,7 @@ function createTemplateString(template, data) {
273
292
  function formatCommitMessage(template, version) {
274
293
  return createTemplateString(template, { version });
275
294
  }
276
- function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
295
+ function updatePackageVersion({ path: path2, version, name, dryRun }) {
277
296
  try {
278
297
  const pkgPath = `${path2}/package.json`;
279
298
  const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
@@ -281,32 +300,36 @@ function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
281
300
  if (!dryRun) {
282
301
  fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
283
302
  `);
284
- log("success", `${name2}: ${version}`);
303
+ log("success", `${name}: ${version}`);
285
304
  } else {
286
- log("info", `[DRY RUN] Would update ${name2} package.json to version ${version}`);
305
+ log("info", `[DRY RUN] Would update ${name} package.json to version ${version}`);
287
306
  }
288
307
  } catch (error) {
289
- log("error", `Failed to update ${name2} to version ${version}`);
308
+ log("error", `Failed to update ${name} to version ${version}`);
290
309
  console.error(error);
291
310
  }
292
311
  }
293
312
 
294
313
  // src/versionEngine.ts
295
- import fs3 from "fs";
296
- import path from "path";
297
- import { cwd as cwd3, exit } from "process";
314
+ import fs3 from "node:fs";
315
+ import path from "node:path";
316
+ import { cwd as cwd3, exit } from "node:process";
298
317
  import { getPackagesSync } from "@manypkg/get-packages";
299
318
  import { Bumper } from "conventional-recommended-bump";
300
319
  import semver from "semver";
301
320
  var VersionEngine = class {
321
+ config;
302
322
  constructor(config) {
303
323
  this.config = config;
304
324
  }
325
+ /**
326
+ * Calculate the next version based on options
327
+ */
305
328
  async calculateVersion(options) {
306
- const { latestTag, type, path: path2, name: name2, branchPattern, prereleaseIdentifier } = options;
329
+ const { latestTag, type, path: path2, name, branchPattern, prereleaseIdentifier } = options;
307
330
  const originalPrefix = this.config.tagPrefix || "";
308
331
  const initialVersion = prereleaseIdentifier ? `0.0.1-${prereleaseIdentifier}` : "0.0.1";
309
- const tagSearchPattern = name2 ? originalPrefix ? `${originalPrefix}${name2}@` : `${name2}@` : originalPrefix ? `${originalPrefix}v` : "v";
332
+ const tagSearchPattern = name ? originalPrefix ? `${originalPrefix}${name}@` : `${name}@` : originalPrefix ? `${originalPrefix}v` : "v";
310
333
  let determinedReleaseType = type || null;
311
334
  if (determinedReleaseType) {
312
335
  if (!latestTag) {
@@ -347,21 +370,21 @@ var VersionEngine = class {
347
370
  if (commitsLength === 0) {
348
371
  log(
349
372
  "info",
350
- `No new commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
373
+ `No new commits found for ${name || "project"} since ${latestTag}, skipping version bump`
351
374
  );
352
375
  return "";
353
376
  }
354
377
  if (!releaseTypeFromCommits) {
355
378
  log(
356
379
  "info",
357
- `No relevant commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
380
+ `No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`
358
381
  );
359
382
  return "";
360
383
  }
361
384
  const currentVersion = semver.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
362
385
  return semver.inc(currentVersion, releaseTypeFromCommits, prereleaseIdentifier) || "";
363
386
  } catch (error) {
364
- log("error", `Failed to calculate version for ${name2 || "project"}`);
387
+ log("error", `Failed to calculate version for ${name || "project"}`);
365
388
  console.error(error);
366
389
  if (error instanceof Error && error.message.includes("No names found")) {
367
390
  log("info", "No tags found, proceeding with initial version calculation (if applicable).");
@@ -370,30 +393,44 @@ var VersionEngine = class {
370
393
  return "";
371
394
  }
372
395
  }
373
- async processPackages(packages = [], configPackages = []) {
374
- const { tagPrefix } = this.config;
375
- const pkgsResult = packages.length ? { packages } : getPackagesSync(cwd3());
396
+ /**
397
+ * Process packages based on discovery, skip list, and optional target list.
398
+ * Returns a list of package.json file paths that were updated (or would be in dry run).
399
+ */
400
+ async processPackages(discoveredPackages = [], targets = []) {
401
+ const { tagPrefix, skip } = this.config;
376
402
  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)) {
403
+ const pkgsToConsider = discoveredPackages.filter((pkg) => {
404
+ if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
405
+ log("info", `Skipping package ${pkg.packageJson.name} based on config skip list.`);
380
406
  return false;
381
407
  }
382
- return configPackages.length === 0 || configPackages.includes(pkg.packageJson.name);
408
+ if (targets.length > 0) {
409
+ const isTargeted = targets.includes(pkg.packageJson.name);
410
+ if (!isTargeted) {
411
+ }
412
+ return isTargeted;
413
+ }
414
+ return true;
383
415
  });
384
- for (const pkg of selectedPackages) {
385
- const name2 = pkg.packageJson.name;
416
+ log("info", `Found ${pkgsToConsider.length} package(s) to process after filtering.`);
417
+ for (const pkg of pkgsToConsider) {
418
+ const name = pkg.packageJson.name;
386
419
  const pkgPath = pkg.dir;
387
420
  const prefix = formatTagPrefix(tagPrefix);
388
421
  const latestTag = await getLatestTag();
389
422
  const nextVersion = await this.calculateVersion({
390
423
  latestTag,
424
+ // This might need refinement for async based on package-specific tags
391
425
  tagPrefix: prefix,
392
426
  path: pkgPath,
393
- name: name2,
427
+ name,
428
+ // Pass name for potential package-specific tag lookups
394
429
  branchPattern: this.config.branchPattern,
395
430
  baseBranch: this.config.baseBranch,
396
- prereleaseIdentifier: this.config.prereleaseIdentifier
431
+ prereleaseIdentifier: this.config.prereleaseIdentifier,
432
+ type: this.config.forceType
433
+ // Pass forced type if provided
397
434
  });
398
435
  if (!nextVersion) {
399
436
  continue;
@@ -401,13 +438,16 @@ var VersionEngine = class {
401
438
  updatePackageVersion({
402
439
  path: pkgPath,
403
440
  version: nextVersion,
404
- name: name2,
441
+ name,
405
442
  dryRun: this.config.dryRun
406
443
  });
407
444
  files.push(path.join(pkgPath, "package.json"));
408
445
  }
409
446
  return files;
410
447
  }
448
+ /**
449
+ * Create git commit and tag
450
+ */
411
451
  async createGitCommitAndTag(files, nextTag, commitMessage, dryRun) {
412
452
  try {
413
453
  await gitProcess({
@@ -426,6 +466,9 @@ var VersionEngine = class {
426
466
  exit(1);
427
467
  }
428
468
  }
469
+ /**
470
+ * Synced versioning strategy (all packages get the same version)
471
+ */
429
472
  async syncedStrategy() {
430
473
  var _a;
431
474
  const {
@@ -494,6 +537,9 @@ var VersionEngine = class {
494
537
  const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
495
538
  await this.createGitCommitAndTag(files, nextTag, formattedCommitMessage, this.config.dryRun);
496
539
  }
540
+ /**
541
+ * Single package versioning strategy
542
+ */
497
543
  async singleStrategy() {
498
544
  const {
499
545
  packages: configPackages,
@@ -553,11 +599,15 @@ var VersionEngine = class {
553
599
  this.config.dryRun
554
600
  );
555
601
  }
556
- async asyncStrategy() {
602
+ /**
603
+ * Async versioning strategy (each package gets its own version)
604
+ */
605
+ async asyncStrategy(cliTargets = []) {
557
606
  const {
558
- packages: configPackages,
559
607
  commitMessage = "chore(release): ${version}",
608
+ // Align with test expectations
560
609
  skipHooks
610
+ // Add skipHooks here
561
611
  } = this.config;
562
612
  let pkgsResult;
563
613
  try {
@@ -571,9 +621,9 @@ var VersionEngine = class {
571
621
  exit(1);
572
622
  return;
573
623
  }
574
- const pkgsToProcess = await this.processPackages(pkgsResult.packages, configPackages);
624
+ const pkgsToProcess = await this.processPackages(pkgsResult.packages, cliTargets);
575
625
  if (pkgsToProcess.length === 0) {
576
- log("info", "No packages to process");
626
+ log("info", "No packages to process based on changes and targets");
577
627
  return;
578
628
  }
579
629
  const formattedCommitMessage = commitMessage;
@@ -586,7 +636,7 @@ var VersionEngine = class {
586
636
  dryRun: this.config.dryRun
587
637
  });
588
638
  if (!this.config.dryRun) {
589
- log("success", "Created version commit");
639
+ log("success", `Created version commit for ${pkgsToProcess.length} package(s)`);
590
640
  }
591
641
  } catch (error) {
592
642
  log("error", "Failed to create version commit");
@@ -597,63 +647,49 @@ var VersionEngine = class {
597
647
  };
598
648
 
599
649
  // 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
- `);
650
+ async function run() {
651
+ printFiglet();
652
+ const program = new Command();
653
+ program.name("package-versioner").description(
654
+ "Automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies."
655
+ ).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(
656
+ "--prerelease <identifier>",
657
+ "Create a prerelease version with the specified identifier"
658
+ ).option(
659
+ "-t, --target <targets>",
660
+ "Comma-separated list of package names to target (only for async strategy)"
661
+ ).parse(process.argv);
662
+ const options = program.opts();
631
663
  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;
664
+ const config = await loadConfig(options.config);
665
+ log("info", `Loaded configuration from ${options.config || "version.config.json"}`);
666
+ if (options.dryRun) config.dryRun = true;
667
+ if (options.synced) config.synced = true;
668
+ if (options.bump) config.forceType = options.bump;
640
669
  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;
670
+ config.prereleaseIdentifier = options.prerelease === true ? "rc" : options.prerelease;
671
+ const cliTargets = options.target ? options.target.split(",").map((t) => t.trim()) : [];
646
672
  const engine = new VersionEngine(config);
647
673
  if (config.synced) {
674
+ log("info", "Using synced versioning strategy.");
648
675
  await engine.syncedStrategy();
649
- } else if (options.bump && options.target) {
676
+ } else if (config.packages && config.packages.length === 1) {
677
+ log("info", "Using single package versioning strategy.");
678
+ if (cliTargets.length > 0) {
679
+ log("warning", "--target flag is ignored for single package strategy.");
680
+ }
650
681
  await engine.singleStrategy();
651
682
  } else {
652
- await engine.asyncStrategy();
683
+ log("info", "Using async versioning strategy.");
684
+ if (cliTargets.length > 0) {
685
+ log("info", `Targeting specific packages: ${cliTargets.join(", ")}`);
686
+ }
687
+ await engine.asyncStrategy(cliTargets);
653
688
  }
654
- } catch (err) {
655
- log("error", `${err instanceof Error ? err.message : String(err)}`);
656
- exit2(1);
689
+ log("success", "Versioning process completed.");
690
+ } catch (error) {
691
+ log("error", error instanceof Error ? error.message : String(error));
692
+ process.exit(1);
657
693
  }
658
- });
659
- program.parse();
694
+ }
695
+ 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.0",
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
  },