diff --git a/lib/commands/new.js b/lib/commands/new.js index 47cebd45e4..081f5c89be 100644 --- a/lib/commands/new.js +++ b/lib/commands/new.js @@ -38,9 +38,15 @@ module.exports = Command.extend({ { name: 'ci-provider', type: ['travis', 'github'], - default: 'github', description: 'Installs the default CI blueprint. Either Travis or Github Actions is supported.', }, + { + name: 'interactive', + type: Boolean, + default: false, + aliases: ['i'], + description: 'Create a new Ember app/addon in an interactive way.', + }, ], anonymousOptions: [''], @@ -53,10 +59,31 @@ module.exports = Command.extend({ commandOptions.name = rawArgs.shift(); - if (!projectName) { - message = `The \`ember ${this.name}\` command requires a name to be specified. For more details, use \`ember help\`.`; + if (!projectName || commandOptions.interactive) { + let answers = await this.runTask('InteractiveNew', commandOptions); - return Promise.reject(new SilentError(message)); + commandOptions.blueprint = answers.blueprint; + + if (answers.name) { + projectName = answers.name; + commandOptions.name = answers.name; + } + + if (answers.lang) { + commandOptions.lang = answers.lang; + } + + if (answers.packageManager) { + commandOptions.yarn = answers.packageManager === 'yarn'; + } + + if (answers.ciProvider) { + commandOptions.ciProvider = answers.ciProvider; + } + } + + if (!commandOptions.ciProvider) { + commandOptions.ciProvider = 'github'; } if (commandOptions.dryRun) { diff --git a/lib/tasks/interactive-new.js b/lib/tasks/interactive-new.js new file mode 100644 index 0000000000..63bd6cc7d8 --- /dev/null +++ b/lib/tasks/interactive-new.js @@ -0,0 +1,153 @@ +'use strict'; + +const inquirer = require('inquirer'); +const { isLangCode } = require('is-language-code'); +const osLocale = require('os-locale'); + +const Task = require('../models/task'); +const isValidProjectName = require('../utilities/valid-project-name'); + +const DEFAULT_LOCALE = 'en-US'; + +class InteractiveNewTask extends Task { + async run(newCommandOptions, _testAnswers) { + let prompt = inquirer.createPromptModule(); + let questions = await this.getQuestions(newCommandOptions); + let answers = await prompt(questions, _testAnswers); + + answers.lang = answers.langSelection || answers.langDifferent; + + delete answers.langSelection; + delete answers.langDifferent; + + return answers; + } + + async getQuestions(newCommandOptions = {}) { + return [ + { + name: 'blueprint', + type: 'list', + message: 'Is this an app or an addon?', + choices: [ + { + name: 'App', + value: 'app', + }, + { + name: 'Addon', + value: 'addon', + }, + ], + }, + { + name: 'name', + type: 'input', + message: ({ blueprint }) => `Please provide the name of your ${blueprint}:`, + when: !newCommandOptions.name, + validate: (name) => { + if (name) { + if (isValidProjectName(name)) { + return true; + } + + return `We currently do not support \`${name}\` as a name.`; + } + + return 'Please provide a name.'; + }, + }, + { + name: 'langSelection', + type: 'list', + message: ({ blueprint }) => `Please provide the spoken/content language of your ${blueprint}:`, + when: !newCommandOptions.lang, + choices: await this.getLangChoices(), + }, + { + name: 'langDifferent', + type: 'input', + message: 'Please provide the different language:', + when: ({ langSelection } = {}) => !newCommandOptions.lang && !langSelection, + validate: (lang) => { + if (isLangCode(lang).res) { + return true; + } + + return 'Please provide a valid locale code.'; + }, + }, + { + name: 'packageManager', + type: 'list', + message: 'Pick the package manager to use when installing dependencies:', + when: !newCommandOptions.yarn, + choices: [ + { + name: 'NPM', + value: 'npm', + }, + { + name: 'Yarn', + value: 'yarn', + }, + { + name: 'Ignore/Skip', + value: null, + }, + ], + }, + { + name: 'ciProvider', + type: 'list', + message: 'Which CI provider do you want to use?', + when: !newCommandOptions.ciProvider, + choices: [ + { + name: 'GitHub Actions', + value: 'github', + }, + { + name: 'Travis CI', + value: 'travis', + }, + { + name: 'Ignore/Skip', + value: null, + }, + ], + }, + ]; + } + + async getLangChoices() { + let userLocale = await this.getUserLocale(); + let langChoices = [ + { + name: DEFAULT_LOCALE, + value: DEFAULT_LOCALE, + }, + ]; + + if (userLocale !== DEFAULT_LOCALE) { + langChoices.push({ + name: userLocale, + value: userLocale, + }); + } + + langChoices.push({ + name: 'I want to manually provide a different language', + value: null, + }); + + return langChoices; + } + + getUserLocale() { + return osLocale(); + } +} + +module.exports = InteractiveNewTask; +module.exports.DEFAULT_LOCALE = DEFAULT_LOCALE; diff --git a/package.json b/package.json index 37d659f41d..d9da05ea6e 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "heimdalljs-logger": "^0.1.10", "http-proxy": "^1.18.1", "inflection": "^1.13.1", + "inquirer": "^8.2.1", "is-git-url": "^1.0.0", "is-language-code": "^3.1.0", "isbinaryfile": "^5.0.0", @@ -106,6 +107,7 @@ "morgan": "^1.10.0", "nopt": "^3.0.6", "npm-package-arg": "^9.1.0", + "os-locale": "^5.0.0", "p-defer": "^3.0.0", "portfinder": "^1.0.29", "promise-map-series": "^0.3.0", diff --git a/tests/acceptance/new-test.js b/tests/acceptance/new-test.js index 8096e81a95..9f673943e5 100644 --- a/tests/acceptance/new-test.js +++ b/tests/acceptance/new-test.js @@ -170,20 +170,6 @@ describe('Acceptance: ember new', function () { expect(blueprintTargets).to.have.same.deep.members(defaultTargets); }); - it('ember new with empty app name fails with a warning', async function () { - let err = await expect(ember(['new', ''])).to.be.rejected; - - expect(err.name).to.equal('SilentError'); - expect(err.message).to.contain('The `ember new` command requires a name to be specified.'); - }); - - it('ember new without app name fails with a warning', async function () { - let err = await expect(ember(['new'])).to.be.rejected; - - expect(err.name).to.equal('SilentError'); - expect(err.message).to.contain('The `ember new` command requires a name to be specified.'); - }); - it('ember new with app name creates new directory and has a dasherized package name', async function () { await ember(['new', 'FooApp', '--skip-npm', '--skip-bower', '--skip-git']); diff --git a/tests/fixtures/help/help-with-addon.txt b/tests/fixtures/help/help-with-addon.txt index 8282ebf911..5362ef211e 100644 --- a/tests/fixtures/help/help-with-addon.txt +++ b/tests/fixtures/help/help-with-addon.txt @@ -139,7 +139,9 @@ ember new   aliases: -dir  --lang (String) Sets the base human language of the application via index.html --embroider (Boolean) (Default: false) Enables the build system to use Embroider - --ci-provider (travis, github) (Default: github) Installs the default CI blueprint. Either Travis or Github Actions is supported. + --ci-provider (travis, github) Installs the default CI blueprint. Either Travis or Github Actions is supported. + --interactive (Boolean) (Default: false) Create a new Ember app/addon in an interactive way. + aliases: -i ember serve  Builds and serves your app, rebuilding on file changes. diff --git a/tests/fixtures/help/help.js b/tests/fixtures/help/help.js index 1b127d3486..456f017e61 100644 --- a/tests/fixtures/help/help.js +++ b/tests/fixtures/help/help.js @@ -602,10 +602,17 @@ module.exports = { name: 'ci-provider', key: 'ciProvider', type: ['travis', 'github'], - default: 'github', description: 'Installs the default CI blueprint. Either Travis or Github Actions is supported.', required: false, }, + { + name: 'interactive', + default: false, + aliases: ['i'], + description: 'Create a new Ember app/addon in an interactive way.', + key: 'interactive', + required: false, + }, ], anonymousOptions: [''] }, diff --git a/tests/fixtures/help/help.txt b/tests/fixtures/help/help.txt index f0e04deec0..c8a104b6b9 100644 --- a/tests/fixtures/help/help.txt +++ b/tests/fixtures/help/help.txt @@ -139,7 +139,9 @@ ember new   aliases: -dir  --lang (String) Sets the base human language of the application via index.html --embroider (Boolean) (Default: false) Enables the build system to use Embroider - --ci-provider (travis, github) (Default: github) Installs the default CI blueprint. Either Travis or Github Actions is supported. + --ci-provider (travis, github) Installs the default CI blueprint. Either Travis or Github Actions is supported. + --interactive (Boolean) (Default: false) Create a new Ember app/addon in an interactive way. + aliases: -i ember serve  Builds and serves your app, rebuilding on file changes. diff --git a/tests/fixtures/help/with-addon-blueprints.js b/tests/fixtures/help/with-addon-blueprints.js index 208e11225f..11ad5d956d 100644 --- a/tests/fixtures/help/with-addon-blueprints.js +++ b/tests/fixtures/help/with-addon-blueprints.js @@ -634,10 +634,17 @@ module.exports = { name: 'ci-provider', key: 'ciProvider', type: ['travis', 'github'], - default: 'github', description: 'Installs the default CI blueprint. Either Travis or Github Actions is supported.', required: false, }, + { + name: 'interactive', + default: false, + aliases: ['i'], + description: 'Create a new Ember app/addon in an interactive way.', + key: 'interactive', + required: false, + }, ], anonymousOptions: [''] }, diff --git a/tests/fixtures/help/with-addon-commands.js b/tests/fixtures/help/with-addon-commands.js index 60302aec4e..db1a4207bf 100644 --- a/tests/fixtures/help/with-addon-commands.js +++ b/tests/fixtures/help/with-addon-commands.js @@ -602,10 +602,17 @@ module.exports = { name: 'ci-provider', key: 'ciProvider', type: ['travis', 'github'], - default: 'github', description: 'Installs the default CI blueprint. Either Travis or Github Actions is supported.', required: false, }, + { + name: 'interactive', + default: false, + aliases: ['i'], + description: 'Create a new Ember app/addon in an interactive way.', + key: 'interactive', + required: false, + }, ], anonymousOptions: [''] }, diff --git a/tests/unit/commands/new-test.js b/tests/unit/commands/new-test.js index 512327c69c..0a62cf7d29 100644 --- a/tests/unit/commands/new-test.js +++ b/tests/unit/commands/new-test.js @@ -7,6 +7,7 @@ const NewCommand = require('../../../lib/commands/new'); const Blueprint = require('../../../lib/models/blueprint'); const Command = require('../../../lib/models/command'); const Task = require('../../../lib/models/task'); +const InteractiveNewTask = require('../../../lib/tasks/interactive-new'); const td = require('testdouble'); describe('new command', function () { @@ -106,4 +107,114 @@ describe('new command', function () { let reason = await command.validateAndRun(['foo', '--custom-option=customValue']); expect(reason).to.equal('Called run'); }); + + describe('interactive', function () { + it('interactive new is entered when no app/addon name is provided', async function () { + class InteractiveNewTaskMock extends InteractiveNewTask { + run(newCommandOptions) { + return super.run(newCommandOptions, { + blueprint: 'addon', + name: 'foo', + langSelection: 'en-US', + packageManager: 'npm', + ciProvider: 'github', + }); + } + } + + class CreateAndStepIntoDirectoryTask extends Task { + run() {} + } + + class InitCommand extends Command { + run(commandOptions) { + expect(commandOptions).to.deep.include({ + blueprint: 'addon', + name: 'foo', + lang: 'en-US', + yarn: false, + ciProvider: 'github', + }); + } + } + + command.tasks.InteractiveNew = InteractiveNewTaskMock; + command.tasks.CreateAndStepIntoDirectory = CreateAndStepIntoDirectoryTask; + command.commands.Init = InitCommand; + + expect(command.validateAndRun([])).to.be.fulfilled; + }); + + it('interactive new is entered when the `--interactive` flag is provided', async function () { + class InteractiveNewTaskMock extends InteractiveNewTask { + run(newCommandOptions) { + return super.run(newCommandOptions, { + blueprint: 'app', + name: newCommandOptions.name, + langSelection: 'nl-BE', + packageManager: 'yarn', + ciProvider: 'travis', + }); + } + } + + class CreateAndStepIntoDirectoryTask extends Task { + run() {} + } + + class InitCommand extends Command { + run(commandOptions) { + expect(commandOptions).to.deep.include({ + blueprint: 'app', + name: 'bar', + lang: 'nl-BE', + yarn: true, + ciProvider: 'travis', + }); + } + } + + command.tasks.InteractiveNew = InteractiveNewTaskMock; + command.tasks.CreateAndStepIntoDirectory = CreateAndStepIntoDirectoryTask; + command.commands.Init = InitCommand; + + expect(command.validateAndRun(['bar', '--interactive'])).to.be.fulfilled; + }); + + it('interactive new is entered when the `-i` flag is provided', async function () { + class InteractiveNewTaskMock extends InteractiveNewTask { + run(newCommandOptions) { + return super.run(newCommandOptions, { + blueprint: 'app', + name: newCommandOptions.name, + langSelection: 'fr-BE', + packageManager: null, + ciProvider: null, + }); + } + } + + class CreateAndStepIntoDirectoryTask extends Task { + run() {} + } + + class InitCommand extends Command { + run(commandOptions) { + expect(commandOptions).does.not.have.key('yarn'); + expect(commandOptions).to.deep.include({ + blueprint: 'app', + name: 'baz', + lang: 'fr-BE', + ciProvider: 'github', + }); + } + } + + command.tasks.InteractiveNew = InteractiveNewTaskMock; + command.tasks.CreateAndStepIntoDirectory = CreateAndStepIntoDirectoryTask; + command.commands.Init = InitCommand; + + expect(command.validateAndRun(['baz', '-i'])).to.be.fulfilled; + }); + }); }); diff --git a/tests/unit/tasks/interactive-new-test.js b/tests/unit/tasks/interactive-new-test.js new file mode 100644 index 0000000000..b615c72e78 --- /dev/null +++ b/tests/unit/tasks/interactive-new-test.js @@ -0,0 +1,148 @@ +'use strict'; + +const { expect } = require('chai'); +const InteractiveNewTask = require('../../../lib/tasks/interactive-new'); + +describe('interactive new task', function () { + let interactiveNewTask; + + beforeEach(function () { + interactiveNewTask = new InteractiveNewTask(); + }); + + afterEach(function () { + interactiveNewTask = null; + }); + + it('it only displays the `name` question when no app/addon name is provided', async function () { + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('name', questions); + + expect(question.when).to.be.true; + + questions = await interactiveNewTask.getQuestions({ name: 'foo' }); + question = getQuestion('name', questions); + + expect(question.when).to.be.false; + }); + + it('it validates the provided app/addon name', async function () { + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('name', questions); + + expect(question.validate('')).to.equal('Please provide a name.'); + expect(question.validate('app')).to.equal(`We currently do not support \`app\` as a name.`); + expect(question.validate('foo')).to.be.true; + }); + + it('it only displays the `langSelection` question when no language is provided', async function () { + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('langSelection', questions); + + expect(question.when).to.be.true; + + questions = await interactiveNewTask.getQuestions({ lang: 'nl-BE' }); + question = getQuestion('langSelection', questions); + + expect(question.when).to.be.false; + }); + + it('it only displays the `langDifferent` question when no language is provided and when the user wants to provide a different language', async function () { + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('langDifferent', questions); + + expect(question.when()).to.be.true; + + questions = await interactiveNewTask.getQuestions({ lang: 'nl-BE' }); + question = getQuestion('langDifferent', questions); + + expect(question.when()).to.be.false; + + questions = await interactiveNewTask.getQuestions(); + question = getQuestion('langDifferent', questions); + + expect(question.when({ langSelection: 'nl-BE' })).to.be.false; + }); + + it('it validates the provided different language', async function () { + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('langDifferent', questions); + + expect(question.validate('')).to.equal('Please provide a valid locale code.'); + expect(question.validate('foo')).to.equal('Please provide a valid locale code.'); + expect(question.validate('nl-BE')).to.be.true; + }); + + it('it only displays the `packageManager` question when the yarn option is not provided', async function () { + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('packageManager', questions); + + expect(question.when).to.be.true; + + questions = await interactiveNewTask.getQuestions({ yarn: true }); + question = getQuestion('packageManager', questions); + + expect(question.when).to.be.false; + }); + + it('it displays the correct language choices', async function () { + let userLocale = InteractiveNewTask.DEFAULT_LOCALE; + + class InteractiveNewTaskMock extends InteractiveNewTask { + getUserLocale() { + return userLocale; + } + } + + interactiveNewTask = new InteractiveNewTaskMock(); + + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('langSelection', questions); + + expect(question.choices).to.deep.equal([ + { + name: InteractiveNewTask.DEFAULT_LOCALE, + value: InteractiveNewTask.DEFAULT_LOCALE, + }, + { + name: 'I want to manually provide a different language', + value: null, + }, + ]); + + userLocale = 'nl-BE'; + questions = await interactiveNewTask.getQuestions(); + question = getQuestion('langSelection', questions); + + expect(question.choices).to.deep.equal([ + { + name: InteractiveNewTask.DEFAULT_LOCALE, + value: InteractiveNewTask.DEFAULT_LOCALE, + }, + { + name: 'nl-BE', + value: 'nl-BE', + }, + { + name: 'I want to manually provide a different language', + value: null, + }, + ]); + }); + + it('it only displays the `ciProvider` question when no CI provider is provided', async function () { + let questions = await interactiveNewTask.getQuestions(); + let question = getQuestion('ciProvider', questions); + + expect(question.when).to.be.true; + + questions = await interactiveNewTask.getQuestions({ ciProvider: 'github' }); + question = getQuestion('ciProvider', questions); + + expect(question.when).to.be.false; + }); +}); + +function getQuestion(name, questions) { + return questions.find((question) => question.name === name); +} diff --git a/yarn.lock b/yarn.lock index 21bb60015c..584c0413af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -915,6 +915,13 @@ 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: + 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== + dependencies: + type-fest "^0.21.3" + ansi-escapes@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" @@ -1259,6 +1266,15 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.3.0.tgz#1d269cbf7e6243ea886aa41453c3651ccbe13c22" integrity sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bl@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/bl/-/bl-5.0.0.tgz#6928804a41e9da9034868e1c50ca88f21f57aea2" @@ -1790,6 +1806,14 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -2021,7 +2045,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, 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, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -2120,6 +2144,13 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + cli-cursor@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" @@ -2132,6 +2163,11 @@ cli-spinners@^2.0.0, cli-spinners@^2.6.1: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== +cli-spinners@^2.5.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + cli-table@^0.3.1: version "0.3.11" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.11.tgz#ac69cdecbe81dccdba4889b9a18b7da312a9d3ee" @@ -2144,6 +2180,11 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== +cli-width@^3.0.0: + version "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.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6" @@ -3608,6 +3649,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +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== + dependencies: + escape-string-regexp "^1.0.5" + figures@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/figures/-/figures-4.0.1.tgz#27b26609907bc888b3e3b0ef5403643f80aa2518" @@ -4701,7 +4749,7 @@ iconv-lite@0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -4818,6 +4866,26 @@ inquirer@^6: strip-ansi "^5.1.0" through "^2.3.6" +inquirer@^8.2.1: + version "8.2.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.1.tgz#e00022e3e8930a92662f760f020686530a84671d" + integrity sha512-pxhBaw9cyTFMjwKtkjePWDhvwzvrNGAw7En4hottzlPvz80GZaMZthdDU35aA6/f5FRZf3uhE057q8w1DE3V2g== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -4832,6 +4900,11 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +invert-kv@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-3.0.1.tgz#a93c7a3d4386a1dc8325b97da9bb1620c0282523" + integrity sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw== + ip@^1.1.5: version "1.1.8" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" @@ -5013,6 +5086,11 @@ is-installed-globally@^0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-interactive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" @@ -5561,6 +5639,13 @@ latest-version@^7.0.0: dependencies: package-json "^8.1.0" +lcid@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-3.1.1.tgz#9030ec479a058fc36b5e8243ebaac8b6ac582fd0" + integrity sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg== + dependencies: + invert-kv "^3.0.0" + leek@0.0.24: version "0.0.24" resolved "https://registry.yarnpkg.com/leek/-/leek-0.0.24.tgz#e400e57f0e60d8ef2bd4d068dc428a54345dbcda" @@ -5812,7 +5897,7 @@ lodash@4.17.21, lodash@^4.0.0, lodash@^4.16.1, lodash@^4.17.12, lodash@^4.17.14, resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.1.0: +log-symbols@4.1.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -5895,6 +5980,13 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +map-age-cleaner@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -5981,6 +6073,15 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +mem@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/mem/-/mem-5.1.1.tgz#7059b67bf9ac2c924c9f1cff7155a064394adfb3" + integrity sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw== + dependencies: + map-age-cleaner "^0.1.3" + mimic-fn "^2.1.0" + p-is-promise "^2.1.0" + memory-streams@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/memory-streams/-/memory-streams-0.1.3.tgz#d9b0017b4b87f1d92f55f2745c9caacb1dc93ceb" @@ -6660,11 +6761,35 @@ ora@^3.4.0: strip-ansi "^5.2.0" wcwidth "^1.0.1" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= +os-locale@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-5.0.0.tgz#6d26c1d95b6597c5d5317bf5fba37eccec3672e0" + integrity sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA== + dependencies: + execa "^4.0.0" + lcid "^3.0.0" + mem "^5.0.0" + os-name@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/os-name/-/os-name-5.0.1.tgz#acb4f996ec5bd86c41755fef9d6d31905c47172e" @@ -6696,6 +6821,11 @@ p-cancelable@^3.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + p-defer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" @@ -6706,6 +6836,11 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -7522,6 +7657,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + restore-cursor@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" @@ -7600,6 +7743,13 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" +rxjs@^7.5.5: + version "7.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" + integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== + dependencies: + tslib "^2.1.0" + rxjs@^7.5.6: version "7.5.6" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" @@ -8584,6 +8734,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^0.8.0: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"