package-versioner 0.1.0 → 0.2.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
@@ -45,7 +45,7 @@ npx package-versioner -t @scope/package-a,@scope/package-b
45
45
  npx package-versioner --dry-run
46
46
  ```
47
47
 
48
-
48
+ **Note on Targeting:** Using the `-t` flag creates package-specific tags (e.g., `@scope/package-a@1.2.0`) but *not* a global tag (like `v1.2.0`). If needed, create the global tag manually in your CI/CD script after this command.
49
49
 
50
50
  ## Configuration
51
51
 
package/dist/index.cjs CHANGED
@@ -58,7 +58,7 @@ var import_figlet = __toESM(require("figlet"), 1);
58
58
  var package_default = {
59
59
  name: "package-versioner",
60
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.",
61
- version: "0.0.2",
61
+ version: "0.1.1",
62
62
  type: "module",
63
63
  main: "./dist/index.js",
64
64
  module: "./dist/index.mjs",
@@ -72,17 +72,9 @@ var package_default = {
72
72
  url: "https://github.com/goosewobbler/package-versioner",
73
73
  homepage: "https://github.com/goosewobbler/package-versioner"
74
74
  },
75
- keywords: [
76
- "version",
77
- "semver",
78
- "git",
79
- "package"
80
- ],
75
+ keywords: ["version", "semver", "git", "package"],
81
76
  license: "MIT",
82
- files: [
83
- "dist/**",
84
- "package-versioner.schema.json"
85
- ],
77
+ files: ["dist/**", "docs/**", "package-versioner.schema.json"],
86
78
  bin: {
87
79
  "package-versioner": "./dist/index.js"
88
80
  },
@@ -100,10 +92,7 @@ var package_default = {
100
92
  prepare: "husky"
101
93
  },
102
94
  "lint-staged": {
103
- "*.{js,ts,jsx,tsx}": [
104
- "biome check --apply",
105
- "biome format --write"
106
- ]
95
+ "*.{js,ts,jsx,tsx}": ["biome check --apply", "biome format --write"]
107
96
  },
108
97
  devDependencies: {
109
98
  "@biomejs/biome": "^1.9.4",
@@ -217,18 +206,22 @@ async function gitProcess({ files, nextTag, commitMessage, skipHooks, dryRun })
217
206
  message: commitMessage,
218
207
  skipHooks
219
208
  });
220
- const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
221
- await createGitTag({
222
- tag: nextTag,
223
- message: tagMessage
224
- });
209
+ if (nextTag) {
210
+ const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
211
+ await createGitTag({
212
+ tag: nextTag,
213
+ message: tagMessage
214
+ });
215
+ }
225
216
  } else {
226
217
  log("info", "[DRY RUN] Would add files:");
227
218
  for (const file of files) {
228
219
  log("info", ` - ${file}`);
229
220
  }
230
221
  log("info", `[DRY RUN] Would commit with message: "${commitMessage}"`);
231
- log("info", `[DRY RUN] Would create tag: ${nextTag}`);
222
+ if (nextTag) {
223
+ log("info", `[DRY RUN] Would create tag: ${nextTag}`);
224
+ }
232
225
  }
233
226
  } catch (err) {
234
227
  console.log(err);
@@ -421,7 +414,7 @@ var VersionEngine = class {
421
414
  * Returns a list of package.json file paths that were updated (or would be in dry run).
422
415
  */
423
416
  async processPackages(discoveredPackages = [], targets = []) {
424
- const { tagPrefix, skip } = this.config;
417
+ const { tagPrefix, skip, dryRun } = this.config;
425
418
  const files = [];
426
419
  const pkgsToConsider = discoveredPackages.filter((pkg) => {
427
420
  if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
@@ -444,16 +437,13 @@ var VersionEngine = class {
444
437
  const latestTag = await getLatestTag();
445
438
  const nextVersion = await this.calculateVersion({
446
439
  latestTag,
447
- // This might need refinement for async based on package-specific tags
448
440
  tagPrefix: prefix,
449
441
  path: pkgPath,
450
442
  name,
451
- // Pass name for potential package-specific tag lookups
452
443
  branchPattern: this.config.branchPattern,
453
444
  baseBranch: this.config.baseBranch,
454
445
  prereleaseIdentifier: this.config.prereleaseIdentifier,
455
446
  type: this.config.forceType
456
- // Pass forced type if provided
457
447
  });
458
448
  if (!nextVersion) {
459
449
  continue;
@@ -462,7 +452,7 @@ var VersionEngine = class {
462
452
  path: pkgPath,
463
453
  version: nextVersion,
464
454
  name,
465
- dryRun: this.config.dryRun
455
+ dryRun
466
456
  });
467
457
  files.push(import_node_path2.default.join(pkgPath, "package.json"));
468
458
  }
@@ -594,13 +584,19 @@ var VersionEngine = class {
594
584
  const pkgPath = pkg.dir;
595
585
  const prefix = formatTagPrefix(tagPrefix);
596
586
  const latestTag = await getLatestTag();
597
- const nextVersion = await this.calculateVersion({
598
- latestTag,
599
- tagPrefix: prefix,
600
- path: pkgPath,
601
- name: packageName
602
- });
603
- if (!nextVersion) {
587
+ let nextVersion = void 0;
588
+ try {
589
+ nextVersion = await this.calculateVersion({
590
+ latestTag,
591
+ tagPrefix: prefix,
592
+ path: pkgPath,
593
+ name: packageName
594
+ });
595
+ } catch (error) {
596
+ const errorMessage = error instanceof Error ? error.message : String(error);
597
+ log("error", `Failed to calculate version for ${packageName}: ${errorMessage}`);
598
+ }
599
+ if (nextVersion === void 0 || nextVersion === "") {
604
600
  log("info", `No version change needed for ${packageName}`);
605
601
  return;
606
602
  }
@@ -626,11 +622,14 @@ var VersionEngine = class {
626
622
  * Async versioning strategy (each package gets its own version)
627
623
  */
628
624
  async asyncStrategy(cliTargets = []) {
625
+ if (cliTargets.length > 0) {
626
+ await this.asyncTargetedStrategy(cliTargets);
627
+ return;
628
+ }
629
629
  const {
630
630
  commitMessage = "chore(release): ${version}",
631
631
  // Align with test expectations
632
632
  skipHooks
633
- // Add skipHooks here
634
633
  } = this.config;
635
634
  let pkgsResult;
636
635
  try {
@@ -654,6 +653,7 @@ var VersionEngine = class {
654
653
  await gitProcess({
655
654
  files: pkgsToProcess,
656
655
  nextTag: "",
656
+ // No tag for default async
657
657
  commitMessage: formattedCommitMessage,
658
658
  skipHooks,
659
659
  dryRun: this.config.dryRun
@@ -667,6 +667,121 @@ var VersionEngine = class {
667
667
  (0, import_node_process3.exit)(1);
668
668
  }
669
669
  }
670
+ // --- NEW METHOD for Async + Targeted ---
671
+ async asyncTargetedStrategy(cliTargets) {
672
+ var _a;
673
+ const {
674
+ tagPrefix,
675
+ skip,
676
+ dryRun,
677
+ skipHooks,
678
+ commitMessage: commitMessageTemplate
679
+ } = this.config;
680
+ const updatedPackagesInfo = [];
681
+ log("info", `Processing targeted packages: ${cliTargets.join(", ")}`);
682
+ let pkgsResult;
683
+ try {
684
+ pkgsResult = (0, import_get_packages.getPackagesSync)((0, import_node_process3.cwd)());
685
+ if (!pkgsResult || !pkgsResult.packages) {
686
+ throw new Error("Failed to get packages information");
687
+ }
688
+ } catch (error) {
689
+ const errorMessage = error instanceof Error ? error.message : String(error);
690
+ log("error", `Failed to get packages information: ${errorMessage}`);
691
+ (0, import_node_process3.exit)(1);
692
+ }
693
+ const pkgsToConsider = pkgsResult.packages.filter((pkg) => {
694
+ if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
695
+ log("info", `Skipping package ${pkg.packageJson.name} based on config skip list.`);
696
+ return false;
697
+ }
698
+ const isTargeted = cliTargets.includes(pkg.packageJson.name);
699
+ if (!isTargeted) {
700
+ }
701
+ return isTargeted;
702
+ });
703
+ log("info", `Found ${pkgsToConsider.length} targeted package(s) to process after filtering.`);
704
+ if (pkgsToConsider.length === 0) {
705
+ log("info", "No matching targeted packages found to process.");
706
+ return;
707
+ }
708
+ for (const pkg of pkgsToConsider) {
709
+ const name = pkg.packageJson.name;
710
+ const pkgPath = pkg.dir;
711
+ const prefix = formatTagPrefix(tagPrefix);
712
+ const latestTag = await getLatestTag();
713
+ const nextVersion = await this.calculateVersion({
714
+ latestTag,
715
+ tagPrefix: prefix,
716
+ path: pkgPath,
717
+ name,
718
+ branchPattern: this.config.branchPattern,
719
+ baseBranch: this.config.baseBranch,
720
+ prereleaseIdentifier: this.config.prereleaseIdentifier,
721
+ type: this.config.forceType
722
+ });
723
+ if (!nextVersion) {
724
+ continue;
725
+ }
726
+ updatePackageVersion({
727
+ path: pkgPath,
728
+ version: nextVersion,
729
+ name,
730
+ dryRun
731
+ });
732
+ const packageTag = formatTag(
733
+ { synced: false, name, tagPrefix },
734
+ { version: nextVersion, tagPrefix }
735
+ );
736
+ const tagMessage = `chore(release): ${name} ${nextVersion}`;
737
+ if (!dryRun) {
738
+ try {
739
+ await createGitTag({ tag: packageTag, message: tagMessage });
740
+ log("success", `Created tag: ${packageTag}`);
741
+ } catch (tagError) {
742
+ log(
743
+ "error",
744
+ `Failed to create tag ${packageTag} for ${name}: ${tagError.message}`
745
+ );
746
+ log("error", tagError.stack || "No stack trace available");
747
+ }
748
+ } else {
749
+ log("info", `[DRY RUN] Would create tag: ${packageTag}`);
750
+ }
751
+ updatedPackagesInfo.push({ name, version: nextVersion, path: pkgPath });
752
+ }
753
+ if (updatedPackagesInfo.length === 0) {
754
+ log("info", "No targeted packages required a version update.");
755
+ return;
756
+ }
757
+ const filesToCommit = updatedPackagesInfo.map((info) => import_node_path2.default.join(info.path, "package.json"));
758
+ const packageNames = updatedPackagesInfo.map((p) => p.name).join(", ");
759
+ const representativeVersion = ((_a = updatedPackagesInfo[0]) == null ? void 0 : _a.version) || "multiple";
760
+ let commitMessage = commitMessageTemplate || "chore(release): publish packages";
761
+ if (updatedPackagesInfo.length === 1 && commitMessage.includes("${version}")) {
762
+ commitMessage = formatCommitMessage(commitMessage, representativeVersion);
763
+ } else {
764
+ commitMessage = `chore(release): ${packageNames} ${representativeVersion}`;
765
+ }
766
+ commitMessage += " [skip-ci]";
767
+ if (!dryRun) {
768
+ try {
769
+ await gitAdd(filesToCommit);
770
+ await gitCommit({ message: commitMessage, skipHooks });
771
+ log("success", `Created commit for targeted release: ${packageNames}`);
772
+ } catch (commitError) {
773
+ log("error", "Failed to create commit for targeted release.");
774
+ console.error(commitError);
775
+ (0, import_node_process3.exit)(1);
776
+ }
777
+ } else {
778
+ log("info", "[DRY RUN] Would add files:");
779
+ for (const file of filesToCommit) {
780
+ log("info", ` - ${file}`);
781
+ }
782
+ log("info", `[DRY RUN] Would commit with message: "${commitMessage}"`);
783
+ }
784
+ }
670
785
  };
671
786
 
672
787
  // src/index.ts
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import figlet from "figlet";
35
35
  var package_default = {
36
36
  name: "package-versioner",
37
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.",
38
- version: "0.0.2",
38
+ version: "0.1.1",
39
39
  type: "module",
40
40
  main: "./dist/index.js",
41
41
  module: "./dist/index.mjs",
@@ -49,17 +49,9 @@ var package_default = {
49
49
  url: "https://github.com/goosewobbler/package-versioner",
50
50
  homepage: "https://github.com/goosewobbler/package-versioner"
51
51
  },
52
- keywords: [
53
- "version",
54
- "semver",
55
- "git",
56
- "package"
57
- ],
52
+ keywords: ["version", "semver", "git", "package"],
58
53
  license: "MIT",
59
- files: [
60
- "dist/**",
61
- "package-versioner.schema.json"
62
- ],
54
+ files: ["dist/**", "docs/**", "package-versioner.schema.json"],
63
55
  bin: {
64
56
  "package-versioner": "./dist/index.js"
65
57
  },
@@ -77,10 +69,7 @@ var package_default = {
77
69
  prepare: "husky"
78
70
  },
79
71
  "lint-staged": {
80
- "*.{js,ts,jsx,tsx}": [
81
- "biome check --apply",
82
- "biome format --write"
83
- ]
72
+ "*.{js,ts,jsx,tsx}": ["biome check --apply", "biome format --write"]
84
73
  },
85
74
  devDependencies: {
86
75
  "@biomejs/biome": "^1.9.4",
@@ -194,18 +183,22 @@ async function gitProcess({ files, nextTag, commitMessage, skipHooks, dryRun })
194
183
  message: commitMessage,
195
184
  skipHooks
196
185
  });
197
- const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
198
- await createGitTag({
199
- tag: nextTag,
200
- message: tagMessage
201
- });
186
+ if (nextTag) {
187
+ const tagMessage = `New Version ${nextTag} generated at ${(/* @__PURE__ */ new Date()).toISOString()}`;
188
+ await createGitTag({
189
+ tag: nextTag,
190
+ message: tagMessage
191
+ });
192
+ }
202
193
  } else {
203
194
  log("info", "[DRY RUN] Would add files:");
204
195
  for (const file of files) {
205
196
  log("info", ` - ${file}`);
206
197
  }
207
198
  log("info", `[DRY RUN] Would commit with message: "${commitMessage}"`);
208
- log("info", `[DRY RUN] Would create tag: ${nextTag}`);
199
+ if (nextTag) {
200
+ log("info", `[DRY RUN] Would create tag: ${nextTag}`);
201
+ }
209
202
  }
210
203
  } catch (err) {
211
204
  console.log(err);
@@ -398,7 +391,7 @@ var VersionEngine = class {
398
391
  * Returns a list of package.json file paths that were updated (or would be in dry run).
399
392
  */
400
393
  async processPackages(discoveredPackages = [], targets = []) {
401
- const { tagPrefix, skip } = this.config;
394
+ const { tagPrefix, skip, dryRun } = this.config;
402
395
  const files = [];
403
396
  const pkgsToConsider = discoveredPackages.filter((pkg) => {
404
397
  if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
@@ -421,16 +414,13 @@ var VersionEngine = class {
421
414
  const latestTag = await getLatestTag();
422
415
  const nextVersion = await this.calculateVersion({
423
416
  latestTag,
424
- // This might need refinement for async based on package-specific tags
425
417
  tagPrefix: prefix,
426
418
  path: pkgPath,
427
419
  name,
428
- // Pass name for potential package-specific tag lookups
429
420
  branchPattern: this.config.branchPattern,
430
421
  baseBranch: this.config.baseBranch,
431
422
  prereleaseIdentifier: this.config.prereleaseIdentifier,
432
423
  type: this.config.forceType
433
- // Pass forced type if provided
434
424
  });
435
425
  if (!nextVersion) {
436
426
  continue;
@@ -439,7 +429,7 @@ var VersionEngine = class {
439
429
  path: pkgPath,
440
430
  version: nextVersion,
441
431
  name,
442
- dryRun: this.config.dryRun
432
+ dryRun
443
433
  });
444
434
  files.push(path.join(pkgPath, "package.json"));
445
435
  }
@@ -571,13 +561,19 @@ var VersionEngine = class {
571
561
  const pkgPath = pkg.dir;
572
562
  const prefix = formatTagPrefix(tagPrefix);
573
563
  const latestTag = await getLatestTag();
574
- const nextVersion = await this.calculateVersion({
575
- latestTag,
576
- tagPrefix: prefix,
577
- path: pkgPath,
578
- name: packageName
579
- });
580
- if (!nextVersion) {
564
+ let nextVersion = void 0;
565
+ try {
566
+ nextVersion = await this.calculateVersion({
567
+ latestTag,
568
+ tagPrefix: prefix,
569
+ path: pkgPath,
570
+ name: packageName
571
+ });
572
+ } catch (error) {
573
+ const errorMessage = error instanceof Error ? error.message : String(error);
574
+ log("error", `Failed to calculate version for ${packageName}: ${errorMessage}`);
575
+ }
576
+ if (nextVersion === void 0 || nextVersion === "") {
581
577
  log("info", `No version change needed for ${packageName}`);
582
578
  return;
583
579
  }
@@ -603,11 +599,14 @@ var VersionEngine = class {
603
599
  * Async versioning strategy (each package gets its own version)
604
600
  */
605
601
  async asyncStrategy(cliTargets = []) {
602
+ if (cliTargets.length > 0) {
603
+ await this.asyncTargetedStrategy(cliTargets);
604
+ return;
605
+ }
606
606
  const {
607
607
  commitMessage = "chore(release): ${version}",
608
608
  // Align with test expectations
609
609
  skipHooks
610
- // Add skipHooks here
611
610
  } = this.config;
612
611
  let pkgsResult;
613
612
  try {
@@ -631,6 +630,7 @@ var VersionEngine = class {
631
630
  await gitProcess({
632
631
  files: pkgsToProcess,
633
632
  nextTag: "",
633
+ // No tag for default async
634
634
  commitMessage: formattedCommitMessage,
635
635
  skipHooks,
636
636
  dryRun: this.config.dryRun
@@ -644,6 +644,121 @@ var VersionEngine = class {
644
644
  exit(1);
645
645
  }
646
646
  }
647
+ // --- NEW METHOD for Async + Targeted ---
648
+ async asyncTargetedStrategy(cliTargets) {
649
+ var _a;
650
+ const {
651
+ tagPrefix,
652
+ skip,
653
+ dryRun,
654
+ skipHooks,
655
+ commitMessage: commitMessageTemplate
656
+ } = this.config;
657
+ const updatedPackagesInfo = [];
658
+ log("info", `Processing targeted packages: ${cliTargets.join(", ")}`);
659
+ let pkgsResult;
660
+ try {
661
+ pkgsResult = getPackagesSync(cwd3());
662
+ if (!pkgsResult || !pkgsResult.packages) {
663
+ throw new Error("Failed to get packages information");
664
+ }
665
+ } catch (error) {
666
+ const errorMessage = error instanceof Error ? error.message : String(error);
667
+ log("error", `Failed to get packages information: ${errorMessage}`);
668
+ exit(1);
669
+ }
670
+ const pkgsToConsider = pkgsResult.packages.filter((pkg) => {
671
+ if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
672
+ log("info", `Skipping package ${pkg.packageJson.name} based on config skip list.`);
673
+ return false;
674
+ }
675
+ const isTargeted = cliTargets.includes(pkg.packageJson.name);
676
+ if (!isTargeted) {
677
+ }
678
+ return isTargeted;
679
+ });
680
+ log("info", `Found ${pkgsToConsider.length} targeted package(s) to process after filtering.`);
681
+ if (pkgsToConsider.length === 0) {
682
+ log("info", "No matching targeted packages found to process.");
683
+ return;
684
+ }
685
+ for (const pkg of pkgsToConsider) {
686
+ const name = pkg.packageJson.name;
687
+ const pkgPath = pkg.dir;
688
+ const prefix = formatTagPrefix(tagPrefix);
689
+ const latestTag = await getLatestTag();
690
+ const nextVersion = await this.calculateVersion({
691
+ latestTag,
692
+ tagPrefix: prefix,
693
+ path: pkgPath,
694
+ name,
695
+ branchPattern: this.config.branchPattern,
696
+ baseBranch: this.config.baseBranch,
697
+ prereleaseIdentifier: this.config.prereleaseIdentifier,
698
+ type: this.config.forceType
699
+ });
700
+ if (!nextVersion) {
701
+ continue;
702
+ }
703
+ updatePackageVersion({
704
+ path: pkgPath,
705
+ version: nextVersion,
706
+ name,
707
+ dryRun
708
+ });
709
+ const packageTag = formatTag(
710
+ { synced: false, name, tagPrefix },
711
+ { version: nextVersion, tagPrefix }
712
+ );
713
+ const tagMessage = `chore(release): ${name} ${nextVersion}`;
714
+ if (!dryRun) {
715
+ try {
716
+ await createGitTag({ tag: packageTag, message: tagMessage });
717
+ log("success", `Created tag: ${packageTag}`);
718
+ } catch (tagError) {
719
+ log(
720
+ "error",
721
+ `Failed to create tag ${packageTag} for ${name}: ${tagError.message}`
722
+ );
723
+ log("error", tagError.stack || "No stack trace available");
724
+ }
725
+ } else {
726
+ log("info", `[DRY RUN] Would create tag: ${packageTag}`);
727
+ }
728
+ updatedPackagesInfo.push({ name, version: nextVersion, path: pkgPath });
729
+ }
730
+ if (updatedPackagesInfo.length === 0) {
731
+ log("info", "No targeted packages required a version update.");
732
+ return;
733
+ }
734
+ const filesToCommit = updatedPackagesInfo.map((info) => path.join(info.path, "package.json"));
735
+ const packageNames = updatedPackagesInfo.map((p) => p.name).join(", ");
736
+ const representativeVersion = ((_a = updatedPackagesInfo[0]) == null ? void 0 : _a.version) || "multiple";
737
+ let commitMessage = commitMessageTemplate || "chore(release): publish packages";
738
+ if (updatedPackagesInfo.length === 1 && commitMessage.includes("${version}")) {
739
+ commitMessage = formatCommitMessage(commitMessage, representativeVersion);
740
+ } else {
741
+ commitMessage = `chore(release): ${packageNames} ${representativeVersion}`;
742
+ }
743
+ commitMessage += " [skip-ci]";
744
+ if (!dryRun) {
745
+ try {
746
+ await gitAdd(filesToCommit);
747
+ await gitCommit({ message: commitMessage, skipHooks });
748
+ log("success", `Created commit for targeted release: ${packageNames}`);
749
+ } catch (commitError) {
750
+ log("error", "Failed to create commit for targeted release.");
751
+ console.error(commitError);
752
+ exit(1);
753
+ }
754
+ } else {
755
+ log("info", "[DRY RUN] Would add files:");
756
+ for (const file of filesToCommit) {
757
+ log("info", ` - ${file}`);
758
+ }
759
+ log("info", `[DRY RUN] Would commit with message: "${commitMessage}"`);
760
+ }
761
+ }
647
762
  };
648
763
 
649
764
  // src/index.ts
@@ -0,0 +1,96 @@
1
+ # Versioning Strategies and Concepts
2
+
3
+ `package-versioner` offers flexible ways to determine the next version for your project based on its history and your configuration.
4
+
5
+ ## How the Next Version is Calculated
6
+
7
+ There are two primary methods the tool uses to decide the version bump (e.g., patch, minor, major), configured via the `versionStrategy` option in `version.config.json`:
8
+
9
+ ### 1. Conventional Commits (`versionStrategy: "conventional"`)
10
+
11
+ This is the default strategy. `package-versioner` analyzes Git commit messages since the last Git tag that follows semver patterns. It uses the [conventional-commits](https://www.conventionalcommits.org/) specification to determine the bump:
12
+
13
+ - **Patch Bump (e.g., 1.2.3 -> 1.2.4):** Triggered by `fix:` commit types.
14
+ - **Minor Bump (e.g., 1.2.3 -> 1.3.0):** Triggered by `feat:` commit types.
15
+ - **Major Bump (e.g., 1.2.3 -> 2.0.0):** Triggered by commits with `BREAKING CHANGE:` in the footer or `feat!:`, `fix!:` etc. in the header.
16
+
17
+ The specific preset used for analysis (e.g., "angular", "conventional") can be set using the `preset` option in `version.config.json`.
18
+
19
+ **Format:** `<type>(<scope>): <subject>`
20
+
21
+ `<scope>` is optional.
22
+
23
+ **Example Commit Types:**
24
+
25
+ - `feat:` (new feature for the user)
26
+ - `fix:` (bug fix for the user)
27
+ - `docs:` (changes to the documentation)
28
+ - `style:` (formatting, missing semi-colons, etc; no production code change)
29
+ - `refactor:` (refactoring production code, e.g. renaming a variable)
30
+ - `test:` (adding missing tests, refactoring tests; no production code change)
31
+ - `chore:` (updating build tasks etc; no production code change)
32
+
33
+ **References:**
34
+
35
+ - [https://www.conventionalcommits.org/](https://www.conventionalcommits.org/)
36
+ - [https://github.com/conventional-changelog/conventional-changelog](https://github.com/conventional-changelog/conventional-changelog)
37
+
38
+ ### 2. Branch Pattern (`versionStrategy: "branchPattern"`)
39
+
40
+ This strategy uses the name of the current Git branch (or the most recently merged branch matching a pattern, if applicable) to determine the version bump.
41
+
42
+ You define patterns in the `branchPattern` array in `version.config.json`. Each pattern is a string like `"prefix:bumptype"`.
43
+
44
+ **Example `version.config.json`:**
45
+
46
+ ```json
47
+ {
48
+ "versionStrategy": "branchPattern",
49
+ "branchPattern": [
50
+ "feature:minor",
51
+ "hotfix:patch",
52
+ "fix:patch",
53
+ "release:major"
54
+ ],
55
+ "baseBranch": "main"
56
+ }
57
+ ```
58
+
59
+ **How it works:**
60
+
61
+ 1. The tool checks the current branch name.
62
+ 2. It might also look for the most recently merged branch into `baseBranch` that matches any pattern in `branchPattern`.
63
+ 3. It compares the relevant branch name (current or last merged) against the prefixes in `branchPattern`.
64
+ 4. If a match is found (e.g., current branch is `feature/add-login`), it applies the corresponding bump type (`minor` in this case).
65
+
66
+ This allows you to enforce version bumps based on your branching workflow (e.g., all branches starting with `feature/` result in a minor bump).
67
+
68
+ ## Monorepo Versioning Modes
69
+
70
+ While primarily used for single packages now, `package-versioner` retains options for monorepo workflows, controlled mainly by the `synced` flag in `version.config.json`.
71
+
72
+ ### Synced Mode (`synced: true`)
73
+
74
+ This is the default if the `synced` flag is present and true.
75
+
76
+ - **Behavior:** The tool calculates **one** version bump based on the overall history (or branch pattern). This single new version is applied to **all** packages within the repository (or just the root `package.json` if not a structured monorepo). A single Git tag is created (e.g., `v1.2.3`).
77
+ - **Use Case:** Suitable for monorepos where all packages are tightly coupled and released together with the same version number. Also the effective mode for single-package repositories.
78
+
79
+ ### Async Mode (`synced: false`)
80
+
81
+ *(Note: This mode relies heavily on monorepo tooling and structure, like `pnpm workspaces` and correctly configured package dependencies.)*
82
+
83
+ - **Behavior (Default - No `-t` flag):** The tool analyzes commits to determine which specific packages within the monorepo have changed since the last relevant commit/tag.
84
+ - It calculates an appropriate version bump **independently for each changed package** based on the commits affecting that package.
85
+ - Only the `package.json` files of the changed packages are updated.
86
+ - A **single commit** is created grouping all the version bumps, using the commit message template. **No Git tags are created** in this mode.
87
+ - **Use Case:** Suitable for monorepos where packages are versioned independently, but a single commit represents the batch of updates for traceability.
88
+
89
+ - **Behavior (Targeted - With `-t` flag):** When using the `-t, --target <targets>` flag:
90
+ - Only the specified packages (respecting the `skip` list) are considered for versioning.
91
+ - It calculates an appropriate version bump **independently for each targeted package** based on its commit history.
92
+ - The `package.json` file of each successfully updated targeted package is modified.
93
+ - An **individual Git tag** (e.g., `packageName@1.2.3`) is created **for each successfully updated package** immediately after its version is bumped.
94
+ - Finally, a **single commit** is created including all the updated `package.json` files, using a summary commit message (e.g., `chore(release): pkg-a, pkg-b 1.2.3 [skip-ci]`).
95
+ - **Important:** Only package-specific tags are created. The global tag (e.g., `v1.2.3`) is **not** automatically generated in this mode. If your release process (like GitHub Releases) depends on a global tag, you'll need to create it manually in your CI/CD script *after* `package-versioner` completes.
96
+ - **Use Case:** Releasing specific packages independently while still tagging each released package individually.
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.1.0",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -24,6 +24,7 @@
24
24
  "license": "MIT",
25
25
  "files": [
26
26
  "dist/**",
27
+ "docs/**",
27
28
  "package-versioner.schema.json"
28
29
  ],
29
30
  "bin": {