Skip to content

Commit

Permalink
Refactor CLI options definition (#4530)
Browse files Browse the repository at this point in the history
* Refactor CLI options definition

This aims to prepare for type-checking `cli.js` and does NOT type-check actually.

- Camelize option names for type-checking.
- Add missing `type` for some options.
- Sort options alphabetically for readability.
- Add a unit test for the definition (A system test is too much for this purpose).

See <#4504 (comment)> for details.
  • Loading branch information
ybiquitous authored and hudochenkov committed Jan 11, 2020
1 parent 1005cbd commit eaee6a4
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 36 deletions.
140 changes: 140 additions & 0 deletions lib/__tests__/cli.test.js
@@ -0,0 +1,140 @@
'use strict';

const { buildCLI } = require('../cli');

describe('buildCLI', () => {
it('flags - default', () => {
expect(buildCLI([]).flags).toEqual({
allowEmptyInput: false,
cache: false,
color: false,
disableDefaultIgnores: false,
fix: false,
formatter: 'string',
help: false,
ignoreDisables: false,
printConfig: false,
quiet: false,
reportInvalidScopeDisables: false,
reportNeedlessDisables: false,
version: false,
});
});

it('flags.allowEmptyInput', () => {
expect(buildCLI(['--allow-empty-input']).flags.allowEmptyInput).toBe(true);
expect(buildCLI(['--aei']).flags.allowEmptyInput).toBe(true);
});

it('flags.cache', () => {
expect(buildCLI(['--cache']).flags.cache).toBe(true);
});

it('flags.cacheLocation', () => {
expect(buildCLI(['--cache-location=foo']).flags.cacheLocation).toBe('foo');
});

it('flags.color', () => {
expect(buildCLI(['--color']).flags.color).toBe(true);
expect(buildCLI(['--no-color']).flags.color).toBe(false);
});

it('flags.config', () => {
expect(buildCLI(['--config=/path/to/file']).flags.config).toBe('/path/to/file');
});

it('flags.configBasedir', () => {
expect(buildCLI(['--config-basedir=/path/to/dir']).flags.configBasedir).toBe('/path/to/dir');
});

it('flags.customFormatter', () => {
expect(buildCLI(['--custom-formatter=foo']).flags.customFormatter).toBe('foo');
});

it('flags.customSyntax', () => {
expect(buildCLI(['--custom-syntax=foo']).flags.customSyntax).toBe('foo');
});

it('flags.disableDefaultIgnores', () => {
expect(buildCLI(['--disable-default-ignores']).flags.disableDefaultIgnores).toBe(true);
expect(buildCLI(['--di']).flags.disableDefaultIgnores).toBe(true);
});

it('flags.fix', () => {
expect(buildCLI(['--fix']).flags.fix).toBe(true);
});

it('flags.formatter', () => {
expect(buildCLI(['--formatter=json']).flags.formatter).toBe('json');
expect(buildCLI(['-f', 'json']).flags.formatter).toBe('json');
});

it('flags.help', () => {
expect(buildCLI(['--help']).flags.help).toBe(true);
expect(buildCLI(['-h']).flags.help).toBe(true);
});

it('flags.ignoreDisables', () => {
expect(buildCLI(['--ignore-disables']).flags.ignoreDisables).toBe(true);
expect(buildCLI(['--id']).flags.ignoreDisables).toBe(true);
});

it('flags.ignorePath', () => {
expect(buildCLI(['--ignore-path=/path/to/file']).flags.ignorePath).toBe('/path/to/file');
expect(buildCLI(['-i', '/path/to/file']).flags.ignorePath).toBe('/path/to/file');
});

it('flags.ignorePattern', () => {
expect(buildCLI(['--ignore-pattern=vendor/**']).flags.ignorePattern).toBe('vendor/**');
expect(buildCLI(['--ip', 'vendor/**']).flags.ignorePattern).toBe('vendor/**');
expect(buildCLI(['--ip', 'vendor/**', '--ip', 'test/*.css']).flags.ignorePattern).toEqual([
'vendor/**',
'test/*.css',
]);
});

it('flags.maxWarnings', () => {
expect(buildCLI(['--max-warnings=7']).flags.maxWarnings).toBe(7);
expect(buildCLI(['--mw', '-1']).flags.maxWarnings).toBe(-1);
});

it('flags.outputFile', () => {
expect(buildCLI(['--output-file=/path/to/file']).flags.outputFile).toBe('/path/to/file');
expect(buildCLI(['-o', '/path/to/file']).flags.outputFile).toBe('/path/to/file');
});

it('flags.printConfig', () => {
expect(buildCLI(['--print-config']).flags.printConfig).toBe(true);
});

it('flags.quiet', () => {
expect(buildCLI(['--quiet']).flags.quiet).toBe(true);
expect(buildCLI(['-q']).flags.quiet).toBe(true);
});

it('flags.reportInvalidScopeDisables', () => {
expect(buildCLI(['--report-invalid-scope-disables']).flags.reportInvalidScopeDisables).toBe(
true,
);
expect(buildCLI(['--risd']).flags.reportInvalidScopeDisables).toBe(true);
});

it('flags.reportNeedlessDisables', () => {
expect(buildCLI(['--report-needless-disables']).flags.reportNeedlessDisables).toBe(true);
expect(buildCLI(['--rd']).flags.reportNeedlessDisables).toBe(true);
});

it('flags.stdinFilename', () => {
expect(buildCLI(['--stdin-filename=foo.css']).flags.stdinFilename).toBe('foo.css');
});

it('flags.syntax', () => {
expect(buildCLI(['--syntax=less']).flags.syntax).toBe('less');
expect(buildCLI(['-s', 'less']).flags.syntax).toBe('less');
});

it('flags.version', () => {
expect(buildCLI(['--version']).flags.version).toBe(true);
expect(buildCLI(['-v']).flags.version).toBe(true);
});
});
75 changes: 41 additions & 34 deletions lib/cli.js
Expand Up @@ -220,35 +220,32 @@ const meowOptions = {
When glob pattern matches no files, the process will exit without throwing an error.
`,
flags: {
'allow-empty-input': {
allowEmptyInput: {
alias: 'aei',
type: 'boolean',
},
cache: {
type: 'boolean',
},
'cache-location': {
cacheLocation: {
type: 'string',
},
color: {
type: 'boolean',
},
config: {
type: 'string',
},
'config-basedir': {
configBasedir: {
type: 'string',
},
'print-config': {
type: 'boolean',
},
color: {
type: 'boolean',
},
'custom-formatter': {
customFormatter: {
type: 'string',
},
'custom-syntax': {
customSyntax: {
type: 'string',
},
'disable-default-ignores': {
disableDefaultIgnores: {
alias: 'di',
type: 'boolean',
},
Expand All @@ -264,44 +261,47 @@ const meowOptions = {
alias: 'h',
type: 'boolean',
},
'ignore-disables': {
ignoreDisables: {
alias: 'id',
type: 'boolean',
},
'ignore-path': {
ignorePath: {
alias: 'i',
type: 'string',
},
'ignore-pattern': {
ignorePattern: {
alias: 'ip',
type: 'string',
},
'no-color': {
type: 'boolean',
maxWarnings: {
alias: 'mw',
type: 'number',
},
'output-file': {
outputFile: {
alias: 'o',
type: 'string',
},
'report-needless-disables': {
alias: 'rd',
printConfig: {
type: 'boolean',
},
'report-invalid-scope-disables': {
quiet: {
alias: 'q',
type: 'boolean',
},
reportInvalidScopeDisables: {
alias: 'risd',
type: 'boolean',
},
'max-warnings': {
alias: 'mw',
reportNeedlessDisables: {
alias: 'rd',
type: 'boolean',
},
'stdin-filename': {
stdinFilename: {
type: 'string',
},
quiet: {
alias: 'q',
type: 'boolean',
default: false,
},
syntax: {
alias: 's',
type: 'string',
},
version: {
alias: 'v',
Expand All @@ -317,11 +317,7 @@ const meowOptions = {
* @returns {Promise<any>}
*/
module.exports = (argv) => {
meowOptions.argv = argv;
/** @type {CLIOptions} */
const cli =
// @ts-ignore TODO TYPES
meow(meowOptions);
const cli = buildCLI(argv);

const invalidOptionsMessage = checkInvalidCLIOptions(meowOptions.flags, cli.flags);

Expand Down Expand Up @@ -536,3 +532,14 @@ function handleError(err) {

process.exit(exitCode); // eslint-disable-line no-process-exit
}

/**
* @param {string[]} argv
* @returns {CLIOptions}
*/
function buildCLI(argv) {
// @ts-ignore TODO TYPES
return meow({ ...meowOptions, argv });
}

module.exports.buildCLI = buildCLI;
2 changes: 1 addition & 1 deletion lib/utils/__tests__/checkInvalidCLIOptions.test.js
Expand Up @@ -8,7 +8,7 @@ describe('checkInvalidCLIOptions', () => {
const allowedOptions = {
fix: {},
config: {},
'max-war': { alias: 'mw' },
maxWar: { alias: 'mw' },
quiet: { alias: 'q' },
};

Expand Down
2 changes: 1 addition & 1 deletion lib/utils/checkInvalidCLIOptions.js
Expand Up @@ -75,8 +75,8 @@ module.exports = function checkInvalidCLIOptions(allowedOptions, inputOptions) {
const allOptions = buildAllowedOptions(allowedOptions);

return Object.keys(inputOptions)
.map((opt) => _.kebabCase(opt))
.filter((opt) => !allOptions.includes(opt))
.map(_.kebabCase)
.reduce((msg, invalid) => {
// NOTE: No suggestion for shortcut options because it's too difficult
const suggestion = invalid.length >= 2 ? suggest(allOptions, invalid) : null;
Expand Down

0 comments on commit eaee6a4

Please sign in to comment.