diff --git a/README.md b/README.md index 21055fff..6285e4c8 100644 --- a/README.md +++ b/README.md @@ -12,19 +12,19 @@ Rstack CLI brings the Rstack toolchain together for JavaScript development, with It also covers local development needs outside Rstack's scope, with Prettier formatting and lint-staged commands. -| Command | Description | -| --------------------------------------------------- | -------------------------------- | -| [`rs dev`](https://rstack.rs/guide/cli/dev) | Run the app dev server | -| [`rs build`](https://rstack.rs/guide/cli/build) | Build the app for production | -| [`rs preview`](https://rstack.rs/guide/cli/preview) | Preview the app production build | -| [`rs test`](https://rstack.rs/guide/cli/test) | Run tests | -| [`rs lint`](https://rstack.rs/guide/cli/lint) | Lint code | -| [`rs fmt`](https://rstack.rs/guide/cli/fmt) | Format code | -| [`rs check`](https://rstack.rs/guide/cli/check) | Run static checks | -| [`rs lib`](https://rstack.rs/guide/cli/lib) | Build library | -| [`rs doc`](https://rstack.rs/guide/cli/doc) | Serve or build docs | -| [`rs setup`](https://rstack.rs/guide/cli/setup) | Install Git hooks | -| [`rs staged`](https://rstack.rs/guide/cli/staged) | Run tasks on staged Git files | +| Command | Description | +| --------------------------------------------------- | -------------------------------------------- | +| [`rs dev`](https://rstack.rs/guide/cli/dev) | Run the app dev server | +| [`rs build`](https://rstack.rs/guide/cli/build) | Build the app for production | +| [`rs preview`](https://rstack.rs/guide/cli/preview) | Preview the app production build | +| [`rs test`](https://rstack.rs/guide/cli/test) | Run tests | +| [`rs lint`](https://rstack.rs/guide/cli/lint) | Lint code | +| [`rs fmt`](https://rstack.rs/guide/cli/fmt) | Format code | +| [`rs check`](https://rstack.rs/guide/cli/check) | Run static checks, including lint and format | +| [`rs lib`](https://rstack.rs/guide/cli/lib) | Build library | +| [`rs doc`](https://rstack.rs/guide/cli/doc) | Serve or build docs | +| [`rs setup`](https://rstack.rs/guide/cli/setup) | Install Git hooks | +| [`rs staged`](https://rstack.rs/guide/cli/staged) | Run tasks on staged Git files | Rstack CLI fits into your existing project workflow. It does not replace your runtime, package manager, or task runner, such as [pnpm](https://github.com/pnpm/pnpm), [Bun](https://github.com/oven-sh/bun), [Turborepo](https://github.com/vercel/turborepo), [Nx](https://github.com/nrwl/nx), and [Nub](https://github.com/nubjs/nub). diff --git a/packages/create-rstack/template-common/README.md b/packages/create-rstack/template-common/README.md index 0c5951f1..abd777a3 100644 --- a/packages/create-rstack/template-common/README.md +++ b/packages/create-rstack/template-common/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the app for production. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Run the app dev server. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-doc-i18n/README.md b/packages/create-rstack/template-doc-i18n/README.md index 6aa686d3..ba4906ab 100644 --- a/packages/create-rstack/template-doc-i18n/README.md +++ b/packages/create-rstack/template-doc-i18n/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the documentation site for production. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Start the documentation dev server. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-doc/README.md b/packages/create-rstack/template-doc/README.md index 849036d5..cd9ac929 100644 --- a/packages/create-rstack/template-doc/README.md +++ b/packages/create-rstack/template-doc/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the documentation site for production. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Start the documentation dev server. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-node-ts/README.md b/packages/create-rstack/template-lib-node-ts/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-node-ts/README.md +++ b/packages/create-rstack/template-lib-node-ts/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-node/README.md b/packages/create-rstack/template-lib-node/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-node/README.md +++ b/packages/create-rstack/template-lib-node/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-react-ts/README.md b/packages/create-rstack/template-lib-react-ts/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-react-ts/README.md +++ b/packages/create-rstack/template-lib-react-ts/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-react/README.md b/packages/create-rstack/template-lib-react/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-react/README.md +++ b/packages/create-rstack/template-lib-react/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-solid-ts/README.md b/packages/create-rstack/template-lib-solid-ts/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-solid-ts/README.md +++ b/packages/create-rstack/template-lib-solid-ts/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-solid/README.md b/packages/create-rstack/template-lib-solid/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-solid/README.md +++ b/packages/create-rstack/template-lib-solid/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-svelte-ts/README.md b/packages/create-rstack/template-lib-svelte-ts/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-svelte-ts/README.md +++ b/packages/create-rstack/template-lib-svelte-ts/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-svelte/README.md b/packages/create-rstack/template-lib-svelte/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-svelte/README.md +++ b/packages/create-rstack/template-lib-svelte/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-vue-ts/README.md b/packages/create-rstack/template-lib-vue-ts/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-vue-ts/README.md +++ b/packages/create-rstack/template-lib-vue-ts/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/create-rstack/template-lib-vue/README.md b/packages/create-rstack/template-lib-vue/README.md index b0746dfb..93e8a06e 100644 --- a/packages/create-rstack/template-lib-vue/README.md +++ b/packages/create-rstack/template-lib-vue/README.md @@ -11,7 +11,7 @@ Install the dependencies: ## Scripts - `{{ packageManager }} run build`: Build the library. -- `{{ packageManager }} run check`: Run static checks, including linting and formatting. +- `{{ packageManager }} run check`: Run static checks, including lint and format. - `{{ packageManager }} run dev`: Build the library in watch mode. - `{{ packageManager }} run format`: Format code. - `{{ packageManager }} run lint`: Lint code. diff --git a/packages/rstack/src/cli/commands.ts b/packages/rstack/src/cli/commands.ts index 39459cc7..28b44062 100644 --- a/packages/rstack/src/cli/commands.ts +++ b/packages/rstack/src/cli/commands.ts @@ -1,48 +1,58 @@ import { join } from 'node:path'; -import { color } from 'rslog'; import { getConfigState } from '../config.ts'; import { insertConfigArg, parseArgs, parseCliArgs } from './args.ts'; +import { renderHelp } from './help.ts'; + +const renderRootHelp = (): string => + renderHelp({ + usage: 'rs [command] [...options]', + sections: [ + { + title: 'Commands', + items: [ + ['dev', 'Run the app dev server'], + ['build', 'Build the app for production'], + ['preview', 'Preview the app production build'], + ['lib', 'Build library'], + ['doc', 'Serve or build docs'], + ['fmt, format', 'Format code'], + ['lint', 'Lint code'], + ['check', 'Run static checks, including lint and format'], + ['test', 'Run tests'], + ['staged', 'Run tasks on staged Git files'], + ['setup', 'Install Git hooks'], + ], + }, + { + content: `For command-specific options, run: + $ rs -h`, + dim: true, + }, + { + title: 'Options', + items: [ + ['-c, --config ', 'Specify Rstack config file path'], + ['-h, --help', 'Display this help message'], + ['-v, --version', 'Display version number'], + ], + }, + ], + }); -declare global { - const RSTACK_VERSION: string; -} - -const helpMessage = `Rstack v${RSTACK_VERSION} - -${color.cyan('Usage')}: -${color.yellow(' $ rs [command] [...options]')} - -${color.cyan('Commands')}: - dev Run the app dev server - build Build the app for production - preview Preview the app production build - lib Build library - doc Serve or build docs - fmt, format Format code - lint Lint code - check Run static checks, including linting and formatting - test Run tests - staged Run tasks on staged Git files - setup Install Git hooks - -${color.dim(`For command-specific options, run: - $ rs -h`)} - -${color.cyan('Options')}: - -c, --config Specify Rstack config file path - -h, --help Display this help message - -v, --version Display version number`; - -const checkHelpMessage = `Rstack v${RSTACK_VERSION} - -${color.cyan('Usage')}: -${color.yellow(' $ rs check [options]')} - -Run static checks, including linting and formatting. - -${color.cyan('Options')}: - --type-check Enable TypeScript type checking - -h, --help Display this help message`; +const renderCheckHelp = (): string => + renderHelp({ + usage: 'rs check [options]', + description: 'Run static checks, including lint and format', + sections: [ + { + title: 'Options', + items: [ + ['--type-check', 'Enable TypeScript type checking'], + ['-h, --help', 'Display this help message'], + ], + }, + ], + }); async function runRsbuildCLI(args: string[]): Promise { const argv = [ @@ -130,7 +140,7 @@ async function runCheckCLI(args: string[]): Promise { }); if (values.help) { - console.log(checkHelpMessage); + console.log(renderCheckHelp()); return; } @@ -153,7 +163,7 @@ export async function setupCommands(): Promise { getConfigState().configPath = configPath; if (!command || command === '-h' || command === '--help') { - console.log(helpMessage); + console.log(renderRootHelp()); return; } diff --git a/packages/rstack/src/cli/help.ts b/packages/rstack/src/cli/help.ts new file mode 100644 index 00000000..42ee9db6 --- /dev/null +++ b/packages/rstack/src/cli/help.ts @@ -0,0 +1,54 @@ +import { color } from 'rslog'; + +declare global { + const RSTACK_VERSION: string; +} + +export type HelpItem = readonly [label: string, description: string]; + +export type HelpSection = + | { + title: string; + items: readonly HelpItem[]; + } + | { + content: string; + dim?: boolean; + }; + +export type HelpDefinition = { + usage: string; + description?: string; + sections?: readonly HelpSection[]; +}; + +const renderItems = (items: readonly HelpItem[]): string => { + const labelWidth = items.reduce((width, [label]) => Math.max(width, label.length), 0); + + return items + .map(([label, description]) => ` ${label.padEnd(labelWidth)} ${description}`) + .join('\n'); +}; + +const renderSection = (section: HelpSection): string => { + if ('items' in section) { + return `${color.cyan(section.title)}:\n${renderItems(section.items)}`; + } + + return section.dim ? color.dim(section.content) : section.content; +}; + +export const renderHelp = ({ usage, description, sections = [] }: HelpDefinition): string => { + const blocks = [ + color.bold(`Rstack v${RSTACK_VERSION}`), + `${color.cyan('Usage')}:\n${color.yellow(` $ ${usage}`)}`, + ]; + + if (description) { + blocks.push(description); + } + + blocks.push(...sections.map(renderSection)); + + return blocks.join('\n\n'); +}; diff --git a/packages/rstack/src/fmt/cli.ts b/packages/rstack/src/fmt/cli.ts index 4a2829ed..d1a7ee7e 100644 --- a/packages/rstack/src/fmt/cli.ts +++ b/packages/rstack/src/fmt/cli.ts @@ -2,6 +2,7 @@ import path from 'node:path'; import { performance } from 'node:perf_hooks'; import { color, logger } from 'rslog'; import { parseArgs } from '../cli/args.ts'; +import { renderHelp } from '../cli/help.ts'; import { loadRstackConfig } from '../config.ts'; import { ensureProjectCacheDir } from '../projectCache.ts'; import { fmtCacheFileName } from './cacheStore.ts'; @@ -26,26 +27,30 @@ interface ParsedFmtCLIArgs { stdinFilepath?: string; } -const fmtHelpMessage: string = `Rstack v${RSTACK_VERSION} - -${color.cyan('Usage')}: -${color.yellow(' $ rs fmt [options] [files/globs...]')} - -Format files with Prettier. - -${color.cyan('Options')}: - -w, --write Write formatted files in place (default) - --check Check whether files are formatted - -l, --list-different Print paths of unformatted files - --ignore-path Path to an additional ignore file (repeatable) - -u, --ignore-unknown Ignore unknown files - --no-cache Disable the formatting cache - --cache-location Path to the formatting cache directory - --no-error-on-unmatched-pattern Do not error when no files match - --with-node-modules Process files inside node_modules - --parallel-workers Number of parallel workers - --stdin-filepath Format stdin as if it were saved at - -h, --help Display this help message`; +const renderFmtHelp = (): string => + renderHelp({ + usage: 'rs fmt [options] [files/globs...]', + description: 'Format code', + sections: [ + { + title: 'Options', + items: [ + ['-w, --write', 'Write formatted files in place (default)'], + ['--check', 'Check whether files are formatted'], + ['-l, --list-different', 'Print paths of unformatted files'], + ['--ignore-path ', 'Path to an additional ignore file (repeatable)'], + ['-u, --ignore-unknown', 'Ignore unknown files'], + ['--no-cache', 'Disable the formatting cache'], + ['--cache-location ', 'Path to the formatting cache directory'], + ['--no-error-on-unmatched-pattern', 'Do not error when no files match'], + ['--with-node-modules', 'Process files inside node_modules'], + ['--parallel-workers ', 'Number of parallel workers'], + ['--stdin-filepath ', 'Format stdin as if it were saved at '], + ['-h, --help', 'Display this help message'], + ], + }, + ], + }); const parseMaxWorkers = (value: string | undefined): number | undefined => { if (value === undefined) { @@ -268,7 +273,7 @@ const runFmtCLI = async (args: string[]): Promise => { withNodeModules, } = parseFmtCLIArgs(args); if (help) { - logger.log(fmtHelpMessage); + logger.log(renderFmtHelp()); return; } diff --git a/packages/rstack/src/setup/index.ts b/packages/rstack/src/setup/index.ts index 114dfcf4..310638e4 100644 --- a/packages/rstack/src/setup/index.ts +++ b/packages/rstack/src/setup/index.ts @@ -1,17 +1,22 @@ import { color, logger } from 'rslog'; import { parseArgs } from '../cli/args.ts'; +import { renderHelp } from '../cli/help.ts'; import { installHooks } from './install.ts'; -const helpMessage = `Rstack v${RSTACK_VERSION} - -${color.cyan('Usage')}: -${color.yellow(' $ rs setup [options]')} - -Install Git hooks in the current repository. - -${color.cyan('Options')}: - --hooks-dir Specify hooks directory relative to the Git repository root - -h, --help Display this help message`; +const renderSetupHelp = (): string => + renderHelp({ + usage: 'rs setup [options]', + description: 'Install Git hooks in the current repository.', + sections: [ + { + title: 'Options', + items: [ + ['--hooks-dir ', 'Specify hooks directory relative to the Git repository root'], + ['-h, --help', 'Display this help message'], + ], + }, + ], + }); export const runSetupCLI = (args: string[]): void => { const { values } = parseArgs({ @@ -32,7 +37,7 @@ export const runSetupCLI = (args: string[]): void => { const hooksDir = hooksDirs?.[0]; if (values.help) { - console.log(helpMessage); + console.log(renderSetupHelp()); return; } diff --git a/packages/rstack/src/staged.ts b/packages/rstack/src/staged.ts index 7aee8839..1dc45c87 100644 --- a/packages/rstack/src/staged.ts +++ b/packages/rstack/src/staged.ts @@ -1,6 +1,6 @@ import lintStaged from 'lint-staged'; -import { color } from 'rslog'; import { parseArgs } from './cli/args.ts'; +import { renderHelp } from './cli/help.ts'; import { loadRstackConfig } from './config.ts'; export type StagedSyncTaskGenerator = (stagedFileNames: readonly string[]) => string | string[]; @@ -21,23 +21,33 @@ export type StagedTask = export type StagedConfig = Record | StagedTaskGenerator; -const stagedHelpMessage = `Rstack v${RSTACK_VERSION} - -${color.cyan('Usage')}: -${color.yellow(' $ rs staged [options]')} - -Runs lint-staged with tasks from define.staged in rstack.config. - -${color.cyan('Options')}: - --allow-empty Allow empty commits when tasks revert all staged changes - -p, --concurrent The number of tasks to run concurrently, or false for serial - --cwd Working directory to run all tasks in - -d, --debug Print additional debug information - --no-stash Disable the backup stash. Implies "--no-revert". - -q, --quiet Disable lint-staged's own console output - -r, --relative Pass relative filepaths to tasks - -v, --verbose Show task output even when tasks succeed; by default only failed output is shown - -h, --help Display this help message`; +const renderStagedHelp = (): string => + renderHelp({ + usage: 'rs staged [options]', + description: 'Run tasks on staged Git files', + sections: [ + { + title: 'Options', + items: [ + ['--allow-empty', 'Allow empty commits when tasks revert all staged changes'], + [ + '-p, --concurrent ', + 'The number of tasks to run concurrently, or false for serial', + ], + ['--cwd ', 'Working directory to run all tasks in'], + ['-d, --debug', 'Print additional debug information'], + ['--no-stash', 'Disable the backup stash. Implies "--no-revert".'], + ['-q, --quiet', "Disable lint-staged's own console output"], + ['-r, --relative', 'Pass relative filepaths to tasks'], + [ + '-v, --verbose', + 'Show task output even when tasks succeed; by default only failed output is shown', + ], + ['-h, --help', 'Display this help message'], + ], + }, + ], + }); export async function runStagedCLI(args: string[]): Promise { const { values } = parseArgs({ @@ -58,7 +68,7 @@ export async function runStagedCLI(args: string[]): Promise { }); if (values.help) { - console.log(stagedHelpMessage); + console.log(renderStagedHelp()); return; } diff --git a/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap b/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap index 7aea98cf..b5fadcd8 100644 --- a/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap +++ b/packages/rstack/tests/cli/__snapshots__/check.test.ts.snap @@ -6,7 +6,7 @@ exports[`displays check help without loading config 1`] = ` Usage: $ rs check [options] -Run static checks, including linting and formatting. +Run static checks, including lint and format Options: --type-check Enable TypeScript type checking diff --git a/packages/rstack/tests/cli/__snapshots__/help.test.ts.snap b/packages/rstack/tests/cli/__snapshots__/help.test.ts.snap new file mode 100644 index 00000000..16becaff --- /dev/null +++ b/packages/rstack/tests/cli/__snapshots__/help.test.ts.snap @@ -0,0 +1,30 @@ +// Rstest Snapshot v1 + +exports[`displays top-level help 1`] = ` +"Rstack v + +Usage: + $ rs [command] [...options] + +Commands: + dev Run the app dev server + build Build the app for production + preview Preview the app production build + lib Build library + doc Serve or build docs + fmt, format Format code + lint Lint code + check Run static checks, including lint and format + test Run tests + staged Run tasks on staged Git files + setup Install Git hooks + +For command-specific options, run: + $ rs -h + +Options: + -c, --config Specify Rstack config file path + -h, --help Display this help message + -v, --version Display version number +" +`; diff --git a/packages/rstack/tests/cli/check.test.ts b/packages/rstack/tests/cli/check.test.ts index ec95d38f..87b92845 100644 --- a/packages/rstack/tests/cli/check.test.ts +++ b/packages/rstack/tests/cli/check.test.ts @@ -1,4 +1,5 @@ import { expect, test } from 'rstack/test'; +import { normalizeHelpOutput } from '#test-helpers'; import { setupFmtTest } from './fmt/helpers.ts'; const { runCLI, writeProjectFile } = setupFmtTest(); @@ -24,7 +25,7 @@ test('displays check help without loading config', () => { const result = runCheck(['--help']); - expect(result.stdout.replace(/^Rstack v.+/u, 'Rstack v')).toMatchSnapshot(); + expect(normalizeHelpOutput(result.stdout)).toMatchSnapshot(); }); test('runs lint followed by a formatting check', () => { diff --git a/packages/rstack/tests/cli/fmt/__snapshots__/files.test.ts.snap b/packages/rstack/tests/cli/fmt/__snapshots__/files.test.ts.snap new file mode 100644 index 00000000..6fb21f4a --- /dev/null +++ b/packages/rstack/tests/cli/fmt/__snapshots__/files.test.ts.snap @@ -0,0 +1,25 @@ +// Rstest Snapshot v1 + +exports[`displays fmt help without loading config 1`] = ` +"Rstack v + +Usage: + $ rs fmt [options] [files/globs...] + +Format code + +Options: + -w, --write Write formatted files in place (default) + --check Check whether files are formatted + -l, --list-different Print paths of unformatted files + --ignore-path Path to an additional ignore file (repeatable) + -u, --ignore-unknown Ignore unknown files + --no-cache Disable the formatting cache + --cache-location Path to the formatting cache directory + --no-error-on-unmatched-pattern Do not error when no files match + --with-node-modules Process files inside node_modules + --parallel-workers Number of parallel workers + --stdin-filepath Format stdin as if it were saved at + -h, --help Display this help message +" +`; diff --git a/packages/rstack/tests/cli/fmt/files.test.ts b/packages/rstack/tests/cli/fmt/files.test.ts index 7780c925..d650eac1 100644 --- a/packages/rstack/tests/cli/fmt/files.test.ts +++ b/packages/rstack/tests/cli/fmt/files.test.ts @@ -1,4 +1,5 @@ import { expect, test } from 'rstack/test'; +import { normalizeHelpOutput } from '#test-helpers'; import { expectWriteSummary, normalizeDuration, setupFmtTest } from './helpers.ts'; const { readProjectFile, runCLI, runFmt, writeProjectFile } = setupFmtTest(); @@ -6,13 +7,10 @@ const { readProjectFile, runCLI, runFmt, writeProjectFile } = setupFmtTest(); test('displays fmt help without loading config', () => { writeProjectFile('rstack.config.ts', 'throw new Error("must not load");\n'); - const topLevel = runCLI(['--help']); const fmt = runFmt(['--help']); - expect(topLevel.status).toBe(0); - expect(topLevel.stdout).toContain('fmt, format Format code'); expect(fmt.status).toBe(0); - expect(fmt.stdout).toContain('Usage:\n $ rs fmt [options] [files/globs...]'); + expect(normalizeHelpOutput(fmt.stdout)).toMatchSnapshot(); expect(fmt.stderr).toBe(''); }); diff --git a/packages/rstack/tests/cli/help.test.ts b/packages/rstack/tests/cli/help.test.ts new file mode 100644 index 00000000..92bc7c3b --- /dev/null +++ b/packages/rstack/tests/cli/help.test.ts @@ -0,0 +1,5 @@ +import { normalizeHelpOutput, test } from '#test-helpers'; + +test('displays top-level help', ({ execCli, expect }) => { + expect(normalizeHelpOutput(execCli('--help'))).toMatchSnapshot(); +}); diff --git a/packages/rstack/tests/cli/setup/__snapshots__/index.test.ts.snap b/packages/rstack/tests/cli/setup/__snapshots__/index.test.ts.snap new file mode 100644 index 00000000..b4cbcf42 --- /dev/null +++ b/packages/rstack/tests/cli/setup/__snapshots__/index.test.ts.snap @@ -0,0 +1,15 @@ +// Rstest Snapshot v1 + +exports[`displays setup help 1`] = ` +"Rstack v + +Usage: + $ rs setup [options] + +Install Git hooks in the current repository. + +Options: + --hooks-dir Specify hooks directory relative to the Git repository root + -h, --help Display this help message +" +`; diff --git a/packages/rstack/tests/cli/setup/index.test.ts b/packages/rstack/tests/cli/setup/index.test.ts index 077f032b..3f29104f 100644 --- a/packages/rstack/tests/cli/setup/index.test.ts +++ b/packages/rstack/tests/cli/setup/index.test.ts @@ -2,7 +2,7 @@ import { spawnSync } from 'node:child_process'; import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import path from 'node:path'; import { afterEach, beforeEach } from 'rstack/test'; -import { RSTACK_BIN_PATH, test } from '#test-helpers'; +import { normalizeHelpOutput, RSTACK_BIN_PATH, test } from '#test-helpers'; const hooksPath = '.rstack/hooks/_'; @@ -44,14 +44,10 @@ afterEach(() => { }); test('displays setup help', ({ execCli, expect }) => { - expect(execCli('--help', { cwd })).toMatch(/setup\s+Install Git hooks/); - const output = execCli('setup --help', { cwd }); expect(execCli('setup -h', { cwd })).toBe(output); - expect(output).toContain('Usage:\n $ rs setup [options]'); - expect(output).toContain('--hooks-dir '); - expect(output).toContain('-h, --help'); + expect(normalizeHelpOutput(output)).toMatchSnapshot(); }); test('rejects unknown setup options', ({ execCli, expect }) => { diff --git a/packages/rstack/tests/cli/staged/__snapshots__/index.test.ts.snap b/packages/rstack/tests/cli/staged/__snapshots__/index.test.ts.snap new file mode 100644 index 00000000..ab0105da --- /dev/null +++ b/packages/rstack/tests/cli/staged/__snapshots__/index.test.ts.snap @@ -0,0 +1,22 @@ +// Rstest Snapshot v1 + +exports[`should display the staged help message 1`] = ` +"Rstack v + +Usage: + $ rs staged [options] + +Run tasks on staged Git files + +Options: + --allow-empty Allow empty commits when tasks revert all staged changes + -p, --concurrent The number of tasks to run concurrently, or false for serial + --cwd Working directory to run all tasks in + -d, --debug Print additional debug information + --no-stash Disable the backup stash. Implies "--no-revert". + -q, --quiet Disable lint-staged's own console output + -r, --relative Pass relative filepaths to tasks + -v, --verbose Show task output even when tasks succeed; by default only failed output is shown + -h, --help Display this help message +" +`; diff --git a/packages/rstack/tests/cli/staged/index.test.ts b/packages/rstack/tests/cli/staged/index.test.ts index d7c0146d..5fb293f1 100644 --- a/packages/rstack/tests/cli/staged/index.test.ts +++ b/packages/rstack/tests/cli/staged/index.test.ts @@ -1,6 +1,6 @@ import lintStaged from 'lint-staged'; import { afterEach, beforeEach, rs } from 'rstack/test'; -import { test } from '#test-helpers'; +import { normalizeHelpOutput, test } from '#test-helpers'; import { loadRstackConfig } from '../../../src/config.ts'; import { runStagedCLI, type StagedConfig } from '../../../src/staged.ts'; @@ -34,17 +34,7 @@ afterEach(() => { test('should display the staged help message', ({ execCli, expect }) => { const output = execCli('staged --help'); - expect(output).toContain('Rstack v'); - expect(output).toContain('Usage:\n $ rs staged [options]'); - expect(output).toContain('Runs lint-staged with tasks from define.staged in rstack.config.'); - expect(output).toContain('--allow-empty'); - expect(output).toContain('--cwd '); - expect(output).toContain('-d, --debug'); - expect(output).toContain('--no-stash'); - expect(output).toContain('-q, --quiet'); - expect(output).toContain('-r, --relative'); - expect(output).toContain('-v, --verbose'); - expect(output).toContain('-h, --help'); + expect(normalizeHelpOutput(output)).toMatchSnapshot(); }); test('should reject unknown staged options', ({ execCli, expect }) => { diff --git a/packages/rstack/tests/helpers/cli.ts b/packages/rstack/tests/helpers/cli.ts index 6e67b7b0..d1d287d0 100644 --- a/packages/rstack/tests/helpers/cli.ts +++ b/packages/rstack/tests/helpers/cli.ts @@ -10,6 +10,9 @@ export type ExecCliOptions = ExecSyncOptions & { export type ExecCli = (command: string, options?: ExecCliOptions) => string; +export const normalizeHelpOutput = (output: string): string => + output.replace(/^Rstack v.+/u, 'Rstack v'); + type ExecCliError = Error & { stdout?: Buffer | string; stderr?: Buffer | string;