package-versioner 0.1.1 → 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.1.0",
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",
@@ -425,7 +414,7 @@ var VersionEngine = class {
425
414
  * Returns a list of package.json file paths that were updated (or would be in dry run).
426
415
  */
427
416
  async processPackages(discoveredPackages = [], targets = []) {
428
- const { tagPrefix, skip } = this.config;
417
+ const { tagPrefix, skip, dryRun } = this.config;
429
418
  const files = [];
430
419
  const pkgsToConsider = discoveredPackages.filter((pkg) => {
431
420
  if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
@@ -448,16 +437,13 @@ var VersionEngine = class {
448
437
  const latestTag = await getLatestTag();
449
438
  const nextVersion = await this.calculateVersion({
450
439
  latestTag,
451
- // This might need refinement for async based on package-specific tags
452
440
  tagPrefix: prefix,
453
441
  path: pkgPath,
454
442
  name,
455
- // Pass name for potential package-specific tag lookups
456
443
  branchPattern: this.config.branchPattern,
457
444
  baseBranch: this.config.baseBranch,
458
445
  prereleaseIdentifier: this.config.prereleaseIdentifier,
459
446
  type: this.config.forceType
460
- // Pass forced type if provided
461
447
  });
462
448
  if (!nextVersion) {
463
449
  continue;
@@ -466,7 +452,7 @@ var VersionEngine = class {
466
452
  path: pkgPath,
467
453
  version: nextVersion,
468
454
  name,
469
- dryRun: this.config.dryRun
455
+ dryRun
470
456
  });
471
457
  files.push(import_node_path2.default.join(pkgPath, "package.json"));
472
458
  }
@@ -598,13 +584,19 @@ var VersionEngine = class {
598
584
  const pkgPath = pkg.dir;
599
585
  const prefix = formatTagPrefix(tagPrefix);
600
586
  const latestTag = await getLatestTag();
601
- const nextVersion = await this.calculateVersion({
602
- latestTag,
603
- tagPrefix: prefix,
604
- path: pkgPath,
605
- name: packageName
606
- });
607
- 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 === "") {
608
600
  log("info", `No version change needed for ${packageName}`);
609
601
  return;
610
602
  }
@@ -630,11 +622,14 @@ var VersionEngine = class {
630
622
  * Async versioning strategy (each package gets its own version)
631
623
  */
632
624
  async asyncStrategy(cliTargets = []) {
625
+ if (cliTargets.length > 0) {
626
+ await this.asyncTargetedStrategy(cliTargets);
627
+ return;
628
+ }
633
629
  const {
634
630
  commitMessage = "chore(release): ${version}",
635
631
  // Align with test expectations
636
632
  skipHooks
637
- // Add skipHooks here
638
633
  } = this.config;
639
634
  let pkgsResult;
640
635
  try {
@@ -658,6 +653,7 @@ var VersionEngine = class {
658
653
  await gitProcess({
659
654
  files: pkgsToProcess,
660
655
  nextTag: "",
656
+ // No tag for default async
661
657
  commitMessage: formattedCommitMessage,
662
658
  skipHooks,
663
659
  dryRun: this.config.dryRun
@@ -671,6 +667,121 @@ var VersionEngine = class {
671
667
  (0, import_node_process3.exit)(1);
672
668
  }
673
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
+ }
674
785
  };
675
786
 
676
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.1.0",
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",
@@ -402,7 +391,7 @@ var VersionEngine = class {
402
391
  * Returns a list of package.json file paths that were updated (or would be in dry run).
403
392
  */
404
393
  async processPackages(discoveredPackages = [], targets = []) {
405
- const { tagPrefix, skip } = this.config;
394
+ const { tagPrefix, skip, dryRun } = this.config;
406
395
  const files = [];
407
396
  const pkgsToConsider = discoveredPackages.filter((pkg) => {
408
397
  if (skip == null ? void 0 : skip.includes(pkg.packageJson.name)) {
@@ -425,16 +414,13 @@ var VersionEngine = class {
425
414
  const latestTag = await getLatestTag();
426
415
  const nextVersion = await this.calculateVersion({
427
416
  latestTag,
428
- // This might need refinement for async based on package-specific tags
429
417
  tagPrefix: prefix,
430
418
  path: pkgPath,
431
419
  name,
432
- // Pass name for potential package-specific tag lookups
433
420
  branchPattern: this.config.branchPattern,
434
421
  baseBranch: this.config.baseBranch,
435
422
  prereleaseIdentifier: this.config.prereleaseIdentifier,
436
423
  type: this.config.forceType
437
- // Pass forced type if provided
438
424
  });
439
425
  if (!nextVersion) {
440
426
  continue;
@@ -443,7 +429,7 @@ var VersionEngine = class {
443
429
  path: pkgPath,
444
430
  version: nextVersion,
445
431
  name,
446
- dryRun: this.config.dryRun
432
+ dryRun
447
433
  });
448
434
  files.push(path.join(pkgPath, "package.json"));
449
435
  }
@@ -575,13 +561,19 @@ var VersionEngine = class {
575
561
  const pkgPath = pkg.dir;
576
562
  const prefix = formatTagPrefix(tagPrefix);
577
563
  const latestTag = await getLatestTag();
578
- const nextVersion = await this.calculateVersion({
579
- latestTag,
580
- tagPrefix: prefix,
581
- path: pkgPath,
582
- name: packageName
583
- });
584
- 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 === "") {
585
577
  log("info", `No version change needed for ${packageName}`);
586
578
  return;
587
579
  }
@@ -607,11 +599,14 @@ var VersionEngine = class {
607
599
  * Async versioning strategy (each package gets its own version)
608
600
  */
609
601
  async asyncStrategy(cliTargets = []) {
602
+ if (cliTargets.length > 0) {
603
+ await this.asyncTargetedStrategy(cliTargets);
604
+ return;
605
+ }
610
606
  const {
611
607
  commitMessage = "chore(release): ${version}",
612
608
  // Align with test expectations
613
609
  skipHooks
614
- // Add skipHooks here
615
610
  } = this.config;
616
611
  let pkgsResult;
617
612
  try {
@@ -635,6 +630,7 @@ var VersionEngine = class {
635
630
  await gitProcess({
636
631
  files: pkgsToProcess,
637
632
  nextTag: "",
633
+ // No tag for default async
638
634
  commitMessage: formattedCommitMessage,
639
635
  skipHooks,
640
636
  dryRun: this.config.dryRun
@@ -648,6 +644,121 @@ var VersionEngine = class {
648
644
  exit(1);
649
645
  }
650
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
+ }
651
762
  };
652
763
 
653
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.1",
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": {