UNPKG

xo

Version:

JavaScript/TypeScript linter (ESLint wrapper) with great defaults

225 lines (201 loc) 5.82 kB
#!/usr/bin/env node /* eslint-disable unicorn/prefer-top-level-await */ import process from 'node:process'; import getStdin from 'get-stdin'; import meow from 'meow'; import formatterPretty from 'eslint-formatter-pretty'; import semver from 'semver'; import openReport from './lib/open-report.js'; import xo from './index.js'; const cli = meow(` Usage $ xo [<file|glob> ...] Options --fix Automagically fix issues --reporter Reporter to use --env Environment preset [Can be set multiple times] --global Global variable [Can be set multiple times] --ignore Additional paths to ignore [Can be set multiple times] --space Use space indent instead of tabs [Default: 2] --no-semicolon Prevent use of semicolons --prettier Conform to Prettier code style --node-version Range of Node.js version to support --plugin Include third-party plugins [Can be set multiple times] --extend Extend defaults with a custom config [Can be set multiple times] --open Open files with issues in your editor --quiet Show only errors and no warnings --extension Additional extension to lint [Can be set multiple times] --cwd=<dir> Working directory for files --stdin Validate/fix code from stdin --stdin-filename Specify a filename for the --stdin option --print-config Print the effective ESLint config for the given file Examples $ xo $ xo index.js $ xo *.js !foo.js $ xo --space $ xo --env=node --env=mocha $ xo --plugin=react $ xo --plugin=html --extension=html $ echo 'const x=true' | xo --stdin --fix $ xo --print-config=index.js Tips - Add XO to your project with \`npm init xo\`. - Put options in package.json instead of using flags so other tools can read it. `, { importMeta: import.meta, autoVersion: false, booleanDefault: undefined, flags: { fix: { type: 'boolean', }, reporter: { type: 'string', }, env: { type: 'string', isMultiple: true, }, global: { type: 'string', isMultiple: true, }, ignore: { type: 'string', isMultiple: true, }, space: { type: 'string', }, semicolon: { type: 'boolean', }, prettier: { type: 'boolean', }, nodeVersion: { type: 'string', }, plugin: { type: 'string', isMultiple: true, }, extend: { type: 'string', isMultiple: true, }, open: { type: 'boolean', }, quiet: { type: 'boolean', }, extension: { type: 'string', isMultiple: true, }, cwd: { type: 'string', }, printConfig: { type: 'string', }, stdin: { type: 'boolean', }, stdinFilename: { type: 'string', }, }, }); const {input, flags: options, showVersion} = cli; // TODO: Fix this properly instead of the below workaround. // Revert behavior of meow >8 to pre-8 (7.1.1) for flags using `isMultiple: true`. // Otherwise, options defined in package.json can't be merged by lib/options-manager.js `mergeOptions()`. for (const key in options) { if (Array.isArray(options[key]) && options[key].length === 0) { delete options[key]; } } // Make data types for `options.space` match those of the API // Check for string type because `xo --no-space` sets `options.space` to `false` if (typeof options.space === 'string') { if (/^\d+$/u.test(options.space)) { options.space = Number.parseInt(options.space, 10); } else if (options.space === 'true') { options.space = true; } else if (options.space === 'false') { options.space = false; } else { if (options.space !== '') { // Assume `options.space` was set to a filename when run as `xo --space file.js` input.push(options.space); } options.space = true; } } if (process.env.GITHUB_ACTIONS && !options.fix && !options.reporter) { options.quiet = true; } const log = async report => { const reporter = options.reporter || process.env.GITHUB_ACTIONS ? await xo.getFormatter(options.reporter || 'compact') : formatterPretty; process.stdout.write(reporter(report.results, {rulesMeta: report.rulesMeta})); process.exitCode = report.errorCount === 0 ? 0 : 1; }; // `xo -` => `xo --stdin` if (input[0] === '-') { options.stdin = true; input.shift(); } if (options.version) { showVersion(); } if (options.nodeVersion) { if (options.nodeVersion === 'false') { options.nodeVersion = false; } else if (!semver.validRange(options.nodeVersion)) { console.error('The `--node-engine` flag must be a valid semver range (for example `>=6`)'); process.exit(1); } } (async () => { if (typeof options.printConfig === 'string') { if (input.length > 0 || options.printConfig === '') { console.error('The `--print-config` flag must be used with exactly one filename'); process.exit(1); } if (options.stdin) { console.error('The `--print-config` flag is not supported on stdin'); process.exit(1); } options.filePath = options.printConfig; const config = await xo.getConfig(options); console.log(JSON.stringify(config, undefined, '\t')); } else if (options.stdin) { const stdin = await getStdin(); if (options.stdinFilename) { options.filePath = options.stdinFilename; } if (options.fix) { const {results: [result]} = await xo.lintText(stdin, options); // If there is no output, pass the stdin back out process.stdout.write((result && result.output) || stdin); return; } if (options.open) { console.error('The `--open` flag is not supported on stdin'); process.exit(1); } await log(await xo.lintText(stdin, options)); } else { const report = await xo.lintFiles(input, options); if (options.fix) { await xo.outputFixes(report); } if (options.open) { openReport(report); } await log(report); } })();