Skip to content

Commit

Permalink
Move from deprecated ESLint.CLIEngine to ESLint (#534)
Browse files Browse the repository at this point in the history
  • Loading branch information
Richienb committed May 9, 2021
1 parent 9934b84 commit 0480d80
Show file tree
Hide file tree
Showing 8 changed files with 443 additions and 293 deletions.
18 changes: 9 additions & 9 deletions cli-main.js
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
@@ -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
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
};

0 comments on commit 0480d80

Please sign in to comment.