Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions packages/rstack/src/fmt/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { parseArgs } from 'node:util';
import { color } from 'rslog';
import type { FmtMode } from './types.ts';

interface ParsedFmtCLIArgs {
mode: FmtMode;
patterns: string[];
help: boolean;
}

const fmtHelpMessage: string = `Rstack v${RSTACK_VERSION}

${color.cyan('Usage')}:
${color.yellow(' $ rs fmt [options] [files/globs...]')}

Format files with Prettier.

${color.cyan('Options')}:
--write Write formatted files in place (default)
--check Check whether files are formatted
--list-different Print paths of unformatted files
-h, --help Display this help message`;

const parseFmtCLIArgs = (args: string[]): ParsedFmtCLIArgs => {
const { values, positionals } = parseArgs({
args,
options: {
write: { type: 'boolean' },
check: { type: 'boolean' },
'list-different': { type: 'boolean' },
listDifferent: { type: 'boolean' },
help: { type: 'boolean', short: 'h' },
},
allowPositionals: true,
strict: true,
});

const listDifferent = values['list-different'] || values.listDifferent;
const modes = [values.write, values.check, listDifferent].filter(Boolean);
if (modes.length > 1) {
throw new Error('The --write, --check, and --list-different options cannot be used together.');
}

const mode = values.check ? 'check' : listDifferent ? 'list-different' : 'write';

return {
mode,
patterns: positionals,
help: values.help ?? false,
};
};

export { fmtHelpMessage, parseFmtCLIArgs };
export type { ParsedFmtCLIArgs };
2 changes: 1 addition & 1 deletion packages/rstack/src/fmt/runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FmtExitCode, FmtFileResult, FmtRunResult, RunFmtFilesOptions } from './types.ts';
import { formatFileSerial } from './vendor/prettier-cli/serial.ts';
import { formatFileSerial } from './serial.ts';

const runFmtFiles = async ({ files, mode }: RunFmtFilesOptions): Promise<FmtRunResult> => {
const startTime = performance.now();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { readFile, writeFile } from 'atomically';
import { format } from 'prettier';
import type { FmtFileRequest } from '../../types.ts';
import type { FmtFileRequest } from './types.ts';

const formatFileSerial = async (
{ path, options }: FmtFileRequest,
Expand Down
7 changes: 0 additions & 7 deletions packages/rstack/src/fmt/vendor/prettier-cli/LICENSE

This file was deleted.

72 changes: 72 additions & 0 deletions packages/rstack/tests/fmt/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { expect, test } from 'rstack/test';
import { fmtHelpMessage, parseFmtCLIArgs } from '../../src/fmt/cli.ts';

test('uses write mode by default', () => {
expect(parseFmtCLIArgs([])).toEqual({
mode: 'write',
patterns: [],
help: false,
});
});

test.each([
['--write', 'write'],
['--check', 'check'],
['--list-different', 'list-different'],
['--listDifferent', 'list-different'],
] as const)('parses %s mode', (option, mode) => {
expect(parseFmtCLIArgs([option])).toEqual({
mode,
patterns: [],
help: false,
});
});

test('preserves file paths and globs', () => {
const patterns = ['src/file with spaces.ts', 'src/**/*.{js,ts}', '!src/generated/**'];

expect(parseFmtCLIArgs([patterns[0], '--check', ...patterns.slice(1)])).toEqual({
mode: 'check',
patterns,
help: false,
});
});

test('treats arguments after the terminator as paths', () => {
expect(parseFmtCLIArgs(['--check', '--', '--write', '--help'])).toEqual({
mode: 'check',
patterns: ['--write', '--help'],
help: false,
});
});

test.each(['--help', '-h'])('parses %s', (option) => {
expect(parseFmtCLIArgs([option]).help).toBe(true);
});

test('provides command help', () => {
expect(fmtHelpMessage).toContain('Usage:\n $ rs fmt [options] [files/globs...]');
expect(fmtHelpMessage).toContain('--write');
expect(fmtHelpMessage).toContain('--check');
expect(fmtHelpMessage).toContain('--list-different');
expect(fmtHelpMessage).toContain('-h, --help');
});

test.each([
['--write', '--check'],
['--write', '--list-different'],
['--write', '--listDifferent'],
['--check', '--list-different'],
['--write', '--check', '--list-different'],
])('rejects conflicting modes: %s', (...args) => {
expect(() => parseFmtCLIArgs(args)).toThrow(
'The --write, --check, and --list-different options cannot be used together.',
);
});

test.each(['--unknown', '--no-cache', '--no-parallel', '--parallel-workers'])(
'rejects unsupported option %s',
(option) => {
expect(() => parseFmtCLIArgs([option])).toThrow();
},
);