diff --git a/package.json b/package.json index 0ec539b..e302c83 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "glob": "^10.3.10", "inquirer": "^9.2.12", "nanospinner": "^1.1.0", - "rslog": "^1.1.0", + "picocolors": "^1.0.0", "semver": "^7.5.4", "tsup": "^7.2.0" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4a1373..2aa95ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,9 +26,9 @@ dependencies: nanospinner: specifier: ^1.1.0 version: registry.npmjs.org/nanospinner@1.1.0 - rslog: - specifier: ^1.1.0 - version: registry.npmjs.org/rslog@1.1.0 + picocolors: + specifier: ^1.0.0 + version: registry.npmjs.org/picocolors@1.0.0 semver: specifier: ^7.5.4 version: registry.npmjs.org/semver@7.5.4 @@ -4425,13 +4425,6 @@ packages: optionalDependencies: fsevents: registry.npmjs.org/fsevents@2.3.3 - registry.npmjs.org/rslog@1.1.0: - resolution: {integrity: sha512-wtTijPuwUhyVG7YvnIVzlefhlto4qJ9vm42ewwJtqrOBP/vKdZCIyfiYm0odVCR3ajR1CIUqaloIzbqhlbyaZA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/rslog/-/rslog-1.1.0.tgz} - name: rslog - version: 1.1.0 - engines: {node: '>=14.17.6'} - dev: false - registry.npmjs.org/run-async@3.0.0: resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz} name: run-async diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..1535847 --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,19 @@ +import pico from 'picocolors' + +export default { + info(text: string) { + console.log(text) + }, + success(text: string) { + console.log(pico.green(text)) + }, + warning(text: string) { + console.log(pico.yellow(text)) + }, + error(text: string) { + console.log(pico.red(text)) + }, + title(text: string) { + console.log(pico.cyan(text)) + }, +} diff --git a/src/release.ts b/src/release.ts index b95fb1f..928ac70 100644 --- a/src/release.ts +++ b/src/release.ts @@ -1,7 +1,7 @@ import { createSpinner } from 'nanospinner' import fse from 'fs-extra' -import { execa } from 'execa' -import { logger } from 'rslog' +import { type ExecaReturnBase, execa } from 'execa' +import logger from './logger' import semver from 'semver' import { glob } from 'glob' import inquirer from 'inquirer' @@ -12,7 +12,55 @@ const cwd = process.cwd() const { writeFileSync, readJSONSync } = fse const { prompt } = inquirer -const releaseTypes = ['premajor', 'preminor', 'prepatch', 'major', 'minor', 'patch'] +const releaseTypes = ['premajor', 'preminor', 'prepatch', 'major', 'minor', 'patch'] as const + +async function getRemoteVersion() { + const { name: packageName, version } = readJSONSync(resolve(cwd, 'package.json')) + + const s = createSpinner('Get Remote version...').start() + const choices = ( + ( + await Promise.allSettled([ + execa('npm', ['view', `${packageName}@latest`, 'version']), + execa('npm', ['view', `${packageName}@alpha`, 'version']), + ]) + ).filter((res) => res.status === 'fulfilled') as PromiseFulfilledResult>[] + ).map((res) => res.value.stdout) + s.stop() + + if (choices.length) { + const name = 'Please select the version' + const ret = await prompt([ + { + name, + type: 'list', + choices, + }, + ]) + + return ret[name] as string + } + + const name = 'Get remote version error, if this is the first release, please select' + const ret = await prompt([ + { + name, + type: 'list', + choices: [ + { + name: 'Use default version 0.0.0', + value: '0.0.0', + }, + { + name: 'Use package.json version', + value: version, + }, + ], + }, + ]) + + return ret[name] as string +} async function isWorktreeEmpty() { const ret = await execa('git', ['status', '--porcelain']) @@ -106,17 +154,33 @@ async function confirmRefs(remote = 'origin') { return ret[name] } -async function getReleaseType() { +async function getExpectVersion(currentVersion: string) { const name = 'Please select release type' + const choices = releaseTypes.map((type) => { + const version = semver.inc( + currentVersion, + type.startsWith('pre') && currentVersion.includes('alpha') ? 'prerelease' : type, + 'alpha', + ) + return { + name: `${type}(${version})`, + value: version, + } + }) const ret = await prompt([ { name, type: 'list', - choices: releaseTypes, + choices, }, ]) - return ret[name] + const expectVersion = ret[name] as string + + return { + isPreRelease: expectVersion.includes('alpha'), + expectVersion, + } } export interface ReleaseCommandOptions { @@ -126,13 +190,6 @@ export interface ReleaseCommandOptions { export async function release(options: ReleaseCommandOptions) { try { - const currentVersion = readJSONSync(resolve(cwd, 'package.json')).version - - if (!currentVersion) { - logger.error('Your package is missing the version field') - return - } - if (!(await isWorktreeEmpty())) { logger.error('Git worktree is not empty, please commit changed') return @@ -146,10 +203,10 @@ export async function release(options: ReleaseCommandOptions) { return } - const type = await getReleaseType() - const isPreRelease = type.startsWith('pre') - let expectVersion = semver.inc(currentVersion, type, `alpha.${Date.now()}`) as string - expectVersion = isPreRelease ? expectVersion.slice(0, -2) : expectVersion + const currentVersion = await getRemoteVersion() + logger.title(`current version: ${currentVersion}`) + + const { expectVersion, isPreRelease } = await getExpectVersion(currentVersion) if (!(await confirmVersion(currentVersion, expectVersion))) { return