diff --git a/README.md b/README.md index 9152e75..f6f86e4 100644 --- a/README.md +++ b/README.md @@ -69,3 +69,5 @@ MIT This project source code is based on **Create Solid** MIT Licensed. + +`./src/prompts.ts` is based on [`@clack/prompts`](https://github.com/bombshell-dev/clack/tree/45ee73b/packages/prompts) MIT Licensed. diff --git a/package.json b/package.json index c0b41a0..049a488 100644 --- a/package.json +++ b/package.json @@ -27,21 +27,20 @@ "registry": "https://registry.npmjs.org" }, "devDependencies": { + "@clack/core": "^0.3.4", "@hono/eslint-config": "^0.0.2", - "@inquirer/confirm": "^3.1.0", - "@inquirer/input": "^2.1.0", - "@inquirer/select": "^2.2.0", "@types/node": "^18.11.18", "@types/yargs-parser": "^21.0.0", - "chalk": "^5.3.0", "commander": "^12.1.0", "esbuild": "^0.16.17", "eslint": "^8.55.0", "execa": "^8.0.1", "giget": "^1.2.3", - "nanospinner": "^1.1.0", + "is-unicode-supported": "^2.0.0", "np": "^7.6.3", + "picocolors": "^1.1.0", "prettier": "^3.3.3", + "sisteransi": "^1.0.5", "tsx": "^4.7.1", "typescript": "^5.3.3", "vitest": "^0.34.6" diff --git a/src/hooks/dependencies.ts b/src/hooks/dependencies.ts index 6141aeb..cdf9424 100644 --- a/src/hooks/dependencies.ts +++ b/src/hooks/dependencies.ts @@ -1,12 +1,9 @@ import { exec } from 'node:child_process' import type { EventEmitter } from 'node:events' import { chdir, exit } from 'node:process' -import confirm from '@inquirer/confirm' -import select from '@inquirer/select' -import chalk from 'chalk' import { execa } from 'execa' -import { createSpinner } from 'nanospinner' import { projectDependenciesHook } from '../hook' +import { confirm, select, spinner } from '../prompts' type PackageManager = 'npm' | 'bun' | 'pnpm' | 'yarn' @@ -50,7 +47,7 @@ const registerInstallationHook = ( } else { installDeps = await confirm({ message: 'Do you want to install project dependencies?', - default: true, + initialValue: true, }) } @@ -63,11 +60,10 @@ const registerInstallationHook = ( } else { packageManager = await select({ message: 'Which package manager do you want to use?', - choices: installedPackageManagerNames.map((template: string) => ({ - title: template, + options: installedPackageManagerNames.map((template: string) => ({ value: template, })), - default: currentPackageManager, + initialValue: currentPackageManager, }) } @@ -78,20 +74,17 @@ const registerInstallationHook = ( exit(1) } - const spinner = createSpinner('Installing project dependencies').start() + const s = spinner() + s.start('Installing project dependencies') const proc = exec(knownPackageManagers[packageManager]) - const procExit: number = await new Promise((res) => { proc.on('exit', (code) => res(code == null ? 0xff : code)) }) if (procExit === 0) { - spinner.success() + s.stop('Installed successfully.') } else { - spinner.stop({ - mark: chalk.red('×'), - text: 'Failed to install project dependencies', - }) + s.stop('Failed to install project dependencies', 1) exit(procExit) } diff --git a/src/index.ts b/src/index.ts index 46942f3..8f1923b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,9 @@ import EventEmitter from 'node:events' import fs from 'node:fs' import path from 'node:path' -import confirm from '@inquirer/confirm' -import input from '@inquirer/input' -import select from '@inquirer/select' -import chalk from 'chalk' import { Option, program, type Command } from 'commander' import { downloadTemplate } from 'giget' -import { createSpinner } from 'nanospinner' +import color from 'picocolors' import { version } from '../package.json' import { projectDependenciesHook } from './hook' import { afterCreateHook } from './hooks/after-create' @@ -16,6 +12,7 @@ import { knownPackageManagerNames, registerInstallationHook, } from './hooks/dependencies' +import { confirm, select, spinner, text } from './prompts' const directoryName = 'templates' const config = { @@ -87,7 +84,7 @@ async function main( options: ArgOptions, command: Command, ) { - console.log(chalk.gray(`${command.name()} version ${command.version()}`)) + console.log(color.gray(`${command.name()} version ${command.version()}`)) const { install, pm, offline, template: templateArg } = options @@ -95,12 +92,13 @@ async function main( if (targetDir) { target = targetDir console.log( - `${chalk.bold(`${chalk.green('✔')} Using target directory`)} … ${target}`, + `${color.bold(`${color.green('✔')} Using target directory`)} … ${target}`, ) } else { - const answer = await input({ + const answer = await text({ message: 'Target directory', - default: 'my-app', + placeholder: 'my-app', + defaultValue: 'my-app', }) target = answer } @@ -115,13 +113,10 @@ async function main( const templateName = templateArg || (await select({ - loop: true, message: 'Which template do you want to use?', - choices: templates.map((template) => ({ - title: template, + options: templates.map((template) => ({ value: template, })), - default: 0, })) if (!templateName) { @@ -136,7 +131,7 @@ async function main( if (fs.readdirSync(target).length > 0) { const response = await confirm({ message: 'Directory not empty. Continue?', - default: false, + initialValue: false, }) if (!response) { process.exit(1) @@ -159,7 +154,8 @@ async function main( }), ) - const spinner = createSpinner('Cloning the template').start() + const s = spinner() + s.start('Cloning the template') await downloadTemplate( `gh:${config.user}/${config.repository}/${config.directory}/${templateName}#${config.ref}`, @@ -169,7 +165,7 @@ async function main( force: true, }, ).then(() => { - spinner.success() + s.stop('Cloned.') emitter.emit('dependencies') }) @@ -200,8 +196,8 @@ async function main( } emitter.on('completed', () => { - console.log(chalk.green(`🎉 ${chalk.bold('Copied project files')}`)) - console.log(chalk.gray('Get started with:'), chalk.bold(`cd ${target}`)) + console.log(color.green(`🎉 ${color.bold('Copied project files')}`)) + console.log(color.gray('Get started with:'), color.bold(`cd ${target}`)) }) } diff --git a/src/prompts.ts b/src/prompts.ts new file mode 100644 index 0000000..535ba47 --- /dev/null +++ b/src/prompts.ts @@ -0,0 +1,301 @@ +import { + block, + ConfirmPrompt, + isCancel, + SelectPrompt, + TextPrompt, +} from '@clack/core' +import isUnicodeSupported from 'is-unicode-supported' +import color from 'picocolors' +import { cursor, erase } from 'sisteransi' + +const unicode = isUnicodeSupported() +const s = (c: string, fallback: string) => (unicode ? c : fallback) + +const S_STEP = color.green('?') + +const S_RADIO_ACTIVE = s('●', '>') +const S_RADIO_INACTIVE = s('○', ' ') + +const S_SUCCESS = s('✔', 'o') +const S_CANCEL = s('✖', 'x') +const S_ERROR = s('⚠️', '!') + +function onCancel() { + console.log(color.red(`\n${S_CANCEL} Operation canceled.`)) +} + +interface LimitOptionsParams { + options: TOption[] + cursor: number + style: (option: TOption, active: boolean) => string +} + +const limitOptions = ( + params: LimitOptionsParams, +): string[] => { + const { cursor, options, style } = params + + const maxItems = 7 + let slidingWindowLocation = 0 + + if (cursor >= slidingWindowLocation + maxItems - 3) { + slidingWindowLocation = Math.max( + Math.min(cursor - maxItems + 3, options.length - maxItems), + 0, + ) + } else if (cursor < slidingWindowLocation + 2) { + slidingWindowLocation = Math.max(cursor - 2, 0) + } + + const shouldRenderTopEllipsis = + maxItems < options.length && slidingWindowLocation > 0 + const shouldRenderBottomEllipsis = + maxItems < options.length && + slidingWindowLocation + maxItems < options.length + + return options + .slice(slidingWindowLocation, slidingWindowLocation + maxItems) + .map((option, i, arr) => { + const isTopLimit = i === 0 && shouldRenderTopEllipsis + const isBottomLimit = i === arr.length - 1 && shouldRenderBottomEllipsis + return isTopLimit || isBottomLimit + ? color.dim('...') + : style(option, i + slidingWindowLocation === cursor) + }) +} + +interface TextOptions { + message: string + placeholder?: string + defaultValue?: string + initialValue?: string + validate?: (value: string) => string | void +} +export const text = (opts: TextOptions) => { + return new TextPrompt({ + validate: opts.validate, + placeholder: opts.placeholder, + defaultValue: opts.defaultValue, + initialValue: opts.initialValue, + render() { + const title = `${S_STEP} ${opts.message}` + const placeholder = opts.placeholder + ? color.inverse(opts.placeholder[0]) + + color.dim(opts.placeholder.slice(1)) + : color.inverse(color.hidden('_')) + const value = !this.value ? placeholder : this.valueWithCursor + + switch (this.state) { + case 'error': + return `${title.trim()} ${value}\n${color.yellow(this.error)}` + case 'submit': + return `${title} ${color.dim(this.value || opts.placeholder)}` + case 'cancel': + return `${title} ${color.gray(color.strikethrough(this.value ?? ''))}` + default: + return `${title} ${value}` + } + }, + }) + .prompt() + .then((result) => { + if (isCancel(result)) { + onCancel() + process.exit(1) + } + return result + }) +} + +interface ConfirmOptions { + message: string + active?: string + inactive?: string + initialValue?: boolean +} +export const confirm = (opts: ConfirmOptions) => { + const active = opts.active ?? 'Yes' + const inactive = opts.inactive ?? 'No' + return new ConfirmPrompt({ + active, + inactive, + initialValue: opts.initialValue ?? true, + render() { + const title = `${S_STEP} ${opts.message}` + const value = this.value ? active : inactive + + switch (this.state) { + case 'submit': + return `${title} ${color.dim(value)}` + case 'cancel': + return `${title} ${color.strikethrough(color.dim(value))}` + default: { + return `${title} ${ + this.value + ? `${color.green(S_RADIO_ACTIVE)} ${active}` + : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(active)}` + } ${color.dim('/')} ${ + !this.value + ? `${color.green(S_RADIO_ACTIVE)} ${inactive}` + : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(inactive)}` + }` + } + } + }, + }) + .prompt() + .then((result) => { + if (isCancel(result)) { + onCancel() + process.exit(1) + } + return result + }) as Promise +} + +type Primitive = Readonly + +type Option = Value extends Primitive + ? { value: Value; label?: string; hint?: string } + : { value: Value; label: string; hint?: string } + +interface SelectOptions { + message: string + options: Option[] + initialValue?: Value +} + +export const select = (opts: SelectOptions) => { + const opt = ( + option: Option, + state: 'inactive' | 'active' | 'selected' | 'cancelled', + ) => { + const label = option.label ?? String(option.value) + switch (state) { + case 'selected': + return `${color.dim(label)}` + case 'active': + return `${color.green(S_RADIO_ACTIVE)} ${label} ${ + option.hint ? color.dim(`(${option.hint})`) : '' + }` + case 'cancelled': + return `${color.strikethrough(color.dim(label))}` + default: + return `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}` + } + } + + return new SelectPrompt({ + options: opts.options, + initialValue: opts.initialValue, + render() { + const title = `${S_STEP} ${opts.message}` + + switch (this.state) { + case 'submit': + return `${title} ${opt(this.options[this.cursor], 'selected')}` + case 'cancel': + return `${title} ${opt(this.options[this.cursor], 'cancelled')}` + default: { + return `${title}\n${limitOptions({ + cursor: this.cursor, + options: this.options, + style: (item, active) => opt(item, active ? 'active' : 'inactive'), + }).join('\n')}` + } + } + }, + }) + .prompt() + .then((result) => { + if (isCancel(result)) { + onCancel() + process.exit(1) + } + return result as Value + }) +} + +export const spinner = () => { + const frames = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0'] + const delay = unicode ? 80 : 120 + + let unblock: () => void + let loop: NodeJS.Timeout + let isSpinnerActive: boolean = false + let _message: string = '' + + const handleExit = (code: number) => { + const msg = code > 1 ? 'Something went wrong' : 'Canceled' + // Due to commander, there may be cancellations with a `0` code. + if (isSpinnerActive) stop(msg, code !== 0 ? code : 1) + } + + const errorEventHandler = () => handleExit(2) + const signalEventHandler = () => handleExit(1) + + const registerHooks = () => { + // Reference: https://nodejs.org/api/process.html#event-uncaughtexception + process.on('uncaughtExceptionMonitor', errorEventHandler) + // Reference: https://nodejs.org/api/process.html#event-unhandledrejection + process.on('unhandledRejection', errorEventHandler) + // Reference Signal Events: https://nodejs.org/api/process.html#signal-events + process.on('SIGINT', signalEventHandler) + process.on('SIGTERM', signalEventHandler) + process.on('exit', handleExit) + } + + const clearHooks = () => { + process.removeListener('uncaughtExceptionMonitor', errorEventHandler) + process.removeListener('unhandledRejection', errorEventHandler) + process.removeListener('SIGINT', signalEventHandler) + process.removeListener('SIGTERM', signalEventHandler) + process.removeListener('exit', handleExit) + } + + const start = (msg: string = ''): void => { + isSpinnerActive = true + unblock = block() + _message = msg.replace(/\.+$/, '') + let frameIndex = 0 + let dotsTimer = 0 + registerHooks() + loop = setInterval(() => { + const frame = color.magenta(frames[frameIndex]) + const loadingDots = '.'.repeat(Math.floor(dotsTimer)).slice(0, 3) + process.stdout.write(cursor.move(-999, 0)) + process.stdout.write(erase.down(1)) + process.stdout.write(`${frame} ${_message}${loadingDots}`) + frameIndex = frameIndex + 1 < frames.length ? frameIndex + 1 : 0 + dotsTimer = dotsTimer < frames.length ? dotsTimer + 0.125 : 0 + }, delay) + } + + const stop = (msg: string = '', code: number = 0): void => { + _message = msg ?? _message + isSpinnerActive = false + clearInterval(loop) + const step = + code === 0 + ? color.green(S_SUCCESS) + : code === 1 + ? color.red(S_CANCEL) + : color.red(S_ERROR) + process.stdout.write(cursor.move(-999, 0)) + process.stdout.write(erase.down(1)) + process.stdout.write(`${step} ${_message}\n`) + clearHooks() + unblock() + } + + const message = (msg: string = ''): void => { + _message = msg ?? _message + } + + return { + start, + stop, + message, + } +} diff --git a/yarn.lock b/yarn.lock index 2c46e72..373e10c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30,6 +30,14 @@ js-tokens "^4.0.0" picocolors "^1.0.0" +"@clack/core@^0.3.4": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@clack/core/-/core-0.3.4.tgz#375e82fc8fe46650b37cab2f2ea8752c6b7f0450" + integrity sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw== + dependencies: + picocolors "^1.0.0" + sisteransi "^1.0.5" + "@esbuild/aix-ppc64@0.19.12": version "0.19.12" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" @@ -434,58 +442,6 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== -"@inquirer/confirm@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-3.1.0.tgz#526cb71ceab28ba827ea287aa81c969e437017b6" - integrity sha512-nH5mxoTEoqk6WpoBz80GMpDSm9jH5V9AF8n+JZAZfMzd9gHeEG9w1o3KawPRR72lfzpP+QxBHLkOKLEApwhDiQ== - dependencies: - "@inquirer/core" "^7.1.0" - "@inquirer/type" "^1.2.1" - -"@inquirer/core@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-7.1.0.tgz#fb78738fd6624de50f027c08d6f24298b72a402b" - integrity sha512-FRCiDiU54XHt5B/D8hX4twwZuzSP244ANHbu3R7CAsJfiv1dUOz24ePBgCZjygEjDUi6BWIJuk4eWLKJ7LATUw== - dependencies: - "@inquirer/type" "^1.2.1" - "@types/mute-stream" "^0.0.4" - "@types/node" "^20.11.26" - "@types/wrap-ansi" "^3.0.0" - ansi-escapes "^4.3.2" - chalk "^4.1.2" - cli-spinners "^2.9.2" - cli-width "^4.1.0" - figures "^3.2.0" - mute-stream "^1.0.0" - run-async "^3.0.0" - signal-exit "^4.1.0" - strip-ansi "^6.0.1" - wrap-ansi "^6.2.0" - -"@inquirer/input@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-2.1.0.tgz#5ff7028245acd9fa9a25e8a04d71611f76bd82ba" - integrity sha512-o57pST+xxZfGww1h4G7ISiX37KlLcajhKgKGG7/h8J6ClWtsyqwMv1el9Ds/4geuYN/HcPj0MyX9gTEO62UpcA== - dependencies: - "@inquirer/core" "^7.1.0" - "@inquirer/type" "^1.2.1" - -"@inquirer/select@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-2.2.0.tgz#f0a6c523f24a7eefd3b912a2a473a2dc82e561d9" - integrity sha512-Pml3DhVM1LnfqasUMIzaBtw+s5UjM5k0bzDeWrWOgqAMWe16AOg0DcAhXHf+SYbnj2CFBeP/TvkvedL4aAEWww== - dependencies: - "@inquirer/core" "^7.1.0" - "@inquirer/type" "^1.2.1" - ansi-escapes "^4.3.2" - chalk "^4.1.2" - figures "^3.2.0" - -"@inquirer/type@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.2.1.tgz#fbc7ab3a2e5050d0c150642d5e8f5e88faa066b8" - integrity sha512-xwMfkPAxeo8Ji/IxfUSqzRi0/+F2GIqJmpc5/thelgMGsjNZcjDDRBO9TLXT1s/hdx/mK5QbVIvgoLIFgXhTMQ== - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -689,14 +645,7 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== -"@types/mute-stream@^0.0.4": - version "0.0.4" - resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478" - integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== - dependencies: - "@types/node" "*" - -"@types/node@*", "@types/node@^20.11.26": +"@types/node@*": version "20.11.30" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.30.tgz#9c33467fc23167a347e73834f788f4b9f399d66f" integrity sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw== @@ -732,11 +681,6 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== -"@types/wrap-ansi@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" - integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== - "@types/yargs-parser@^21.0.0": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -921,7 +865,7 @@ ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: +ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -1233,7 +1177,7 @@ chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1241,11 +1185,6 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" - integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -1299,11 +1238,6 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.9.2: - version "2.9.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" - integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== - cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -1322,11 +1256,6 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== -cli-width@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" - integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== - clone-response@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" @@ -2107,7 +2036,7 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.0.0, figures@^3.2.0: +figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -2884,6 +2813,11 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unicode-supported@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz#fdf32df9ae98ff6ab2cedc155a5a6e895701c451" + integrity sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q== + is-url-superb@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" @@ -3346,23 +3280,11 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mute-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" - integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== - nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== -nanospinner@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/nanospinner/-/nanospinner-1.1.0.tgz#d17ff621cb1784b0a206b400da88a0ef6db39b97" - integrity sha512-yFvNYMig4AthKYfHFl1sLj7B2nkHL4lzdig4osvl9/LdGbXwrdFRoqBS98gsEsOakr0yH+r5NZ/1Y9gdVB8trA== - dependencies: - picocolors "^1.0.0" - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3821,6 +3743,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== + picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -4099,11 +4026,6 @@ run-async@^2.2.0, run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -run-async@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" - integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -4230,6 +4152,11 @@ signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -4854,15 +4781,6 @@ wrap-ansi@^3.0.1: string-width "^2.1.1" strip-ansi "^4.0.0" -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"