From fad9d974888dd7f789437f2b38dddb0defb5cddc Mon Sep 17 00:00:00 2001 From: Will Boyd Date: Mon, 27 Jan 2025 14:11:31 -0500 Subject: [PATCH] Some wizard comments and renamed variables --- index.js | 5 +---- src/wizard.js | 37 +++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index a6715ec..2c04919 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -import chalk from 'chalk'; import * as commander from 'commander'; import path from 'path'; import * as parser from './src/parser.js'; @@ -9,13 +8,11 @@ import * as wizard from './src/wizard.js'; import * as writer from './src/writer.js'; (async () => { + // configure command line help output commander.program .name('node index.js') .helpOption('-h, --help', 'See the thing you\'re looking at right now') .addHelpText('after', '\nMore documentation is at https://github.com/lonekorean/wordpress-export-to-markdown') - .configureOutput({ - outputError: (str, write) => write(chalk.red(str)) - }); // gather config options from command line and wizard const config = await wizard.getConfig(); diff --git a/src/wizard.js b/src/wizard.js index 8835834..3e7ce28 100644 --- a/src/wizard.js +++ b/src/wizard.js @@ -4,6 +4,7 @@ import * as commander from 'commander'; import * as normalizers from './normalizers.js'; import * as questions from './questions.js'; +// visual formatting for wizard const promptTheme = { prefix: { idle: chalk.gray('\n?'), @@ -17,12 +18,15 @@ const promptTheme = { export async function getConfig() { const config = {}; + // check command line for any config options const commandLineQuestions = questions.all; Object.assign(config, getCommandLineAnswers(commandLineQuestions)); if (config.wizard) { console.log('\nStarting wizard...'); - const wizardQuestions = questions.all.filter((question) => question.name !== 'wizard' && !(camelcase(question.name) in config)); + + // run wizard for remaining config options + const wizardQuestions = questions.all.filter((question) => !(camelcase(question.name) in config)); Object.assign(config, await getWizardAnswers(wizardQuestions)); } else { console.log('\nSkipping wizard...'); @@ -32,11 +36,17 @@ export async function getConfig() { } function getCommandLineAnswers(questions) { + // show errors in red + commander.program.configureOutput({ + outputError: (str, write) => write(chalk.red(str)) + }); + questions.forEach((question) => { const option = new commander.Option('--' + question.name + ' <' + question.type + '>', question.description); option.default(question.default); if (question.choices && question.type !== 'boolean') { + // let commander handle non-boolean multiple choice validation option.choices(question.choices.map((choice) => choice.value)); } else { option.argParser((value) => normalize(value, question.type, (errorMessage) => { @@ -49,17 +59,21 @@ function getCommandLineAnswers(questions) { const answers = commander.program.parse().opts(); + // do some post-processing on the answers for (const [key, value] of Object.entries(answers)) { + // the "wizard" answer and any user-provided (not defaulted) answers are left alone if (key === 'wizard' || commander.program.getOptionValueSource(key) !== 'default') { continue; } if (answers.wizard) { + // remove this default answer so the wizard will ask about it later delete answers[key]; } else { - const option = questions.find((option) => camelcase(option.name) === key); - answers[key] = normalize(value, option.type, (errorMessage) => { - commander.program.error(`error: option '--${option.name} <${option.type}>' argument '${value}' is invalid. ${errorMessage}`); + // normalize and validate default answer + const question = questions.find((question) => camelcase(question.name) === key); + answers[key] = normalize(value, question.type, (errorMessage) => { + commander.program.error(`error: option '--${question.name} <${question.type}>' argument '${value}' is invalid. ${errorMessage}`); }); } } @@ -70,7 +84,8 @@ function getCommandLineAnswers(questions) { export async function getWizardAnswers(questions) { const answers = {}; for (const question of questions) { - let normalizedAnswer = undefined; + // this will be set to the normalized answer during validation + let normalizedAnswer; const promptConfig = { theme: promptTheme, @@ -83,15 +98,17 @@ export async function getWizardAnswers(questions) { promptConfig.loop = false; } else { promptConfig.validate = (value) => { - let validationResult; + let validationErrorMessage; normalizedAnswer = normalize(value, question.type, (errorMessage) => { - validationResult = errorMessage; + validationErrorMessage = errorMessage; }); - return validationResult ?? true; + return validationErrorMessage ?? true; } } - let answer = await question.prompt(promptConfig).catch((ex) => { + // don't care about the return value of prompt() because normalizedAnswer will be used + await question.prompt(promptConfig).catch((ex) => { + // exit gracefully if user hits ctrl + c during wizard if (ex instanceof Error && ex.name === 'ExitPromptError') { console.log('\nUser quit wizard early.'); process.exit(0); @@ -100,7 +117,7 @@ export async function getWizardAnswers(questions) { } }); - answers[camelcase(question.name)] = normalizedAnswer ?? answer; + answers[camelcase(question.name)] = normalizedAnswer; } return answers;