# Versioning Strategies and Concepts

`package-versioner` offers flexible ways to determine the next version for your project based on its history and your configuration.

## How the Next Version is Calculated

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`:

### 1. Conventional Commits (`versionStrategy: "conventional"`)

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:

-   **Patch Bump (e.g., 1.2.3 -> 1.2.4):** Triggered by `fix:` commit types.
-   **Minor Bump (e.g., 1.2.3 -> 1.3.0):** Triggered by `feat:` commit types.
-   **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.

The specific preset used for analysis (e.g., "angular", "conventional") can be set using the `preset` option in `version.config.json`.

**Format:** `<type>(<scope>): <subject>`

`<scope>` is optional.

**Example Commit Types:**

-   `feat:` (new feature for the user)
-   `fix:` (bug fix for the user)
-   `docs:` (changes to the documentation)
-   `style:` (formatting, missing semi-colons, etc; no production code change)
-   `refactor:` (refactoring production code, e.g. renaming a variable)
-   `test:` (adding missing tests, refactoring tests; no production code change)
-   `chore:` (updating build tasks etc; no production code change)

**References:**

-   [https://www.conventionalcommits.org/](https://www.conventionalcommits.org/)
-   [https://github.com/conventional-changelog/conventional-changelog](https://github.com/conventional-changelog/conventional-changelog)

### 2. Branch Pattern (`versionStrategy: "branchPattern"`)

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.

You define patterns in the `branchPattern` array in `version.config.json`. Each pattern is a string like `"prefix:bumptype"`.

**Example `version.config.json`:**

```json
{
  "versionStrategy": "branchPattern",
  "branchPattern": [
    "feature:minor",
    "hotfix:patch",
    "fix:patch",
    "release:major" 
  ],
  "baseBranch": "main" 
}
```

**How it works:**

1.  The tool checks the current branch name.
2.  It might also look for the most recently merged branch into `baseBranch` that matches any pattern in `branchPattern`.
3.  It compares the relevant branch name (current or last merged) against the prefixes in `branchPattern`.
4.  If a match is found (e.g., current branch is `feature/add-login`), it applies the corresponding bump type (`minor` in this case).

This allows you to enforce version bumps based on your branching workflow (e.g., all branches starting with `feature/` result in a minor bump).

## Monorepo Versioning Modes

While primarily used for single packages now, `package-versioner` retains options for monorepo workflows, controlled mainly by the `synced` flag in `version.config.json`.

### Synced Mode (`synced: true`)

This is the default if the `synced` flag is present and true.

-   **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`).
-   **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.

### Async Mode (`synced: false`)

*(Note: This mode relies heavily on monorepo tooling and structure, like `pnpm workspaces` and correctly configured package dependencies.)*

-   **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.
    -   It calculates an appropriate version bump **independently for each changed package** based on the commits affecting that package.
    -   Only the `package.json` files of the changed packages are updated.
    -   A **single commit** is created grouping all the version bumps, using the commit message template. **No Git tags are created** in this mode.
-   **Use Case:** Suitable for monorepos where packages are versioned independently, but a single commit represents the batch of updates for traceability.

-   **Behavior (Targeted - With `-t` flag):** When using the `-t, --target <targets>` flag:
    -   Only the specified packages (respecting the `skip` list) are considered for versioning.
    -   It calculates an appropriate version bump **independently for each targeted package** based on its commit history.
    -   The `package.json` file of each successfully updated targeted package is modified.
    -   An **individual Git tag** (e.g., `packageName@1.2.3`) is created **for each successfully updated package** immediately after its version is bumped.
    -   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]`).
    -   **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.
-   **Use Case:** Releasing specific packages independently while still tagging each released package individually.

## Prerelease Handling

`package-versioner` provides flexible handling for prerelease versions, allowing both creation of prereleases and promotion to stable releases.

### Creating Prereleases

Use the `--prerelease` flag with an identifier to create a prerelease version:

```bash
# Create a beta prerelease
npx package-versioner --bump minor --prerelease beta
# Result: 1.0.0 -> 1.1.0-beta.0
```

You can also set a default prerelease identifier in your `version.config.json`:

```json
{
  "prereleaseIdentifier": "beta"
}
```

### Promoting Prereleases to Stable Releases

When using standard bump types (`major`, `minor`, `patch`) with the `--bump` flag on a prerelease version, `package-versioner` will automatically clean the prerelease identifier:

```bash
# Starting from version 1.0.0-beta.1
npx package-versioner --bump major
# Result: 1.0.0-beta.1 -> 2.0.0 (not 2.0.0-beta.0)
```

This intuitive behavior means you don't need to use an empty prerelease identifier (`--prerelease ""`) to promote a prerelease to a stable version. Simply specify the standard bump type and the tool will automatically produce a clean version number.

This applies to all standard bump types:
- `--bump major`: 1.0.0-beta.1 -> 2.0.0
- `--bump minor`: 1.0.0-beta.1 -> 1.1.0 
- `--bump patch`: 1.0.0-beta.1 -> 1.0.1

## Tag Templates and Configuration

`package-versioner` provides flexible configuration for how Git tags are formatted, allowing you to customize the tag structure for both single package repositories and monorepos.

### Tag Template Configuration

You can customize how tags are formatted using the following configuration options in `version.config.json`:

```json
{
  "versionPrefix": "v",
  "tagTemplate": "${prefix}${version}",
  "packageTagTemplate": "${packageName}@${prefix}${version}"
}
```

- **versionPrefix**: The prefix used for all version numbers in tags (default: `"v"`)
- **tagTemplate**: The template for the main Git tag (default: `"${prefix}${version}"`)
- **packageTagTemplate**: The template for package-specific Git tags in monorepos (default: `"${packageName}@${prefix}${version}"`)

### Available Template Variables

The tag templates support the following variables:

- `${prefix}`: Replaced with the value of `versionPrefix`
- `${version}`: Replaced with the calculated version number
- `${packageName}`: (Only in `packageTagTemplate`) Replaced with the package name

### Examples

#### Default Tag Format
With default settings, tags will look like:
- Single repository or synced monorepo: `v1.2.3`
- Package-specific tag in async monorepo: `@scope/package-name@v1.2.3`

#### Custom Tag Format Examples
```json
{
  "versionPrefix": "",
  "tagTemplate": "release-${version}"
}
```
This would produce tags like `release-1.2.3` instead of `v1.2.3`.

```json
{
  "versionPrefix": "v",
  "packageTagTemplate": "${packageName}-${prefix}${version}"
}
```
This would produce package tags like `@scope/package-name-v1.2.3` instead of `@scope/package-name@v1.2.3`.
