From 8f8546d0ebf656706affaf44d8187c90cf5fb928 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 5 Nov 2025 10:22:43 -0500 Subject: [PATCH 1/2] chore: use async/await Promises instead of .then/.catch Promises --- lib/cli.js | 21 ++++++++++++++---- lib/router.js | 7 +++--- lib/routes/clear-config.js | 14 ++++++------ lib/routes/help.js | 42 ++++++++++++++++++----------------- lib/routes/home.js | 45 +++++++++++++++++++------------------- lib/routes/install.js | 26 ++++++++++++---------- lib/routes/update.js | 29 ++++++++++++------------ 7 files changed, 102 insertions(+), 82 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index ead662b7..f3f81c76 100755 --- a/lib/cli.js +++ b/lib/cli.js @@ -139,7 +139,12 @@ async function init() { return; } - runYo(env).catch(error => onError(error)); + try { + await runYo(env); + } catch (error) { + onError(error); + } + return; } @@ -152,7 +157,11 @@ async function init() { console.log(chalk.red('Installed generators don\'t need the "generator-" prefix.')); console.log(`In the future, run ${generatorCommand} instead!\n`); - await env.run(generatorName, firstCmd.opts).catch(error => onError(error)); + try { + await env.run(generatorName, firstCmd.opts); + } catch (error) { + onError(error); + } return; } @@ -161,8 +170,12 @@ async function init() { // one that will be triggered by the below args. Maybe the nopt parsing // should be done internally, from the args. for (const generator of cli) { - // eslint-disable-next-line no-await-in-loop - await env.run(generator.args, generator.opts).catch(error => onError(error)); + try { + // eslint-disable-next-line no-await-in-loop + await env.run(generator.args, generator.opts); + } catch (error) { + onError(error); + } } } diff --git a/lib/router.js b/lib/router.js index c53d13e8..74f0cbe4 100644 --- a/lib/router.js +++ b/lib/router.js @@ -32,11 +32,12 @@ export default class Router { * Navigate to a route * @param {String} name Route name * @param {*} arg A single argument to pass to the route handler - * @return {Promise} Promise this. + * @return {Promise} */ - navigate(name, argument) { + async navigate(name, argument) { if (typeof this.routes[name] === 'function') { - return this.routes[name].call(null, this, argument).then(() => this); + await this.routes[name].call(null, this, argument); + return this; } throw new Error(`No routes called: ${name}`); diff --git a/lib/routes/clear-config.js b/lib/routes/clear-config.js index ef52eaf5..2ff0d5c0 100644 --- a/lib/routes/clear-config.js +++ b/lib/routes/clear-config.js @@ -46,7 +46,7 @@ export const clearConfig = async app => { }); } - return app.adapter.prompt([{ + const answer = await app.adapter.prompt([{ name: 'whatNext', type: 'list', message: 'Which store would you like to clear?', @@ -54,13 +54,13 @@ export const clearConfig = async app => { generatorList, defaultChoices, ].flat(), - }]).then(answer => { - if (answer.whatNext === 'home') { - return app.navigate('home'); - } + }]); + + if (answer.whatNext === 'home') { + return app.navigate('home'); + } - _clearGeneratorConfig(app, answer.whatNext); - }); + _clearGeneratorConfig(app, answer.whatNext); }; /** diff --git a/lib/routes/help.js b/lib/routes/help.js index f104ce62..a0965bf3 100644 --- a/lib/routes/help.js +++ b/lib/routes/help.js @@ -4,28 +4,30 @@ import open from 'open'; * @param {import('../router.js').default} app * @returns */ -export const help = async app => app.adapter.prompt([{ - name: 'whereTo', - type: 'list', - message: 'Here are a few helpful resources.\n\nI will open the link you select in your browser for you', - choices: [{ - name: 'Take me to the documentation', - value: 'http://yeoman.io/learning/', - }, { - name: 'View Frequently Asked Questions', - value: 'http://yeoman.io/learning/faq.html', - }, { - name: 'File an issue on GitHub', - value: 'http://yeoman.io/contributing/opening-issues.html', - }, { - name: 'Take me back home, Yo!', - value: 'home', - }], -}]).then(async answer => { +export const help = async app => { + const answer = await app.adapter.prompt([{ + name: 'whereTo', + type: 'list', + message: 'Here are a few helpful resources.\n\nI will open the link you select in your browser for you', + choices: [{ + name: 'Take me to the documentation', + value: 'http://yeoman.io/learning/', + }, { + name: 'View Frequently Asked Questions', + value: 'http://yeoman.io/learning/faq.html', + }, { + name: 'File an issue on GitHub', + value: 'http://yeoman.io/contributing/opening-issues.html', + }, { + name: 'Take me back home, Yo!', + value: 'home', + }], + }]); + if (answer.whereTo === 'home') { console.log('I get it, you like learning on your own. I respect that.'); return app.navigate('home'); } - open(answer.whereTo); -}); + return open(answer.whereTo); +}; diff --git a/lib/routes/home.js b/lib/routes/home.js index fb9058a9..5dc28ab5 100644 --- a/lib/routes/home.js +++ b/lib/routes/home.js @@ -53,30 +53,29 @@ export const home = async app => { }); } - return fullname().then(name => { - const allo = (name && _.isString(name)) ? `'Allo ${name.split(' ')[0]}! ` : '\'Allo! '; + const name = await fullname(); + const allo = (name && _.isString(name)) ? `'Allo ${name.split(' ')[0]}! ` : '\'Allo! '; - return app.adapter.prompt([{ - name: 'whatNext', - type: 'list', - message: `${allo}What would you like to do?`, - choices: [ - app.adapter.separator?.('Run a generator'), - generatorList, - app.adapter.separator?.(), - defaultChoices, - app.adapter.separator?.(), - ].flat(), - }]).then(answer => { - if (answer.whatNext.method === 'run') { - return app.navigate('run', answer.whatNext.generator); - } + const answer = await app.adapter.prompt([{ + name: 'whatNext', + type: 'list', + message: `${allo}What would you like to do?`, + choices: [ + app.adapter.separator?.('Run a generator'), + generatorList, + app.adapter.separator?.(), + defaultChoices, + app.adapter.separator?.(), + ].flat(), + }]); - if (answer.whatNext === 'exit') { - return; - } + if (answer.whatNext.method === 'run') { + return app.navigate('run', answer.whatNext.generator); + } - return app.navigate(answer.whatNext); - }); - }); + if (answer.whatNext === 'exit') { + return; + } + + return app.navigate(answer.whatNext); }; diff --git a/lib/routes/install.js b/lib/routes/install.js index 10ba5721..090730e7 100644 --- a/lib/routes/install.js +++ b/lib/routes/install.js @@ -32,10 +32,14 @@ const OFFICIAL_GENERATORS = new Set([ * @param {import('../router.js').default} app * @returns */ -export const install = app => app.adapter.prompt([{ - name: 'searchTerm', - message: 'Search npm for generators:', -}]).then(answers => searchNpm(app, answers.searchTerm)); +export const install = async app => { + const answers = await app.adapter.prompt([{ + name: 'searchTerm', + message: 'Search npm for generators:', + }]); + + return searchNpm(app, answers.searchTerm); +}; const generatorMatchTerm = (generator, term) => `${generator.name} ${generator.description}`.includes(term); const getAllGenerators = _.memoize(() => npmKeyword('yeoman-generator')); @@ -79,7 +83,7 @@ async function searchNpm(app, term) { * @param {import('../router.js').default} app * @returns */ -function promptInstallOptions(app, choices) { +async function promptInstallOptions(app, choices) { let introMessage = 'Sorry, no results matches your search term'; if (choices.length > 0) { @@ -99,13 +103,13 @@ function promptInstallOptions(app, choices) { }], }]; - return app.adapter.prompt(resultsPrompt).then(answer => { - if (answer.toInstall === 'home' || answer.toInstall === 'install') { - return app.navigate(answer.toInstall); - } + const answer = await app.adapter.prompt(resultsPrompt); - installGenerator(app, answer.toInstall); - }); + if (answer.toInstall === 'home' || answer.toInstall === 'install') { + return app.navigate(answer.toInstall); + } + + installGenerator(app, answer.toInstall); } function installGenerator(app, packageName) { diff --git a/lib/routes/update.js b/lib/routes/update.js index 32199701..3b3bf1e8 100644 --- a/lib/routes/update.js +++ b/lib/routes/update.js @@ -18,19 +18,20 @@ function updateGenerators(app, pkgs) { /** * @param {import('../router.js').default} app - * @returns */ -export const update = app => app.adapter.prompt([{ - name: 'generators', - message: 'Generators to update', - type: 'checkbox', - validate(input) { - return input.length > 0 ? true : 'Please select at least one generator to update.'; - }, - choices: Object.keys(app.generators || {}).map(key => ({ - name: app.generators[key].name, - checked: true, - })), -}]).then(answer => { +export const update = async app => { + const answer = await app.adapter.prompt([{ + name: 'generators', + message: 'Generators to update', + type: 'checkbox', + validate(input) { + return input.length > 0 ? true : 'Please select at least one generator to update.'; + }, + choices: Object.keys(app.generators || {}).map(key => ({ + name: app.generators[key].name, + checked: true, + })), + }]); + updateGenerators(app, answer.generators); -}); +}; From 3d340469112d4a5686a6874b650573d18ed30c03 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 5 Nov 2025 10:32:19 -0500 Subject: [PATCH 2/2] Fixed existing incorrect test --- test/router.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/router.js b/test/router.js index eadae9df..7b0133e2 100644 --- a/test/router.js +++ b/test/router.js @@ -56,7 +56,10 @@ describe('Router', () => { }); it('throws on invalid route name', function () { - assert.throws(this.router.navigate.bind(this.route, 'invalid route name')); + assert.rejects( + this.router.navigate.bind(this.router, 'invalid route name'), + 'No routes called: invalid route name', + ); }); });