diff --git a/src/index.ts b/src/index.ts index e758777..282ef6c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -211,41 +211,52 @@ function logHelpMessage( } const hasTools = toolsList.length > 0; - const gitOptionLine = git - ? ' --no-git skip Git repository initialization\n' - : ''; - const toolsOptionLine = hasTools - ? ' --tools add additional tools, comma separated\n' - : ''; - const skillsOptionLine = hasSkills - ? ' --skill add optional skills, comma separated\n' - : ''; - const optionalToolsSection = hasTools - ? ` - Optional tools: - ${toolsList.join(', ')}` - : ''; - const optionalSkillsSection = hasSkills - ? ` - Optional skills: - ${skillsList.join(', ')}` - : ''; - - logger.log(` - Usage: create-${name} [dir] [options] - - Options: - - -h, --help display help for command - -d, --dir create project in specified directory - -t, --template specify the template to use -${gitOptionLine}${toolsOptionLine}${skillsOptionLine} --override override files in target directory - --packageName specify the package name - --template-version specify the npm template version - - Available templates: - ${templates.join(', ')}${optionalToolsSection}${optionalSkillsSection} -`); + const options: [flags: string, description: string][] = [ + ['-h, --help', 'display help for command'], + ['-d, --dir ', 'create project in specified directory'], + ['-t, --template ', 'specify the template to use'], + ]; + + if (git) { + options.push(['--no-git', 'skip Git repository initialization']); + } + if (hasTools) { + options.push(['--tools ', 'add additional tools, comma separated']); + } + if (hasSkills) { + options.push(['--skill ', 'add optional skills, comma separated']); + } + + options.push( + ['--override', 'override files in target directory'], + ['--package-name ', 'specify the package name'], + ['--template-version ', 'specify the npm template version'], + ); + + const optionWidth = Math.max(...options.map(([flags]) => flags.length)); + const optionLines = options + .map( + ([flags, description]) => + ` ${flags.padEnd(optionWidth)} ${description}`, + ) + .join('\n'); + const helpSections = [ + `Usage: create-${name} [dir] [options]`, + '', + 'Options:', + optionLines, + '', + `Available templates: ${templates.join(', ')}`, + ]; + + if (hasTools) { + helpSections.push('', `Optional tools: ${toolsList.join(', ')}`); + } + if (hasSkills) { + helpSections.push('', `Optional skills: ${skillsList.join(', ')}`); + } + + logger.log(helpSections.join('\n')); } async function getTools( @@ -747,6 +758,13 @@ export async function create({ */ argv?: string[]; }) { + const argv = parseArgv(processArgv); + + if (argv.help) { + logHelpMessage(name, templates, git, builtinTools, extraTools, extraSkills); + return; + } + logger.greet(`\n◆ Create ${upperFirst(name)} Project`); const { isAgent } = await determineAgent(); @@ -757,14 +775,8 @@ export async function create({ ); } - const argv = parseArgv(processArgv); const gitEnabled = git && argv.git !== false; - if (argv.help) { - logHelpMessage(name, templates, git, builtinTools, extraTools, extraSkills); - return; - } - const cwd = process.cwd(); const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent); const packageManager = pkgInfo ? pkgInfo.name : 'npm'; diff --git a/test/__snapshots__/help.test.ts.snap b/test/__snapshots__/help.test.ts.snap new file mode 100644 index 0000000..8a4f31a --- /dev/null +++ b/test/__snapshots__/help.test.ts.snap @@ -0,0 +1,20 @@ +// Rstest Snapshot v1 + +exports[`help message uses compact, aligned output 1`] = ` +[ + [ + "Usage: create-test [dir] [options] + +Options: + -h, --help display help for command + -d, --dir create project in specified directory + -t, --template specify the template to use + --no-git skip Git repository initialization + --override override files in target directory + --package-name specify the package name + --template-version specify the npm template version + +Available templates: vanilla", + ], +] +`; diff --git a/test/help.test.ts b/test/help.test.ts index bd53cb6..85a74d6 100644 --- a/test/help.test.ts +++ b/test/help.test.ts @@ -1,7 +1,26 @@ -import { expect, test } from 'rstack/test'; +import { afterEach, expect, rs, test } from 'rstack/test'; import { logger } from 'rslog'; import { create } from '../src'; +afterEach(() => { + rs.restoreAllMocks(); +}); + +test('help message uses compact, aligned output', async () => { + const log = rs.spyOn(logger, 'log').mockImplementation(() => {}); + + await create({ + name: 'test', + root: '.', + templates: ['vanilla'], + getTemplateName: async () => 'vanilla', + builtinTools: [], + argv: ['node', 'test', '--help'], + }); + + expect(log.mock.calls).toMatchSnapshot(); +}); + test('help message includes the Git opt-out option', async () => { const logs: string[] = []; const originalLog = logger.log; @@ -26,9 +45,7 @@ test('help message includes the Git opt-out option', async () => { }); } - expect(logs.join('\n')).toContain( - '--no-git skip Git repository initialization', - ); + expect(logs.join('\n')).toContain('--no-git'); }); test('help message hides the Git opt-out option when Git is disabled', async () => {