import fs from 'fs-extra' import { existsSync, writeFileSync } from 'node:fs' import path from 'node:path' import type { ReleaseType } from 'semver' import semver from 'semver' import type { Options as ExecaOptions, ExecaReturnValue } from 'execa' import execa from 'execa' import colors from 'picocolors' export async function run( bin: string, args: string[], opts: ExecaOptions = {}, ): Promise> { return execa(bin, args, { stdio: 'inherit', ...opts }) } interface VersionChoice { title: string value: string } export function getVersionChoices(currentVersion: string): VersionChoice[] { const currentBeta = currentVersion.includes('beta') const currentAlpha = currentVersion.includes('alpha') const isStable = !currentBeta && !currentAlpha function inc(i: ReleaseType, tag = currentAlpha ? 'alpha' : 'beta') { return semver.inc(currentVersion, i, tag)! } let versionChoices: VersionChoice[] = [ { title: 'next', value: inc(isStable ? 'patch' : 'prerelease'), }, ] if (isStable) { versionChoices.push( { title: 'beta-minor', value: inc('preminor'), }, { title: 'beta-major', value: inc('premajor'), }, { title: 'alpha-minor', value: inc('preminor', 'alpha'), }, { title: 'alpha-major', value: inc('premajor', 'alpha'), }, { title: 'minor', value: inc('minor'), }, { title: 'major', value: inc('major'), }, ) } else if (currentAlpha) { versionChoices.push({ title: 'beta', value: inc('patch') + '-beta.0', }) } else { versionChoices.push({ title: 'stable', value: inc('patch'), }) } versionChoices.push({ value: 'custom', title: 'custom' }) versionChoices = versionChoices.map((i) => { i.title = `${i.title} (${i.value})` return i }) return versionChoices } interface Pkg { name: string version: string private?: boolean } export function getPackageInfo(pkgName: string = ''): { pkg: Pkg pkgName: string pkgDir: string pkgPath: string currentVersion: string } { const pkgDir = path.resolve(__dirname, '../') if (!existsSync(pkgDir)) { throw new Error(`Package ${pkgName} not found`) } const pkgPath = path.resolve(pkgDir, 'package.json') const pkg: Pkg = require(pkgPath) const currentVersion = pkg.version if (pkg.private) { throw new Error(`Package ${pkgName} is private`) } return { pkg, pkgName, pkgDir, pkgPath, currentVersion, } } export function step(msg: string): void { return console.log(colors.cyan(msg)) } export async function getLatestTag(pkgName: string): Promise { const tags = (await run('git', ['tag'], { stdio: 'pipe' })).stdout .split(/\n/) .filter(Boolean) const prefix = pkgName === 'nova' ? 'v' : `${pkgName}@` return tags .filter((tag) => tag.startsWith(prefix)) .sort() .reverse()[0] } export function updateVersion(pkgPath: string, version: string): void { const pkg = fs.readJSONSync(pkgPath) pkg.version = version writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n') } export async function publishPackage( pkdDir: string, tag?: string, ): Promise { const publicArgs = ['publish', '--access', 'public'] if (tag) { publicArgs.push(`--tag`, tag) } await run('npm', publicArgs, { cwd: pkdDir, }) }