diff --git a/package.json b/package.json index 99561e63..ea16fb81 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,7 @@ "@types/eslint": "^8.56.12", "@types/estraverse": "^5.1.6", "@types/estree": "^1.0.5", - "@types/node": "^20.8.10", - "@types/yargs": "^17.0.29", + "@types/node": "^22.7.4", "dedent": "^1.5.1", "eslint": "^8.57.0", "npm-run-all2": "^5.0.0", @@ -60,8 +59,7 @@ "is-installed-globally": "^1.0.0", "ora": "^7.0.1", "table": "^6.8.1", - "terminal-link": "^3.0.0", - "yargs": "^17.7.2" + "terminal-link": "^3.0.0" }, "peerDependencies": { "eslint": "^8.45.0 || ^9.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 189c1cea..4e63b210 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,9 +38,6 @@ importers: terminal-link: specifier: ^3.0.0 version: 3.0.0 - yargs: - specifier: ^17.7.2 - version: 17.7.2 devDependencies: '@mizdra/eslint-config-mizdra': specifier: 2.1.0-alpha.0 @@ -67,11 +64,8 @@ importers: specifier: ^1.0.5 version: 1.0.5 '@types/node': - specifier: ^20.8.10 - version: 20.8.10 - '@types/yargs': - specifier: ^17.0.29 - version: 17.0.29 + specifier: ^22.7.4 + version: 22.7.4 dedent: specifier: ^1.5.1 version: 1.5.1 @@ -92,7 +86,7 @@ importers: version: 5.6.2 vitest: specifier: ^2.1.1 - version: 2.1.1(@types/node@20.8.10) + version: 2.1.1(@types/node@22.7.4) e2e-test/import-as-esm-from-esm: dependencies: @@ -585,10 +579,10 @@ packages: resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==} dev: true - /@types/node@20.8.10: - resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==} + /@types/node@22.7.4: + resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} dependencies: - undici-types: 5.26.5 + undici-types: 6.19.8 dev: true /@types/normalize-package-data@2.4.1: @@ -599,16 +593,6 @@ packages: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true - /@types/yargs-parser@21.0.2: - resolution: {integrity: sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==} - dev: true - - /@types/yargs@17.0.29: - resolution: {integrity: sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==} - dependencies: - '@types/yargs-parser': 21.0.2 - dev: true - /@typescript-eslint/eslint-plugin@5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.57.0)(typescript@5.6.2): resolution: {integrity: sha512-yVrXupeHjRxLDcPKL10sGQ/QlVrA8J5IYOEWVqk0lJaSZP7X5DfnP7Ns3cc74/blmbipQ1htFNVGsHX6wsYm0A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -767,7 +751,7 @@ packages: '@vitest/spy': 2.1.1 estree-walker: 3.0.3 magic-string: 0.30.11 - vite: 5.0.10(@types/node@20.8.10) + vite: 5.0.10(@types/node@22.7.4) dev: true /@vitest/pretty-format@2.1.1: @@ -1108,15 +1092,6 @@ packages: engines: {node: '>=6'} dev: false - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: false - /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -1363,11 +1338,6 @@ packages: '@esbuild/win32-x64': 0.19.9 dev: true - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - dev: false - /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1798,11 +1768,6 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: false - /get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true @@ -2722,11 +2687,6 @@ packages: jsesc: 0.5.0 dev: true - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: false - /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -3174,8 +3134,8 @@ packages: which-boxed-primitive: 1.0.2 dev: true - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + /undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} dev: true /uri-js@4.4.0: @@ -3201,7 +3161,7 @@ packages: spdx-expression-parse: 3.0.0 dev: true - /vite-node@2.1.1(@types/node@20.8.10): + /vite-node@2.1.1(@types/node@22.7.4): resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -3209,7 +3169,7 @@ packages: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 - vite: 5.0.10(@types/node@20.8.10) + vite: 5.0.10(@types/node@22.7.4) transitivePeerDependencies: - '@types/node' - less @@ -3221,7 +3181,7 @@ packages: - terser dev: true - /vite@5.0.10(@types/node@20.8.10): + /vite@5.0.10(@types/node@22.7.4): resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -3249,7 +3209,7 @@ packages: terser: optional: true dependencies: - '@types/node': 20.8.10 + '@types/node': 22.7.4 esbuild: 0.19.9 postcss: 8.4.32 rollup: 4.9.1 @@ -3257,7 +3217,7 @@ packages: fsevents: 2.3.3 dev: true - /vitest@2.1.1(@types/node@20.8.10): + /vitest@2.1.1(@types/node@22.7.4): resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -3282,7 +3242,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.8.10 + '@types/node': 22.7.4 '@vitest/expect': 2.1.1 '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.0.10) '@vitest/pretty-format': 2.1.1 @@ -3299,8 +3259,8 @@ packages: tinyexec: 0.3.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.0.10(@types/node@20.8.10) - vite-node: 2.1.1(@types/node@20.8.10) + vite: 5.0.10(@types/node@22.7.4) + vite-node: 2.1.1(@types/node@22.7.4) why-is-node-running: 2.3.0 transitivePeerDependencies: - less @@ -3359,15 +3319,6 @@ packages: string-width: 5.1.2 dev: false - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: false - /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} @@ -3381,33 +3332,10 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: false - /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: false - - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - dependencies: - cliui: 8.0.1 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - dev: false - /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/src/cli/parse-argv.test.ts b/src/cli/parse-argv.test.ts index e9da4e79..418de152 100644 --- a/src/cli/parse-argv.test.ts +++ b/src/cli/parse-argv.test.ts @@ -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']); @@ -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'); diff --git a/src/cli/parse-argv.ts b/src/cli/parse-argv.ts index 4186684d..61db8db3 100644 --- a/src/cli/parse-argv.ts +++ b/src/cli/parse-argv.ts @@ -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'; @@ -29,90 +29,81 @@ export const cliOptionsDefaults = { /** 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({ + allowPositionals: true, + allowNegative: true, + 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'], }; }