1 | 'use strict';
|
2 | const path = require('path');
|
3 | const eslint = require('eslint');
|
4 | const globby = require('globby');
|
5 | const isEqual = require('lodash/isEqual');
|
6 | const uniq = require('lodash/uniq');
|
7 | const micromatch = require('micromatch');
|
8 | const arrify = require('arrify');
|
9 | const pReduce = require('p-reduce');
|
10 | const {cosmiconfig, defaultLoaders} = require('cosmiconfig');
|
11 | const {CONFIG_FILES, MODULE_NAME, DEFAULT_IGNORES} = require('./lib/constants');
|
12 | const {
|
13 | normalizeOptions,
|
14 | getIgnores,
|
15 | mergeWithFileConfig,
|
16 | mergeWithFileConfigs,
|
17 | buildConfig,
|
18 | mergeOptions
|
19 | } = require('./lib/options-manager');
|
20 |
|
21 | const mergeReports = reports => {
|
22 |
|
23 | let results = [];
|
24 | let errorCount = 0;
|
25 | let warningCount = 0;
|
26 |
|
27 | for (const report of reports) {
|
28 | results = results.concat(report.results);
|
29 | errorCount += report.errorCount;
|
30 | warningCount += report.warningCount;
|
31 | }
|
32 |
|
33 | return {
|
34 | errorCount,
|
35 | warningCount,
|
36 | results
|
37 | };
|
38 | };
|
39 |
|
40 | const processReport = (report, options) => {
|
41 | report.results = options.quiet ? eslint.CLIEngine.getErrorResults(report.results) : report.results;
|
42 | return report;
|
43 | };
|
44 |
|
45 | const runEslint = (paths, options) => {
|
46 | const engine = new eslint.CLIEngine(options);
|
47 | const report = engine.executeOnFiles(
|
48 | paths.filter(path => !engine.isPathIgnored(path)),
|
49 | options
|
50 | );
|
51 | return processReport(report, options);
|
52 | };
|
53 |
|
54 | const globFiles = async (patterns, {ignores, extensions, cwd}) => (
|
55 | await globby(
|
56 | patterns.length === 0 ? [`**/*.{${extensions.join(',')}}`] : arrify(patterns),
|
57 | {ignore: ignores, gitignore: true, cwd}
|
58 | )).filter(file => extensions.includes(path.extname(file).slice(1))).map(file => path.resolve(cwd, file));
|
59 |
|
60 | const lintText = (string, options) => {
|
61 | const {options: foundOptions, prettierOptions} = mergeWithFileConfig(normalizeOptions(options));
|
62 | options = buildConfig(foundOptions, prettierOptions);
|
63 |
|
64 | if (options.ignores && !isEqual(getIgnores({}), options.ignores) && typeof options.filename !== 'string') {
|
65 | throw new Error('The `ignores` option requires the `filename` option to be defined.');
|
66 | }
|
67 |
|
68 | const engine = new eslint.CLIEngine(options);
|
69 |
|
70 | if (options.filename) {
|
71 | const filename = path.relative(options.cwd, options.filename);
|
72 |
|
73 | if (
|
74 | micromatch.isMatch(filename, options.ignores) ||
|
75 | globby.gitignore.sync({cwd: options.cwd, ignore: options.ignores})(options.filename) ||
|
76 | engine.isPathIgnored(options.filename)
|
77 | ) {
|
78 | return {
|
79 | errorCount: 0,
|
80 | warningCount: 0,
|
81 | results: [{
|
82 | errorCount: 0,
|
83 | filePath: filename,
|
84 | messages: [],
|
85 | warningCount: 0
|
86 | }]
|
87 | };
|
88 | }
|
89 | }
|
90 |
|
91 | const report = engine.executeOnText(string, options.filename);
|
92 |
|
93 | return processReport(report, options);
|
94 | };
|
95 |
|
96 | const lintFiles = async (patterns, options = {}) => {
|
97 | options.cwd = path.resolve(options.cwd || process.cwd());
|
98 | const configExplorer = cosmiconfig(MODULE_NAME, {searchPlaces: CONFIG_FILES, loaders: {noExt: defaultLoaders['.json']}, stopDir: options.cwd});
|
99 |
|
100 | const configFiles = (await Promise.all(
|
101 | (await globby(
|
102 | CONFIG_FILES.map(configFile => `**/${configFile}`),
|
103 | {ignore: DEFAULT_IGNORES, gitignore: true, cwd: options.cwd}
|
104 | )).map(async configFile => configExplorer.load(path.resolve(options.cwd, configFile)))
|
105 | )).filter(Boolean);
|
106 |
|
107 | const paths = configFiles.length > 0 ?
|
108 | await pReduce(
|
109 | configFiles,
|
110 | async (paths, {filepath, config}) =>
|
111 | [...paths, ...(await globFiles(patterns, {...mergeOptions(options, config), cwd: path.dirname(filepath)}))],
|
112 | []) :
|
113 | await globFiles(patterns, mergeOptions(options));
|
114 |
|
115 | return mergeReports((await mergeWithFileConfigs(uniq(paths), options, configFiles)).map(
|
116 | ({files, options, prettierOptions}) => runEslint(files, buildConfig(options, prettierOptions)))
|
117 | );
|
118 | };
|
119 |
|
120 | module.exports = {
|
121 | getFormatter: eslint.CLIEngine.getFormatter,
|
122 | getErrorResults: eslint.CLIEngine.getErrorResults,
|
123 | outputFixes: eslint.CLIEngine.outputFixes,
|
124 | lintText,
|
125 | lintFiles
|
126 | };
|