release.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { readFileSync, writeFileSync } from 'node:fs'
  2. import { resolve } from 'node:path'
  3. import { fileURLToPath } from 'node:url'
  4. import { createRequire } from 'node:module'
  5. import c from 'picocolors'
  6. import prompts from 'prompts'
  7. import { execa } from 'execa'
  8. import semver from 'semver'
  9. const { version: currentVersion } = createRequire(import.meta.url)(
  10. '../package.json'
  11. )
  12. const { inc: _inc, valid } = semver
  13. const versionIncrements = ['patch', 'minor', 'major']
  14. const tags = ['latest', 'next']
  15. const dir = fileURLToPath(new URL('.', import.meta.url))
  16. const inc = (i) => _inc(currentVersion, i)
  17. const run = (bin, args, opts = {}) =>
  18. execa(bin, args, { stdio: 'inherit', ...opts })
  19. const step = (msg) => console.log(c.cyan(msg))
  20. async function main() {
  21. let targetVersion
  22. const versions = versionIncrements
  23. .map((i) => `${i} (${inc(i)})`)
  24. .concat(['custom'])
  25. const { release } = await prompts({
  26. type: 'select',
  27. name: 'release',
  28. message: 'Select release type',
  29. choices: versions
  30. })
  31. if (release === 3) {
  32. targetVersion = (
  33. await prompts({
  34. type: 'text',
  35. name: 'version',
  36. message: 'Input custom version',
  37. initial: currentVersion
  38. })
  39. ).version
  40. } else {
  41. targetVersion = versions[release].match(/\((.*)\)/)[1]
  42. }
  43. if (!valid(targetVersion)) {
  44. throw new Error(`Invalid target version: ${targetVersion}`)
  45. }
  46. const { tag } = await prompts({
  47. type: 'select',
  48. name: 'tag',
  49. message: 'Select tag type',
  50. choices: tags
  51. })
  52. const { yes: tagOk } = await prompts({
  53. type: 'confirm',
  54. name: 'yes',
  55. message: `Releasing v${targetVersion} on ${tags[tag]}. Confirm?`
  56. })
  57. if (!tagOk) {
  58. return
  59. }
  60. // Update the package version.
  61. step('\nUpdating the package version...')
  62. updatePackage(targetVersion)
  63. // Build the package.
  64. step('\nBuilding the package...')
  65. await run('pnpm', ['build'])
  66. // Generate the changelog.
  67. step('\nGenerating the changelog...')
  68. await run('pnpm', ['changelog'])
  69. await run('pnpm', ['prettier', '--write', 'CHANGELOG.md'])
  70. const { yes: changelogOk } = await prompts({
  71. type: 'confirm',
  72. name: 'yes',
  73. message: `Changelog generated. Does it look good?`
  74. })
  75. if (!changelogOk) {
  76. return
  77. }
  78. // Commit changes to the Git and create a tag.
  79. step('\nCommitting changes...')
  80. await run('git', ['add', 'CHANGELOG.md', 'package.json'])
  81. await run('git', ['commit', '-m', `release: v${targetVersion}`])
  82. await run('git', ['tag', `v${targetVersion}`])
  83. // Publish the package.
  84. step('\nPublishing the package...')
  85. await run('pnpm', [
  86. 'publish',
  87. '--tag',
  88. tags[tag],
  89. '--ignore-scripts',
  90. '--no-git-checks'
  91. ])
  92. // Push to GitHub.
  93. step('\nPushing to GitHub...')
  94. await run('git', ['push', 'origin', `refs/tags/v${targetVersion}`])
  95. await run('git', ['push'])
  96. }
  97. function updatePackage(version) {
  98. const pkgPath = resolve(resolve(dir, '..'), 'package.json')
  99. const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
  100. pkg.version = version
  101. writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
  102. }
  103. main().catch((err) => console.error(err))