Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use util.parseArgs instead of yargs #380

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@types/eslint": "^8.56.12",
"@types/estraverse": "^5.1.6",
"@types/estree": "^1.0.5",
"@types/node": "^20.8.10",
"@types/node": "^22.7.4",
"@types/yargs": "^17.0.29",
"dedent": "^1.5.1",
"eslint": "^8.57.0",
Expand Down
34 changes: 17 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions src/cli/parse-argv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ describe('parseArgv', () => {
});
test('--no-eslintrc', () => {
expect(parseArgv([...baseArgs, '--no-eslintrc']).useEslintrc).toStrictEqual(false);
expect(parseArgv([...baseArgs, '--no-eslintrc=false']).useEslintrc).toStrictEqual(true);
});
test('--config', () => {
expect(parseArgv([...baseArgs]).overrideConfigFile).toStrictEqual(undefined);
expect(parseArgv([...baseArgs, '--config', 'override-config-file.json']).overrideConfigFile).toStrictEqual(
'override-config-file.json',
);
expect(parseArgv([...baseArgs, '-c', 'override-config-file.json']).overrideConfigFile).toStrictEqual(
'override-config-file.json',
);
});
test('--rulesdir', () => {
expect(parseArgv([...baseArgs, '--rulesdir', 'foo']).rulePaths).toStrictEqual(['foo']);
Expand Down Expand Up @@ -47,7 +49,6 @@ describe('parseArgv', () => {
test('--cache', () => {
expect(parseArgv([...baseArgs, '--cache']).cache).toBe(true);
expect(parseArgv([...baseArgs, '--no-cache']).cache).toBe(false);
expect(parseArgv([...baseArgs, '--cache', 'false']).cache).toBe(false);
});
test('--cache-location', () => {
expect(parseArgv([...baseArgs, '--cache-location', '.eslintcache']).cacheLocation).toBe('.eslintcache');
Expand Down
153 changes: 72 additions & 81 deletions src/cli/parse-argv.ts
Copy link
Owner Author

@mizdra mizdra Sep 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

result: 8d04f7c

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { join, relative } from 'node:path';
import yargs from 'yargs';
import { parseArgs } from 'node:util';
import { getCacheDir } from '../util/cache.js';
import { DeepPartial } from '../util/type-check.js';
import { VERSION } from './package.js';
Expand Down Expand Up @@ -29,90 +29,81 @@

/** Parse CLI options */
export function parseArgv(argv: string[]): ParsedCLIOptions {
const parsedArgv = yargs(argv.slice(2))
.wrap(Math.min(140, process.stdout.columns))
.scriptName('eslint-interactive')
.version(VERSION)
.usage('$0 [file.js] [dir]')
.detectLocale(false)
// NOTE: yargs doesn't support negative only option. So we use `--eslintrc` instead of `--no-eslintrc`.
.option('eslintrc', {
type: 'boolean',
describe: 'Enable use of configuration from .eslintrc.*',
default: cliOptionsDefaults.useEslintrc,
})
.option('config', {
alias: 'c',
type: 'string',
describe: 'Use this configuration, overriding .eslintrc.* config options if present',
})
.option('ext', {
type: 'array',
describe: 'Specify JavaScript file extensions',
})
.nargs('ext', 1)
.option('resolve-plugins-relative-to', {
type: 'string',
describe: 'A folder where plugins should be resolved from, CWD by default',
})
.option('rulesdir', {
type: 'array',
describe: 'Use additional rules from this directory',
})
.nargs('rulesdir', 1)
// Following ESLint, --ignore-path accepts only one path. However, this limitation may be relaxed in the future.
// ref: https://github.com/eslint/eslint/issues/9794
.option('ignore-path', {
type: 'string',
describe: 'Specify path of ignore file',
})
.option('format', {
type: 'string',
describe: 'Specify the format to be used for the `Display problem messages` action',
default: cliOptionsDefaults.formatterName,
})
.option('quiet', {
type: 'boolean',
describe: 'Report errors only',
default: cliOptionsDefaults.quiet,
})
.option('cache', {
type: 'boolean',
describe: 'Only check changed files',
default: cliOptionsDefaults.cache,
})
.option('cache-location', {
type: 'string',
describe: `Path to the cache file or directory`,
default: cliOptionsDefaults.cacheLocation,
})
.example('$0 ./src', 'Lint ./src/ directory')
.example('$0 ./src ./test', 'Lint multiple directories')
.example("$0 './src/**/*.{ts,tsx,vue}'", 'Lint with glob pattern')
.example('$0 ./src --ext .ts,.tsx,.vue', 'Lint with custom extensions')
.example('$0 ./src --rulesdir ./rules', 'Lint with custom rules')
.example('$0 ./src --no-eslintrc --config ./.eslintrc.ci.js', 'Lint with custom config')
.parseSync();
// NOTE: convert `string` type because yargs convert `'10'` (`string` type) into `10` (`number` type)
// and `lintFiles` only accepts `string[]`.
const patterns = parsedArgv._.map((pattern) => pattern.toString());
const rulePaths = parsedArgv.rulesdir?.map((rulePath) => rulePath.toString());
const extensions = parsedArgv.ext
?.map((extension) => extension.toString())
// map '.js,.ts' into ['.js', '.ts']
.flatMap((extension) => extension.split(','));
const formatterName = parsedArgv.format;
const options = {
'eslintrc': { type: 'boolean', default: cliOptionsDefaults.useEslintrc },
'config': { type: 'string', short: 'c' },
'ext': { type: 'string', multiple: true },
'resolve-plugins-relative-to': { type: 'string' },
'rulesdir': { type: 'string', multiple: true },
'ignore-path': { type: 'string' },
'format': { type: 'string', default: cliOptionsDefaults.formatterName },
'quiet': { type: 'boolean', default: cliOptionsDefaults.quiet },
'cache': { type: 'boolean', default: cliOptionsDefaults.cache },
'cache-location': { type: 'string', default: cliOptionsDefaults.cacheLocation },
'version': { type: 'boolean' },
'help': { type: 'boolean' },
} as const;

const { values, positionals } = parseArgs({

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, ubuntu-latest)

src/cli/parse-argv.test.ts > parseArgv > --no-eslintrc

TypeError: Unknown option '--no-eslintrc'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-eslintrc" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:13:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, ubuntu-latest)

src/cli/parse-argv.test.ts > parseArgv > --quiet

TypeError: Unknown option '--no-quiet'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-quiet" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:47:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, ubuntu-latest)

src/cli/parse-argv.test.ts > parseArgv > --cache

TypeError: Unknown option '--no-cache'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-cache" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:51:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, macos-latest)

src/cli/parse-argv.test.ts > parseArgv > --no-eslintrc

TypeError: Unknown option '--no-eslintrc'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-eslintrc" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:13:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, macos-latest)

src/cli/parse-argv.test.ts > parseArgv > --quiet

TypeError: Unknown option '--no-quiet'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-quiet" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:47:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, macos-latest)

src/cli/parse-argv.test.ts > parseArgv > --cache

TypeError: Unknown option '--no-cache'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-cache" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:51:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, windows-latest)

src/cli/parse-argv.test.ts > parseArgv > --no-eslintrc

TypeError: Unknown option '--no-eslintrc'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-eslintrc" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:13:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, windows-latest)

src/cli/parse-argv.test.ts > parseArgv > --quiet

TypeError: Unknown option '--no-quiet'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-quiet" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:47:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 8, windows-latest)

src/cli/parse-argv.test.ts > parseArgv > --cache

TypeError: Unknown option '--no-cache'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-cache" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:51:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, ubuntu-latest)

src/cli/parse-argv.test.ts > parseArgv > --no-eslintrc

TypeError: Unknown option '--no-eslintrc'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-eslintrc" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:13:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, ubuntu-latest)

src/cli/parse-argv.test.ts > parseArgv > --quiet

TypeError: Unknown option '--no-quiet'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-quiet" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:47:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, ubuntu-latest)

src/cli/parse-argv.test.ts > parseArgv > --cache

TypeError: Unknown option '--no-cache'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-cache" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:51:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, macos-latest)

src/cli/parse-argv.test.ts > parseArgv > --no-eslintrc

TypeError: Unknown option '--no-eslintrc'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-eslintrc" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:13:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, macos-latest)

src/cli/parse-argv.test.ts > parseArgv > --quiet

TypeError: Unknown option '--no-quiet'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-quiet" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:47:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, macos-latest)

src/cli/parse-argv.test.ts > parseArgv > --cache

TypeError: Unknown option '--no-cache'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-cache" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:51:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, windows-latest)

src/cli/parse-argv.test.ts > parseArgv > --no-eslintrc

TypeError: Unknown option '--no-eslintrc'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-eslintrc" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:13:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, windows-latest)

src/cli/parse-argv.test.ts > parseArgv > --quiet

TypeError: Unknown option '--no-quiet'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-quiet" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:47:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }

Check failure on line 47 in src/cli/parse-argv.ts

View workflow job for this annotation

GitHub Actions / test (18, 9, windows-latest)

src/cli/parse-argv.test.ts > parseArgv > --cache

TypeError: Unknown option '--no-cache'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "--no-cache" ❯ Module.parseArgv src/cli/parse-argv.ts:47:35 ❯ src/cli/parse-argv.test.ts:51:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION' }
allowPositionals: true,
allowNegative: true,
Copy link
Owner Author

@mizdra mizdra Sep 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowNegative requires Node.js 20.16.0+ or v22.4.0+.

https://nodejs.org/api/util.html#utilparseargsconfig

strict: true,
args: argv.slice(2),
options,
});

if (values.version) {
console.log(VERSION);
// eslint-disable-next-line n/no-process-exit
process.exit(0);
}

if (values.help) {
console.log(`
eslint-interactive [file.js] [dir]

Options:
--help Show help [boolean]
--version Show version number [boolean]
--eslintrc Enable use of configuration from .eslintrc.* [boolean] [default: true]
-c, --config Use this configuration, overriding .eslintrc.* config options if present [string]
--resolve-plugins-relative-to A folder where plugins should be resolved from, CWD by default [string]
--ext Specify JavaScript file extensions [array]
--rulesdir Use additional rules from this directory [array]
--ignore-path Specify path of ignore file [string]
--format Specify the format to be used for the \`Display problem messages\` action [string] [default: "codeframe"]
--quiet Report errors only [boolean] [default: false]
--cache Only check changed files [boolean] [default: true]
--cache-location Path to the cache file or directory

Examples:
eslint-interactive ./src Lint ./src/ directory
eslint-interactive ./src ./test Lint multiple directories
eslint-interactive './src/**/*.{ts,tsx,vue}' Lint with glob pattern
eslint-interactive ./src --ext .ts,.tsx,.vue Lint with custom extensions
eslint-interactive ./src --rulesdir ./rules Lint with custom rules
eslint-interactive ./src --no-eslintrc --config ./.eslintrc.ci.js Lint with custom config
`);
// eslint-disable-next-line n/no-process-exit
process.exit(0);
}

const patterns = positionals.map((pattern) => pattern.toString());
const rulePaths = values.rulesdir?.map((rulePath) => rulePath.toString());
const extensions = values.ext?.map((extension) => extension.toString()).flatMap((extension) => extension.split(','));
const formatterName = values.format;

return {
patterns,
formatterName,
quiet: parsedArgv.quiet,
useEslintrc: parsedArgv.eslintrc,
overrideConfigFile: parsedArgv.config,
quiet: values.quiet,
useEslintrc: values.eslintrc,
overrideConfigFile: values.config,
extensions,
rulePaths,
ignorePath: parsedArgv.ignorePath,
cache: parsedArgv.cache,
cacheLocation: parsedArgv['cache-location'],
resolvePluginsRelativeTo: parsedArgv['resolve-plugins-relative-to'],
ignorePath: values['ignore-path'],
cache: values.cache,
cacheLocation: values['cache-location'],
resolvePluginsRelativeTo: values['resolve-plugins-relative-to'],
};
}
Loading