Skip to content

Commit

Permalink
feat: allow passing --current-version to override the current versi…
Browse files Browse the repository at this point in the history
…on (#17)

Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
murongg and antfu authored Mar 4, 2024
1 parent 81b7e35 commit 4877803
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 31 deletions.
2 changes: 2 additions & 0 deletions src/cli/parse-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export async function parseArgs(): Promise<ParsedArgs> {
noVerify: !args.verify,
files: [...(args['--'] || []), ...resultArgs],
ignoreScripts: args.ignoreScripts,
currentVersion: args.currentVersion,
execute: args.execute,
recursive: !!args.recursive,
}),
Expand Down Expand Up @@ -113,6 +114,7 @@ export function loadCliArgs(argv = process.argv) {
.option('--ignore-scripts', `Ignore scripts (default: ${bumpConfigDefaults.ignoreScripts})`)
.option('-q, --quiet', 'Quiet mode')
.option('-v, --version <version>', 'Target version')
.option('--current-version <version>', 'Current version')
.option('-x, --execute <command>', 'Commands to execute after version bumps')
.help()

Expand Down
9 changes: 6 additions & 3 deletions src/get-old-version.ts → src/get-current-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import type { Operation } from './operation'
* Finds the current version number from files such as package.json.
* An error is thrown if no version number can be found.
*/
export async function getOldVersion(operation: Operation): Promise<Operation> {
export async function getCurrentVersion(operation: Operation): Promise<Operation> {
if (operation.state.currentVersion)
return operation

const { cwd, files } = operation.options

// Check all JSON files in the files list
Expand All @@ -24,8 +27,8 @@ export async function getOldVersion(operation: Operation): Promise<Operation> {
if (version) {
// We found the current version number!
return operation.update({
oldVersionSource: file,
oldVersion: version,
currentVersionSource: file,
currentVersion: version,
})
}
}
Expand Down
28 changes: 14 additions & 14 deletions src/get-new-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { isPrerelease, releaseTypes } from './release-type'
*/
export async function getNewVersion(operation: Operation): Promise<Operation> {
const { release } = operation.options
const { oldVersion } = operation.state
const { currentVersion } = operation.state

switch (release.type) {
case 'prompt':
Expand All @@ -26,16 +26,16 @@ export async function getNewVersion(operation: Operation): Promise<Operation> {
default:
return operation.update({
release: release.type,
newVersion: getNextVersion(oldVersion, release),
newVersion: getNextVersion(currentVersion, release),
})
}
}

/**
* Returns the next version number of the specified type.
*/
function getNextVersion(oldVersion: string, bump: BumpRelease): string {
const oldSemVer = new SemVer(oldVersion)
function getNextVersion(currentVersion: string, bump: BumpRelease): string {
const oldSemVer = new SemVer(currentVersion)

const type = bump.type === 'next'
? oldSemVer.prerelease.length ? 'prerelease' : 'patch'
Expand Down Expand Up @@ -63,15 +63,15 @@ function getNextVersion(oldVersion: string, bump: BumpRelease): string {
/**
* Returns the next version number for all release types.
*/
function getNextVersions(oldVersion: string, preid: string): Record<ReleaseType, string> {
function getNextVersions(currentVersion: string, preid: string): Record<ReleaseType, string> {
const next: Record<string, string> = {}

const parse = semver.parse(oldVersion)
const parse = semver.parse(currentVersion)
if (typeof parse?.prerelease[0] === 'string')
preid = parse?.prerelease[0] || 'preid'

for (const type of releaseTypes)
next[type] = getNextVersion(oldVersion, { type, preid })
next[type] = getNextVersion(currentVersion, { type, preid })

return next
}
Expand All @@ -82,18 +82,18 @@ function getNextVersions(oldVersion: string, preid: string): Record<ReleaseType,
* @returns - A tuple containing the new version number and the release type (if any)
*/
async function promptForNewVersion(operation: Operation): Promise<Operation> {
const { oldVersion } = operation.state
const { currentVersion } = operation.state
const release = operation.options.release as PromptRelease

const next = getNextVersions(oldVersion, release.preid)
const configCustomVersion = await operation.options.customVersion?.(oldVersion, semver)
const next = getNextVersions(currentVersion, release.preid)
const configCustomVersion = await operation.options.customVersion?.(currentVersion, semver)

const PADDING = 13
const answers = await prompts([
{
type: 'autocomplete',
name: 'release',
message: `Current version ${c.green(oldVersion)}`,
message: `Current version ${c.green(currentVersion)}`,
initial: configCustomVersion ? 'config' : 'next',
choices: [
{ value: 'major', title: `${'major'.padStart(PADDING, ' ')} ${c.bold(next.major)}` },
Expand All @@ -108,15 +108,15 @@ async function promptForNewVersion(operation: Operation): Promise<Operation> {
{ value: 'prepatch', title: `${'pre-patch'.padStart(PADDING, ' ')} ${c.bold(next.prepatch)}` },
{ value: 'preminor', title: `${'pre-minor'.padStart(PADDING, ' ')} ${c.bold(next.preminor)}` },
{ value: 'premajor', title: `${'pre-major'.padStart(PADDING, ' ')} ${c.bold(next.premajor)}` },
{ value: 'none', title: `${'as-is'.padStart(PADDING, ' ')} ${c.bold(oldVersion)}` },
{ value: 'none', title: `${'as-is'.padStart(PADDING, ' ')} ${c.bold(currentVersion)}` },
{ value: 'custom', title: 'custom ...'.padStart(PADDING + 4, ' ') },
],
},
{
type: prev => prev === 'custom' ? 'text' : null,
name: 'custom',
message: 'Enter the new version number:',
initial: oldVersion,
initial: currentVersion,
validate: (custom: string) => {
return isValidVersion(custom) ? true : 'That\'s not a valid version number'
},
Expand All @@ -127,7 +127,7 @@ async function promptForNewVersion(operation: Operation): Promise<Operation> {
}

const newVersion = answers.release === 'none'
? oldVersion
? currentVersion
: answers.release === 'custom'
? cleanVersion(answers.custom!)!
: answers.release === 'config'
Expand Down
2 changes: 2 additions & 0 deletions src/normalize-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface NormalizedOptions {
ignoreScripts: boolean
execute?: string
customVersion?: VersionBumpOptions['customVersion']
currentVersion?: string
}

/**
Expand Down Expand Up @@ -145,5 +146,6 @@ export async function normalizeOptions(raw: VersionBumpOptions): Promise<Normali
ignoreScripts,
execute,
customVersion: raw.customVersion,
currentVersion: raw.currentVersion,
}
}
16 changes: 11 additions & 5 deletions src/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type ProgressCallback = (progress: VersionBumpProgress) => void

interface OperationState {
release: ReleaseType | undefined
oldVersionSource: string
oldVersion: string
currentVersionSource: string
currentVersion: string
newVersion: string
commitMessage: string
tagName: string
Expand All @@ -37,8 +37,8 @@ export class Operation {
*/
public readonly state: Readonly<OperationState> = {
release: undefined,
oldVersion: '',
oldVersionSource: '',
currentVersion: '',
currentVersionSource: '',
newVersion: '',
commitMessage: '',
tagName: '',
Expand All @@ -55,7 +55,7 @@ export class Operation {

return {
release: state.release,
oldVersion: state.oldVersion,
currentVersion: state.currentVersion,
newVersion: state.newVersion,
commit: options.commit ? state.commitMessage : false,
tag: options.tag ? state.tagName : false,
Expand All @@ -75,6 +75,12 @@ export class Operation {
private constructor(options: NormalizedOptions, progress?: ProgressCallback) {
this.options = options
this._progress = progress
if (options.currentVersion) {
this.update({
currentVersion: options.currentVersion,
currentVersionSource: 'user',
})
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/types/version-bump-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export interface VersionBumpOptions {
*/
release?: string

/**
* The current version number to be bumpped.
* If not provide, it will be read from the first file in the `files` array.
*/
currentVersion?: string

/**
* The prerelease type (e.g. "alpha", "beta", "next").
*
Expand Down
2 changes: 1 addition & 1 deletion src/types/version-bump-results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface VersionBumpResults {
/**
* The previous version number in package.json.
*/
oldVersion: string
currentVersion: string

/**
* The new version number.
Expand Down
6 changes: 3 additions & 3 deletions src/update-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ async function updateManifestFile(relPath: string, operation: Operation): Promis
*/
async function updateTextFile(relPath: string, operation: Operation): Promise<boolean> {
const { cwd } = operation.options
const { oldVersion, newVersion } = operation.state
const { currentVersion, newVersion } = operation.state
const modified = false

const file = await readTextFile(relPath, cwd)

// Only update the file if it contains at least one occurrence of the old version
if (file.data.includes(oldVersion)) {
if (file.data.includes(currentVersion)) {
// Escape all non-alphanumeric characters in the version
const sanitizedVersion = oldVersion.replace(/(\W)/g, '\\$1')
const sanitizedVersion = currentVersion.replace(/(\W)/g, '\\$1')

// Replace occurrences of the old version number that are surrounded by word boundaries.
// This ensures that it matches "1.23.456" or "v1.23.456", but not "321.23.456".
Expand Down
10 changes: 5 additions & 5 deletions src/version-bump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import c from 'picocolors'
import symbols from 'log-symbols'
import prompts from 'prompts'
import { getNewVersion } from './get-new-version'
import { getOldVersion } from './get-old-version'
import { getCurrentVersion } from './get-current-version'
import { formatVersionString, gitCommit, gitPush, gitTag } from './git'
import { Operation } from './operation'
import { runNpmScript } from './run-npm-script'
Expand Down Expand Up @@ -42,14 +42,14 @@ export async function versionBump(options: VersionBumpOptions): Promise<VersionB
* Bumps the version number in one or more files, prompting the user if necessary.
* Optionally also commits, tags, and pushes to git.
*/
export async function versionBump(arg: VersionBumpOptions | string = {}): Promise<VersionBumpResults | undefined> {
export async function versionBump(arg: (VersionBumpOptions) | string = {}): Promise<VersionBumpResults | undefined> {
if (typeof arg === 'string')
arg = { release: arg }

const operation = await Operation.start(arg)

// Get the old and new version numbers
await getOldVersion(operation)
await getCurrentVersion(operation)
await getNewVersion(operation)

if (arg.confirm) {
Expand Down Expand Up @@ -104,7 +104,7 @@ function printSummary(operation: Operation) {
if (operation.options.push)
console.log(` push ${c.cyan(c.bold('yes'))}`)
console.log()
console.log(` from ${c.bold(operation.state.oldVersion)}`)
console.log(` from ${c.bold(operation.state.currentVersion)}`)
console.log(` to ${c.green(c.bold(operation.state.newVersion))}`)
console.log()
}
Expand All @@ -119,7 +119,7 @@ export async function versionBumpInfo(arg: VersionBumpOptions | string = {}): Pr
const operation = await Operation.start(arg)

// Get the old and new version numbers
await getOldVersion(operation)
await getCurrentVersion(operation)
await getNewVersion(operation)
return operation
}

0 comments on commit 4877803

Please sign in to comment.