Source: prompts.js

import * as TYPES from '#types/types.js' // eslint-disable-line
import { select, input, confirm } from '@inquirer/prompts'
import semver from 'semver'
import chalk from 'chalk'
import { stagedFiles } from '#git.js'

/**
 * @ignore
 * @typedef {TYPES.DATA} DATA {@link DATA}
 */

/**
 * #### Select release type
 * @async
 * @memberof PROMPTS
 * @param {DATA} data - data object
 * @returns {Promise<string>} - release type
 */
async function selectReleaseType (data) {
  function isRemote () {
    if (data.git.isRemote === false || data.git.branchUpstream === '') {
      return { disabled: '(not available) - No remote repository, branch upstream or permission' }
    }
  }
  const answer = await select({
    message: 'Release type:',
    choices: [
      {
        name: 'Local',
        value: 'local',
        description: 'Local git release with new tag and generated changelog on demand.'
      },
      {
        name: 'Remote',
        value: 'remote',
        description: 'Remote release is a local release pushed to a remote repository with Github release notes generated on demand.',
        ...isRemote()
      }
    ]
  })
  return answer
}

/**
 * #### Select a new release version
 * @memberof PROMPTS
 * @async
 * @param {DATA} data - data object
 * @returns {Promise<string>} - release version
 */
async function selectReleaseVersion (data) {
  let version = []
  if (data.git.lastTag !== '') {
    if (semver.valid(data.git.lastTag) === null) {
      console.warn(`${chalk.red('Warning:')} last existing version tag ${data.git.lastTag} is not a valid SemVer version. Please create a valid SemVer version for the new release.\n`)
      return await customReleaseVersion(data)
    }
    version = [
      {
        name: semver.inc(data.git.lastTag, 'patch') ?? '',
        value: semver.inc(data.git.lastTag, 'patch') ?? '',
        description: 'Patch release, bug fixes.'
      },
      {
        name: semver.inc(data.git.lastTag, 'minor') ?? '',
        value: semver.inc(data.git.lastTag, 'minor') ?? '',
        description: 'Minor release, new features.'
      },
      {
        name: semver.inc(data.git.lastTag, 'major') ?? '',
        value: semver.inc(data.git.lastTag, 'major') ?? '',
        description: 'Major release, breaking changes.'
      }
    ]
    const answer = await select({
      message: 'New version:',
      choices: [
        ...version
      ]
    })
    return answer
  } else {
    return await customReleaseVersion(data)
  }
}

/**
 * #### ask if create a changelog file
 * @memberof PROMPTS
 * @async
 * @param {string} file - file name
 * @returns {Promise<boolean>}
 */
async function createChangelogFile (file) {
  const answer = await confirm({
    message: `${chalk.green(file)} file does not exist, create it?`,
    default: true
  })
  return answer
}

/**
 * #### check if staged files are available
 * @memberof PROMPTS
 * @async
 * @param {Array<string>} staged - if staged files
 * @param {DATA} data - data object
 * @returns undefined
 */
async function checkStagedFiles (staged, data) {
  if (staged.length === 0) {
    const answer = await confirm({
      message: 'Nothing to commit. Manually add files to include in the release commit.',
      default: true
    })
    if (answer === true) {
      const newStaged = await stagedFiles()
      if (newStaged.length !== 0) {
        data.git.stagedFiles = newStaged
        return
      } else {
        console.error(`\n${chalk.bold.red('Error:')} You have no staged files for commit.`)
        console.error(`Please add files by running ${chalk.green('git add <file>')} command in a separate terminal window.\n`)
      }
      await checkStagedFiles(staged, data)
    } else {
      process.exit(0)
    }
  }
}

/**
 * #### Custom release version
 * @memberof PROMPTS
 * @param {DATA} data - data object
 * @returns {Promise<string>} portal name|names
 */
async function customReleaseVersion (data) {
  const answer = await input({
    message: 'New version:',
    validate: (value) => {
      if (semver.valid(value) === null) {
        return 'Use semantic versioning (0.0.1, 0.1.0, 1.0.1)'
      }
      return true
    }
  })
  return answer
}

/**
 * #### Confirm to continue
 * @async
 * @memberof PROMPTS
 * @returns {Promise<boolean>} - true if confirmed
 */
async function confirmToContinue () {
  const answer = await confirm({
    message: 'Continue?',
    default: true
  })
  if (answer === false) {
    process.exit(0)
  }
  return answer
}

/**
 * #### Confirm changelog generation
 * @async
 * @memberof PROMPTS
 * @param {DATA} data - data object
 * @returns {Promise<boolean>} - true if confirmed
 */
async function confirmGenerateChangelog (data) {
  const answer = await confirm({
    message: 'Confirm changelog generation?',
    default: true
  })
  return answer
}

/**
 * #### Confirm release commit
 * @memberof PROMPTS
 * @async
 * @param {DATA} data - data object
 * @returns {Promise<boolean>} - true if confirmed
 */
async function confirmReleaseCommit (data) {
  const answer = await confirm({
    message: `Commit a release? ${chalk.grey(`(git commit -m 'Release ${chalk.dim.yellow(data.answers.releaseVersion)}' & git tag ${chalk.dim.yellow(data.answers.releaseVersion)})`)}`,
    default: true
  })
  return answer
}

/**
 * #### Ask if GitHub release notes should be published
 * @memberof PROMPTS
 * @async
 * @returns {Promise<boolean>} - true if confirmed
 */
async function confirmPublishReleaseNotes () {
  const answer = await confirm({
    message: 'Publish GitHub release notes?',
    default: true
  })
  return answer
}

/**
 * #### Confirm git push to remote repository and push tags
 * @memberof PROMPTS
 * @async
 * @returns {Promise<boolean>} - true if confirmed
 */
async function confirmPush () {
  const answer = await confirm({
    message: `Push to remote repository with tags? ${chalk.grey('(git push & git push --tags)')}`,
    default: true
  })
  return answer
}

/**
 * #### Ask if the release should be pushed to the remote repository
 * @memberof PROMPTS
 * @async
 * @param {DATA} data - data object
 * @returns {Promise<boolean>} - true if confirmed
 */

export {
  confirmToContinue,
  confirmGenerateChangelog,
  confirmReleaseCommit,
  selectReleaseVersion,
  selectReleaseType,
  createChangelogFile,
  checkStagedFiles,
  confirmPublishReleaseNotes,
  confirmPush
}

Table of contents