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

Move from deprecated ESLint.CLIEngine to ESLint #534

Merged
merged 14 commits into from
May 9, 2021
18 changes: 9 additions & 9 deletions cli-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ if (process.env.GITHUB_ACTIONS && !options.fix && !options.reporter) {
options.quiet = true;
}

const log = report => {
const reporter = options.reporter || process.env.GITHUB_ACTIONS ? xo.getFormatter(options.reporter || 'compact') : formatterPretty;
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));
process.exitCode = report.errorCount === 0 ? 0 : 1;
};
Expand Down Expand Up @@ -186,18 +186,18 @@ if (options.nodeVersion) {
process.exit(1);
}

options.filename = options.printConfig;
const config = xo.getConfig(options);
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.filename = options.stdinFilename;
options.filePath = options.stdinFilename;
}

if (options.fix) {
const result = xo.lintText(stdin, options).results[0];
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;
Expand All @@ -208,18 +208,18 @@ if (options.nodeVersion) {
process.exit(1);
}

log(xo.lintText(stdin, options));
await log(await xo.lintText(stdin, options));
} else {
const report = await xo.lintFiles(input, options);

if (options.fix) {
xo.outputFixes(report);
await xo.outputFixes(report);
}

if (options.open) {
openReport(report);
}

log(report);
await log(report);
}
})();
140 changes: 99 additions & 41 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
'use strict';
const path = require('path');
const eslint = require('eslint');
const {ESLint} = require('eslint');
const globby = require('globby');
const isEqual = require('lodash/isEqual');
const uniq = require('lodash/uniq');
const pick = require('lodash/pick');
const omit = require('lodash/omit');
const micromatch = require('micromatch');
const arrify = require('arrify');
const pReduce = require('p-reduce');
const pMap = require('p-map');
const {cosmiconfig, defaultLoaders} = require('cosmiconfig');
const {CONFIG_FILES, MODULE_NAME, DEFAULT_IGNORES} = require('./lib/constants');
const defineLazyProperty = require('define-lazy-prop');
const pFilter = require('p-filter');
const {CONFIG_FILES, MODULE_NAME, DEFAULT_IGNORES, LINT_METHOD_OPTIONS} = require('./lib/constants');
const {
normalizeOptions,
getIgnores,
Expand Down Expand Up @@ -37,18 +42,68 @@ const mergeReports = reports => {
};
};

const processReport = (report, options) => {
report.results = options.quiet ? eslint.CLIEngine.getErrorResults(report.results) : report.results;
return report;
const getReportStatistics = results => {
let errorCount = 0;
let warningCount = 0;
let fixableErrorCount = 0;
let fixableWarningCount = 0;

for (const {
errorCount: currentErrorCount,
warningCount: currentWarningCount,
fixableErrorCount: currentFixableErrorCount,
fixableWarningCount: currentFixableWarningCount
} of results) {
errorCount += currentErrorCount;
warningCount += currentWarningCount;
fixableErrorCount += currentFixableErrorCount;
fixableWarningCount += currentFixableWarningCount;
}

return {
errorCount,
warningCount,
fixableErrorCount,
fixableWarningCount
};
};

const processReport = (report, {isQuiet = false} = {}) => {
if (isQuiet) {
report = ESLint.getErrorResults(report);
}

const result = {
results: report,
...getReportStatistics(report)
};

defineLazyProperty(result, 'usedDeprecatedRules', () => {
const seenRules = new Set();
const rules = [];

for (const {usedDeprecatedRules} of report) {
for (const rule of usedDeprecatedRules) {
if (seenRules.has(rule.ruleId)) {
continue;
}

seenRules.add(rule.ruleId);
rules.push(rule);
}
}

return rules;
});

return result;
};

const runEslint = (paths, options) => {
const engine = new eslint.CLIEngine(options);
const report = engine.executeOnFiles(
paths.filter(path => !engine.isPathIgnored(path)),
options
);
return processReport(report, options);
const runEslint = async (paths, options, processorOptions) => {
const engine = new ESLint(options);

const report = await engine.lintFiles(await pFilter(paths, async path => !(await engine.isPathIgnored(path))));
return processReport(report, processorOptions);
};

const globFiles = async (patterns, {ignores, extensions, cwd}) => (
Expand All @@ -57,30 +112,30 @@ const globFiles = async (patterns, {ignores, extensions, cwd}) => (
{ignore: ignores, gitignore: true, cwd}
)).filter(file => extensions.includes(path.extname(file).slice(1))).map(file => path.resolve(cwd, file));

const getConfig = options => {
const getConfig = async options => {
const {options: foundOptions, prettierOptions} = mergeWithFileConfig(normalizeOptions(options));
options = buildConfig(foundOptions, prettierOptions);
const engine = new eslint.CLIEngine(options);
return engine.getConfigForFile(options.filename);
const engine = new ESLint(omit(options, LINT_METHOD_OPTIONS));
return engine.calculateConfigForFile(options.filePath);
};

const lintText = (string, options) => {
const {options: foundOptions, prettierOptions} = mergeWithFileConfig(normalizeOptions(options));
options = buildConfig(foundOptions, prettierOptions);
const lintText = async (string, inputOptions = {}) => {
const {options: foundOptions, prettierOptions} = mergeWithFileConfig(normalizeOptions(inputOptions));
const options = buildConfig(foundOptions, prettierOptions);

if (options.ignores && !isEqual(getIgnores({}), options.ignores) && typeof options.filename !== 'string') {
throw new Error('The `ignores` option requires the `filename` option to be defined.');
if (options.baseConfig.ignorePatterns && !isEqual(getIgnores({}), options.baseConfig.ignorePatterns) && typeof options.filePath !== 'string') {
throw new Error('The `ignores` option requires the `filePath` option to be defined.');
}

const engine = new eslint.CLIEngine(options);
const engine = new ESLint(omit(options, LINT_METHOD_OPTIONS));

if (options.filename) {
const filename = path.relative(options.cwd, options.filename);
if (options.filePath) {
const filename = path.relative(options.cwd, options.filePath);

if (
micromatch.isMatch(filename, options.ignores) ||
globby.gitignore.sync({cwd: options.cwd, ignore: options.ignores})(options.filename) ||
engine.isPathIgnored(options.filename)
micromatch.isMatch(filename, options.baseConfig.ignorePatterns) ||
globby.gitignore.sync({cwd: options.cwd, ignore: options.baseConfig.ignorePatterns})(options.filePath) ||
await engine.isPathIgnored(options.filePath)
) {
return {
errorCount: 0,
Expand All @@ -95,39 +150,42 @@ const lintText = (string, options) => {
}
}

const report = engine.executeOnText(string, options.filename);
const report = await engine.lintText(string, pick(options, LINT_METHOD_OPTIONS));

return processReport(report, options);
return processReport(report, {isQuiet: inputOptions.quiet});
};

const lintFiles = async (patterns, options = {}) => {
options.cwd = path.resolve(options.cwd || process.cwd());
const configExplorer = cosmiconfig(MODULE_NAME, {searchPlaces: CONFIG_FILES, loaders: {noExt: defaultLoaders['.json']}, stopDir: options.cwd});
const lintFiles = async (patterns, inputOptions = {}) => {
inputOptions.cwd = path.resolve(inputOptions.cwd || process.cwd());
const configExplorer = cosmiconfig(MODULE_NAME, {searchPlaces: CONFIG_FILES, loaders: {noExt: defaultLoaders['.json']}, stopDir: inputOptions.cwd});

const configFiles = (await Promise.all(
(await globby(
CONFIG_FILES.map(configFile => `**/${configFile}`),
{ignore: DEFAULT_IGNORES, gitignore: true, cwd: options.cwd}
)).map(async configFile => configExplorer.load(path.resolve(options.cwd, configFile)))
{ignore: DEFAULT_IGNORES, gitignore: true, cwd: inputOptions.cwd}
)).map(async configFile => configExplorer.load(path.resolve(inputOptions.cwd, configFile)))
)).filter(Boolean);

const paths = configFiles.length > 0 ?
await pReduce(
configFiles,
async (paths, {filepath, config}) =>
[...paths, ...(await globFiles(patterns, {...mergeOptions(options, config), cwd: path.dirname(filepath)}))],
[...paths, ...(await globFiles(patterns, {...mergeOptions(inputOptions, config), cwd: path.dirname(filepath)}))],
[]) :
await globFiles(patterns, mergeOptions(options));
await globFiles(patterns, mergeOptions(inputOptions));

return mergeReports(await pMap(await mergeWithFileConfigs(uniq(paths), inputOptions, configFiles), async ({files, options, prettierOptions}) => runEslint(files, buildConfig(options, prettierOptions), {isQuiet: options.quiet})));
};

return mergeReports((await mergeWithFileConfigs(uniq(paths), options, configFiles)).map(
({files, options, prettierOptions}) => runEslint(files, buildConfig(options, prettierOptions)))
);
const getFormatter = async name => {
const {format} = await new ESLint().loadFormatter(name);
return format;
};

module.exports = {
getFormatter: eslint.CLIEngine.getFormatter,
getErrorResults: eslint.CLIEngine.getErrorResults,
outputFixes: eslint.CLIEngine.outputFixes,
getFormatter,
getErrorResults: ESLint.getErrorResults,
outputFixes: async ({results}) => ESLint.outputFixes(results),
getConfig,
lintText,
lintFiles
Expand Down
5 changes: 4 additions & 1 deletion lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ const TSCONFIG_DEFFAULTS = {

const CACHE_DIR_NAME = 'xo-linter';

const LINT_METHOD_OPTIONS = ['filePath', 'warnIgnored'];

module.exports = {
DEFAULT_IGNORES,
DEFAULT_EXTENSION,
Expand All @@ -147,5 +149,6 @@ module.exports = {
CONFIG_FILES,
MERGE_OPTIONS_CONCAT,
TSCONFIG_DEFFAULTS,
CACHE_DIR_NAME
CACHE_DIR_NAME,
LINT_METHOD_OPTIONS
};