Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / cli-engine / cli-engine.js
1 /**
2  * @fileoverview Main CLI object.
3  * @author Nicholas C. Zakas
4  */
5
6 "use strict";
7
8 /*
9  * The CLI object should *not* call process.exit() directly. It should only return
10  * exit codes. This allows other programs to use the CLI object and still control
11  * when the program exits.
12  */
13
14 //------------------------------------------------------------------------------
15 // Requirements
16 //------------------------------------------------------------------------------
17
18 const fs = require("fs");
19 const path = require("path");
20 const defaultOptions = require("../../conf/default-cli-options");
21 const pkg = require("../../package.json");
22 const ConfigOps = require("../shared/config-ops");
23 const naming = require("../shared/naming");
24 const ModuleResolver = require("../shared/relative-module-resolver");
25 const { Linter } = require("../linter");
26 const builtInRules = require("../rules");
27 const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory");
28 const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array");
29 const { FileEnumerator } = require("./file-enumerator");
30 const hash = require("./hash");
31 const LintResultCache = require("./lint-result-cache");
32
33 const debug = require("debug")("eslint:cli-engine");
34 const validFixTypes = new Set(["problem", "suggestion", "layout"]);
35
36 //------------------------------------------------------------------------------
37 // Typedefs
38 //------------------------------------------------------------------------------
39
40 // For VSCode IntelliSense
41 /** @typedef {import("../shared/types").ConfigData} ConfigData */
42 /** @typedef {import("../shared/types").LintMessage} LintMessage */
43 /** @typedef {import("../shared/types").ParserOptions} ParserOptions */
44 /** @typedef {import("../shared/types").Plugin} Plugin */
45 /** @typedef {import("../shared/types").RuleConf} RuleConf */
46 /** @typedef {import("../shared/types").Rule} Rule */
47 /** @typedef {ReturnType<CascadingConfigArrayFactory["getConfigArrayForFile"]>} ConfigArray */
48 /** @typedef {ReturnType<ConfigArray["extractConfig"]>} ExtractedConfig */
49
50 /**
51  * The options to configure a CLI engine with.
52  * @typedef {Object} CLIEngineOptions
53  * @property {boolean} allowInlineConfig Enable or disable inline configuration comments.
54  * @property {ConfigData} baseConfig Base config object, extended by all configs used with this CLIEngine instance
55  * @property {boolean} cache Enable result caching.
56  * @property {string} cacheLocation The cache file to use instead of .eslintcache.
57  * @property {string} configFile The configuration file to use.
58  * @property {string} cwd The value to use for the current working directory.
59  * @property {string[]} envs An array of environments to load.
60  * @property {string[]} extensions An array of file extensions to check.
61  * @property {boolean|Function} fix Execute in autofix mode. If a function, should return a boolean.
62  * @property {string[]} fixTypes Array of rule types to apply fixes for.
63  * @property {string[]} globals An array of global variables to declare.
64  * @property {boolean} ignore False disables use of .eslintignore.
65  * @property {string} ignorePath The ignore file to use instead of .eslintignore.
66  * @property {string|string[]} ignorePattern One or more glob patterns to ignore.
67  * @property {boolean} useEslintrc False disables looking for .eslintrc
68  * @property {string} parser The name of the parser to use.
69  * @property {ParserOptions} parserOptions An object of parserOption settings to use.
70  * @property {string[]} plugins An array of plugins to load.
71  * @property {Record<string,RuleConf>} rules An object of rules to use.
72  * @property {string[]} rulePaths An array of directories to load custom rules from.
73  * @property {boolean} reportUnusedDisableDirectives `true` adds reports for unused eslint-disable directives
74  * @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
75  * @property {string} resolvePluginsRelativeTo The folder where plugins should be resolved from, defaulting to the CWD
76  */
77
78 /**
79  * A linting result.
80  * @typedef {Object} LintResult
81  * @property {string} filePath The path to the file that was linted.
82  * @property {LintMessage[]} messages All of the messages for the result.
83  * @property {number} errorCount Number of errors for the result.
84  * @property {number} warningCount Number of warnings for the result.
85  * @property {number} fixableErrorCount Number of fixable errors for the result.
86  * @property {number} fixableWarningCount Number of fixable warnings for the result.
87  * @property {string} [source] The source code of the file that was linted.
88  * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
89  */
90
91 /**
92  * Information of deprecated rules.
93  * @typedef {Object} DeprecatedRuleInfo
94  * @property {string} ruleId The rule ID.
95  * @property {string[]} replacedBy The rule IDs that replace this deprecated rule.
96  */
97
98 /**
99  * Linting results.
100  * @typedef {Object} LintReport
101  * @property {LintResult[]} results All of the result.
102  * @property {number} errorCount Number of errors for the result.
103  * @property {number} warningCount Number of warnings for the result.
104  * @property {number} fixableErrorCount Number of fixable errors for the result.
105  * @property {number} fixableWarningCount Number of fixable warnings for the result.
106  * @property {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules.
107  */
108
109 /**
110  * Private data for CLIEngine.
111  * @typedef {Object} CLIEngineInternalSlots
112  * @property {Map<string, Plugin>} additionalPluginPool The map for additional plugins.
113  * @property {string} cacheFilePath The path to the cache of lint results.
114  * @property {CascadingConfigArrayFactory} configArrayFactory The factory of configs.
115  * @property {(filePath: string) => boolean} defaultIgnores The default predicate function to check if a file ignored or not.
116  * @property {FileEnumerator} fileEnumerator The file enumerator.
117  * @property {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used.
118  * @property {LintResultCache|null} lintResultCache The cache of lint results.
119  * @property {Linter} linter The linter instance which has loaded rules.
120  * @property {CLIEngineOptions} options The normalized options of this instance.
121  */
122
123 //------------------------------------------------------------------------------
124 // Helpers
125 //------------------------------------------------------------------------------
126
127 /** @type {WeakMap<CLIEngine, CLIEngineInternalSlots>} */
128 const internalSlotsMap = new WeakMap();
129
130 /**
131  * Determines if each fix type in an array is supported by ESLint and throws
132  * an error if not.
133  * @param {string[]} fixTypes An array of fix types to check.
134  * @returns {void}
135  * @throws {Error} If an invalid fix type is found.
136  */
137 function validateFixTypes(fixTypes) {
138     for (const fixType of fixTypes) {
139         if (!validFixTypes.has(fixType)) {
140             throw new Error(`Invalid fix type "${fixType}" found.`);
141         }
142     }
143 }
144
145 /**
146  * It will calculate the error and warning count for collection of messages per file
147  * @param {LintMessage[]} messages Collection of messages
148  * @returns {Object} Contains the stats
149  * @private
150  */
151 function calculateStatsPerFile(messages) {
152     return messages.reduce((stat, message) => {
153         if (message.fatal || message.severity === 2) {
154             stat.errorCount++;
155             if (message.fix) {
156                 stat.fixableErrorCount++;
157             }
158         } else {
159             stat.warningCount++;
160             if (message.fix) {
161                 stat.fixableWarningCount++;
162             }
163         }
164         return stat;
165     }, {
166         errorCount: 0,
167         warningCount: 0,
168         fixableErrorCount: 0,
169         fixableWarningCount: 0
170     });
171 }
172
173 /**
174  * It will calculate the error and warning count for collection of results from all files
175  * @param {LintResult[]} results Collection of messages from all the files
176  * @returns {Object} Contains the stats
177  * @private
178  */
179 function calculateStatsPerRun(results) {
180     return results.reduce((stat, result) => {
181         stat.errorCount += result.errorCount;
182         stat.warningCount += result.warningCount;
183         stat.fixableErrorCount += result.fixableErrorCount;
184         stat.fixableWarningCount += result.fixableWarningCount;
185         return stat;
186     }, {
187         errorCount: 0,
188         warningCount: 0,
189         fixableErrorCount: 0,
190         fixableWarningCount: 0
191     });
192 }
193
194 /**
195  * Processes an source code using ESLint.
196  * @param {Object} config The config object.
197  * @param {string} config.text The source code to verify.
198  * @param {string} config.cwd The path to the current working directory.
199  * @param {string|undefined} config.filePath The path to the file of `text`. If this is undefined, it uses `<text>`.
200  * @param {ConfigArray} config.config The config.
201  * @param {boolean} config.fix If `true` then it does fix.
202  * @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
203  * @param {boolean} config.reportUnusedDisableDirectives If `true` then it reports unused `eslint-disable` comments.
204  * @param {RegExp} config.extensionRegExp The `RegExp` object that tests if a file path has the allowed file extensions.
205  * @param {Linter} config.linter The linter instance to verify.
206  * @returns {LintResult} The result of linting.
207  * @private
208  */
209 function verifyText({
210     text,
211     cwd,
212     filePath: providedFilePath,
213     config,
214     fix,
215     allowInlineConfig,
216     reportUnusedDisableDirectives,
217     extensionRegExp,
218     linter
219 }) {
220     const filePath = providedFilePath || "<text>";
221
222     debug(`Lint ${filePath}`);
223
224     /*
225      * Verify.
226      * `config.extractConfig(filePath)` requires an absolute path, but `linter`
227      * doesn't know CWD, so it gives `linter` an absolute path always.
228      */
229     const filePathToVerify = filePath === "<text>" ? path.join(cwd, filePath) : filePath;
230     const { fixed, messages, output } = linter.verifyAndFix(
231         text,
232         config,
233         {
234             allowInlineConfig,
235             filename: filePathToVerify,
236             fix,
237             reportUnusedDisableDirectives,
238
239             /**
240              * Check if the linter should adopt a given code block or not.
241              * Currently, the linter adopts code blocks if the name matches `--ext` option.
242              * In the future, `overrides` in the configuration would affect the adoption (https://github.com/eslint/rfcs/pull/20).
243              * @param {string} blockFilename The virtual filename of a code block.
244              * @returns {boolean} `true` if the linter should adopt the code block.
245              */
246             filterCodeBlock(blockFilename) {
247                 return extensionRegExp.test(blockFilename);
248             }
249         }
250     );
251
252     // Tweak and return.
253     const result = {
254         filePath,
255         messages,
256         ...calculateStatsPerFile(messages)
257     };
258
259     if (fixed) {
260         result.output = output;
261     }
262     if (
263         result.errorCount + result.warningCount > 0 &&
264         typeof result.output === "undefined"
265     ) {
266         result.source = text;
267     }
268
269     return result;
270 }
271
272 /**
273  * Returns result with warning by ignore settings
274  * @param {string} filePath File path of checked code
275  * @param {string} baseDir  Absolute path of base directory
276  * @returns {LintResult} Result with single warning
277  * @private
278  */
279 function createIgnoreResult(filePath, baseDir) {
280     let message;
281     const isHidden = /^\./u.test(path.basename(filePath));
282     const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
283     const isInBowerComponents = baseDir && path.relative(baseDir, filePath).startsWith("bower_components");
284
285     if (isHidden) {
286         message = "File ignored by default.  Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
287     } else if (isInNodeModules) {
288         message = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
289     } else if (isInBowerComponents) {
290         message = "File ignored by default. Use \"--ignore-pattern '!bower_components/*'\" to override.";
291     } else {
292         message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
293     }
294
295     return {
296         filePath: path.resolve(filePath),
297         messages: [
298             {
299                 fatal: false,
300                 severity: 1,
301                 message
302             }
303         ],
304         errorCount: 0,
305         warningCount: 1,
306         fixableErrorCount: 0,
307         fixableWarningCount: 0
308     };
309 }
310
311 /**
312  * Get a rule.
313  * @param {string} ruleId The rule ID to get.
314  * @param {ConfigArray[]} configArrays The config arrays that have plugin rules.
315  * @returns {Rule|null} The rule or null.
316  */
317 function getRule(ruleId, configArrays) {
318     for (const configArray of configArrays) {
319         const rule = configArray.pluginRules.get(ruleId);
320
321         if (rule) {
322             return rule;
323         }
324     }
325     return builtInRules.get(ruleId) || null;
326 }
327
328 /**
329  * Collect used deprecated rules.
330  * @param {ConfigArray[]} usedConfigArrays The config arrays which were used.
331  * @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules.
332  */
333 function *iterateRuleDeprecationWarnings(usedConfigArrays) {
334     const processedRuleIds = new Set();
335
336     // Flatten used configs.
337     /** @type {ExtractedConfig[]} */
338     const configs = [].concat(
339         ...usedConfigArrays.map(getUsedExtractedConfigs)
340     );
341
342     // Traverse rule configs.
343     for (const config of configs) {
344         for (const [ruleId, ruleConfig] of Object.entries(config.rules)) {
345
346             // Skip if it was processed.
347             if (processedRuleIds.has(ruleId)) {
348                 continue;
349             }
350             processedRuleIds.add(ruleId);
351
352             // Skip if it's not used.
353             if (!ConfigOps.getRuleSeverity(ruleConfig)) {
354                 continue;
355             }
356             const rule = getRule(ruleId, usedConfigArrays);
357
358             // Skip if it's not deprecated.
359             if (!(rule && rule.meta && rule.meta.deprecated)) {
360                 continue;
361             }
362
363             // This rule was used and deprecated.
364             yield {
365                 ruleId,
366                 replacedBy: rule.meta.replacedBy || []
367             };
368         }
369     }
370 }
371
372 /**
373  * Checks if the given message is an error message.
374  * @param {LintMessage} message The message to check.
375  * @returns {boolean} Whether or not the message is an error message.
376  * @private
377  */
378 function isErrorMessage(message) {
379     return message.severity === 2;
380 }
381
382
383 /**
384  * return the cacheFile to be used by eslint, based on whether the provided parameter is
385  * a directory or looks like a directory (ends in `path.sep`), in which case the file
386  * name will be the `cacheFile/.cache_hashOfCWD`
387  *
388  * if cacheFile points to a file or looks like a file then in will just use that file
389  * @param {string} cacheFile The name of file to be used to store the cache
390  * @param {string} cwd Current working directory
391  * @returns {string} the resolved path to the cache file
392  */
393 function getCacheFile(cacheFile, cwd) {
394
395     /*
396      * make sure the path separators are normalized for the environment/os
397      * keeping the trailing path separator if present
398      */
399     const normalizedCacheFile = path.normalize(cacheFile);
400
401     const resolvedCacheFile = path.resolve(cwd, normalizedCacheFile);
402     const looksLikeADirectory = normalizedCacheFile.slice(-1) === path.sep;
403
404     /**
405      * return the name for the cache file in case the provided parameter is a directory
406      * @returns {string} the resolved path to the cacheFile
407      */
408     function getCacheFileForDirectory() {
409         return path.join(resolvedCacheFile, `.cache_${hash(cwd)}`);
410     }
411
412     let fileStats;
413
414     try {
415         fileStats = fs.lstatSync(resolvedCacheFile);
416     } catch (ex) {
417         fileStats = null;
418     }
419
420
421     /*
422      * in case the file exists we need to verify if the provided path
423      * is a directory or a file. If it is a directory we want to create a file
424      * inside that directory
425      */
426     if (fileStats) {
427
428         /*
429          * is a directory or is a file, but the original file the user provided
430          * looks like a directory but `path.resolve` removed the `last path.sep`
431          * so we need to still treat this like a directory
432          */
433         if (fileStats.isDirectory() || looksLikeADirectory) {
434             return getCacheFileForDirectory();
435         }
436
437         // is file so just use that file
438         return resolvedCacheFile;
439     }
440
441     /*
442      * here we known the file or directory doesn't exist,
443      * so we will try to infer if its a directory if it looks like a directory
444      * for the current operating system.
445      */
446
447     // if the last character passed is a path separator we assume is a directory
448     if (looksLikeADirectory) {
449         return getCacheFileForDirectory();
450     }
451
452     return resolvedCacheFile;
453 }
454
455 /**
456  * Convert a string array to a boolean map.
457  * @param {string[]|null} keys The keys to assign true.
458  * @param {boolean} defaultValue The default value for each property.
459  * @param {string} displayName The property name which is used in error message.
460  * @returns {Record<string,boolean>} The boolean map.
461  */
462 function toBooleanMap(keys, defaultValue, displayName) {
463     if (keys && !Array.isArray(keys)) {
464         throw new Error(`${displayName} must be an array.`);
465     }
466     if (keys && keys.length > 0) {
467         return keys.reduce((map, def) => {
468             const [key, value] = def.split(":");
469
470             if (key !== "__proto__") {
471                 map[key] = value === void 0
472                     ? defaultValue
473                     : value === "true";
474             }
475
476             return map;
477         }, {});
478     }
479     return void 0;
480 }
481
482 /**
483  * Create a config data from CLI options.
484  * @param {CLIEngineOptions} options The options
485  * @returns {ConfigData|null} The created config data.
486  */
487 function createConfigDataFromOptions(options) {
488     const {
489         ignorePattern,
490         parser,
491         parserOptions,
492         plugins,
493         rules
494     } = options;
495     const env = toBooleanMap(options.envs, true, "envs");
496     const globals = toBooleanMap(options.globals, false, "globals");
497
498     if (
499         env === void 0 &&
500         globals === void 0 &&
501         (ignorePattern === void 0 || ignorePattern.length === 0) &&
502         parser === void 0 &&
503         parserOptions === void 0 &&
504         plugins === void 0 &&
505         rules === void 0
506     ) {
507         return null;
508     }
509     return {
510         env,
511         globals,
512         ignorePatterns: ignorePattern,
513         parser,
514         parserOptions,
515         plugins,
516         rules
517     };
518 }
519
520 /**
521  * Checks whether a directory exists at the given location
522  * @param {string} resolvedPath A path from the CWD
523  * @returns {boolean} `true` if a directory exists
524  */
525 function directoryExists(resolvedPath) {
526     try {
527         return fs.statSync(resolvedPath).isDirectory();
528     } catch (error) {
529         if (error && error.code === "ENOENT") {
530             return false;
531         }
532         throw error;
533     }
534 }
535
536 //------------------------------------------------------------------------------
537 // Public Interface
538 //------------------------------------------------------------------------------
539
540 class CLIEngine {
541
542     /**
543      * Creates a new instance of the core CLI engine.
544      * @param {CLIEngineOptions} providedOptions The options for this instance.
545      */
546     constructor(providedOptions) {
547         const options = Object.assign(
548             Object.create(null),
549             defaultOptions,
550             { cwd: process.cwd() },
551             providedOptions
552         );
553
554         if (options.fix === void 0) {
555             options.fix = false;
556         }
557
558         const additionalPluginPool = new Map();
559         const cacheFilePath = getCacheFile(
560             options.cacheLocation || options.cacheFile,
561             options.cwd
562         );
563         const configArrayFactory = new CascadingConfigArrayFactory({
564             additionalPluginPool,
565             baseConfig: options.baseConfig || null,
566             cliConfig: createConfigDataFromOptions(options),
567             cwd: options.cwd,
568             ignorePath: options.ignorePath,
569             resolvePluginsRelativeTo: options.resolvePluginsRelativeTo,
570             rulePaths: options.rulePaths,
571             specificConfigPath: options.configFile,
572             useEslintrc: options.useEslintrc
573         });
574         const fileEnumerator = new FileEnumerator({
575             configArrayFactory,
576             cwd: options.cwd,
577             extensions: options.extensions,
578             globInputPaths: options.globInputPaths,
579             errorOnUnmatchedPattern: options.errorOnUnmatchedPattern,
580             ignore: options.ignore
581         });
582         const lintResultCache =
583             options.cache ? new LintResultCache(cacheFilePath) : null;
584         const linter = new Linter({ cwd: options.cwd });
585
586         /** @type {ConfigArray[]} */
587         const lastConfigArrays = [configArrayFactory.getConfigArrayForFile()];
588
589         // Store private data.
590         internalSlotsMap.set(this, {
591             additionalPluginPool,
592             cacheFilePath,
593             configArrayFactory,
594             defaultIgnores: IgnorePattern.createDefaultIgnore(options.cwd),
595             fileEnumerator,
596             lastConfigArrays,
597             lintResultCache,
598             linter,
599             options
600         });
601
602         // setup special filter for fixes
603         if (options.fix && options.fixTypes && options.fixTypes.length > 0) {
604             debug(`Using fix types ${options.fixTypes}`);
605
606             // throw an error if any invalid fix types are found
607             validateFixTypes(options.fixTypes);
608
609             // convert to Set for faster lookup
610             const fixTypes = new Set(options.fixTypes);
611
612             // save original value of options.fix in case it's a function
613             const originalFix = (typeof options.fix === "function")
614                 ? options.fix : () => true;
615
616             options.fix = message => {
617                 const rule = message.ruleId && getRule(message.ruleId, lastConfigArrays);
618                 const matches = rule && rule.meta && fixTypes.has(rule.meta.type);
619
620                 return matches && originalFix(message);
621             };
622         }
623     }
624
625     getRules() {
626         const { lastConfigArrays } = internalSlotsMap.get(this);
627
628         return new Map(function *() {
629             yield* builtInRules;
630
631             for (const configArray of lastConfigArrays) {
632                 yield* configArray.pluginRules;
633             }
634         }());
635     }
636
637     /**
638      * Returns results that only contains errors.
639      * @param {LintResult[]} results The results to filter.
640      * @returns {LintResult[]} The filtered results.
641      */
642     static getErrorResults(results) {
643         const filtered = [];
644
645         results.forEach(result => {
646             const filteredMessages = result.messages.filter(isErrorMessage);
647
648             if (filteredMessages.length > 0) {
649                 filtered.push({
650                     ...result,
651                     messages: filteredMessages,
652                     errorCount: filteredMessages.length,
653                     warningCount: 0,
654                     fixableErrorCount: result.fixableErrorCount,
655                     fixableWarningCount: 0
656                 });
657             }
658         });
659
660         return filtered;
661     }
662
663     /**
664      * Outputs fixes from the given results to files.
665      * @param {LintReport} report The report object created by CLIEngine.
666      * @returns {void}
667      */
668     static outputFixes(report) {
669         report.results.filter(result => Object.prototype.hasOwnProperty.call(result, "output")).forEach(result => {
670             fs.writeFileSync(result.filePath, result.output);
671         });
672     }
673
674
675     /**
676      * Add a plugin by passing its configuration
677      * @param {string} name Name of the plugin.
678      * @param {Plugin} pluginObject Plugin configuration object.
679      * @returns {void}
680      */
681     addPlugin(name, pluginObject) {
682         const {
683             additionalPluginPool,
684             configArrayFactory,
685             lastConfigArrays
686         } = internalSlotsMap.get(this);
687
688         additionalPluginPool.set(name, pluginObject);
689         configArrayFactory.clearCache();
690         lastConfigArrays.length = 1;
691         lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
692     }
693
694     /**
695      * Resolves the patterns passed into executeOnFiles() into glob-based patterns
696      * for easier handling.
697      * @param {string[]} patterns The file patterns passed on the command line.
698      * @returns {string[]} The equivalent glob patterns.
699      */
700     resolveFileGlobPatterns(patterns) {
701         const { options } = internalSlotsMap.get(this);
702
703         if (options.globInputPaths === false) {
704             return patterns.filter(Boolean);
705         }
706
707         const extensions = options.extensions.map(ext => ext.replace(/^\./u, ""));
708         const dirSuffix = `/**/*.{${extensions.join(",")}}`;
709
710         return patterns.filter(Boolean).map(pathname => {
711             const resolvedPath = path.resolve(options.cwd, pathname);
712             const newPath = directoryExists(resolvedPath)
713                 ? pathname.replace(/[/\\]$/u, "") + dirSuffix
714                 : pathname;
715
716             return path.normalize(newPath).replace(/\\/gu, "/");
717         });
718     }
719
720     /**
721      * Executes the current configuration on an array of file and directory names.
722      * @param {string[]} patterns An array of file and directory names.
723      * @returns {LintReport} The results for all files that were linted.
724      */
725     executeOnFiles(patterns) {
726         const {
727             cacheFilePath,
728             fileEnumerator,
729             lastConfigArrays,
730             lintResultCache,
731             linter,
732             options: {
733                 allowInlineConfig,
734                 cache,
735                 cwd,
736                 fix,
737                 reportUnusedDisableDirectives
738             }
739         } = internalSlotsMap.get(this);
740         const results = [];
741         const startTime = Date.now();
742
743         // Clear the last used config arrays.
744         lastConfigArrays.length = 0;
745
746         // Delete cache file; should this do here?
747         if (!cache) {
748             try {
749                 fs.unlinkSync(cacheFilePath);
750             } catch (error) {
751                 const errorCode = error && error.code;
752
753                 // Ignore errors when no such file exists or file system is read only (and cache file does not exist)
754                 if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !fs.existsSync(cacheFilePath))) {
755                     throw error;
756                 }
757             }
758         }
759
760         // Iterate source code files.
761         for (const { config, filePath, ignored } of fileEnumerator.iterateFiles(patterns)) {
762             if (ignored) {
763                 results.push(createIgnoreResult(filePath, cwd));
764                 continue;
765             }
766
767             /*
768              * Store used configs for:
769              * - this method uses to collect used deprecated rules.
770              * - `getRules()` method uses to collect all loaded rules.
771              * - `--fix-type` option uses to get the loaded rule's meta data.
772              */
773             if (!lastConfigArrays.includes(config)) {
774                 lastConfigArrays.push(config);
775             }
776
777             // Skip if there is cached result.
778             if (lintResultCache) {
779                 const cachedResult =
780                     lintResultCache.getCachedLintResults(filePath, config);
781
782                 if (cachedResult) {
783                     const hadMessages =
784                         cachedResult.messages &&
785                         cachedResult.messages.length > 0;
786
787                     if (hadMessages && fix) {
788                         debug(`Reprocessing cached file to allow autofix: ${filePath}`);
789                     } else {
790                         debug(`Skipping file since it hasn't changed: ${filePath}`);
791                         results.push(cachedResult);
792                         continue;
793                     }
794                 }
795             }
796
797             // Do lint.
798             const result = verifyText({
799                 text: fs.readFileSync(filePath, "utf8"),
800                 filePath,
801                 config,
802                 cwd,
803                 fix,
804                 allowInlineConfig,
805                 reportUnusedDisableDirectives,
806                 extensionRegExp: fileEnumerator.extensionRegExp,
807                 linter
808             });
809
810             results.push(result);
811
812             /*
813              * Store the lint result in the LintResultCache.
814              * NOTE: The LintResultCache will remove the file source and any
815              * other properties that are difficult to serialize, and will
816              * hydrate those properties back in on future lint runs.
817              */
818             if (lintResultCache) {
819                 lintResultCache.setCachedLintResults(filePath, config, result);
820             }
821         }
822
823         // Persist the cache to disk.
824         if (lintResultCache) {
825             lintResultCache.reconcile();
826         }
827
828         // Collect used deprecated rules.
829         const usedDeprecatedRules = Array.from(
830             iterateRuleDeprecationWarnings(lastConfigArrays)
831         );
832
833         debug(`Linting complete in: ${Date.now() - startTime}ms`);
834         return {
835             results,
836             ...calculateStatsPerRun(results),
837             usedDeprecatedRules
838         };
839     }
840
841     /**
842      * Executes the current configuration on text.
843      * @param {string} text A string of JavaScript code to lint.
844      * @param {string} [filename] An optional string representing the texts filename.
845      * @param {boolean} [warnIgnored] Always warn when a file is ignored
846      * @returns {LintReport} The results for the linting.
847      */
848     executeOnText(text, filename, warnIgnored) {
849         const {
850             configArrayFactory,
851             fileEnumerator,
852             lastConfigArrays,
853             linter,
854             options: {
855                 allowInlineConfig,
856                 cwd,
857                 fix,
858                 reportUnusedDisableDirectives
859             }
860         } = internalSlotsMap.get(this);
861         const results = [];
862         const startTime = Date.now();
863         const resolvedFilename = filename && path.resolve(cwd, filename);
864
865         // Clear the last used config arrays.
866         lastConfigArrays.length = 0;
867
868         if (resolvedFilename && this.isPathIgnored(resolvedFilename)) {
869             if (warnIgnored) {
870                 results.push(createIgnoreResult(resolvedFilename, cwd));
871             }
872         } else {
873             const config = configArrayFactory.getConfigArrayForFile(
874                 resolvedFilename || "__placeholder__.js"
875             );
876
877             /*
878              * Store used configs for:
879              * - this method uses to collect used deprecated rules.
880              * - `getRules()` method uses to collect all loaded rules.
881              * - `--fix-type` option uses to get the loaded rule's meta data.
882              */
883             lastConfigArrays.push(config);
884
885             // Do lint.
886             results.push(verifyText({
887                 text,
888                 filePath: resolvedFilename,
889                 config,
890                 cwd,
891                 fix,
892                 allowInlineConfig,
893                 reportUnusedDisableDirectives,
894                 extensionRegExp: fileEnumerator.extensionRegExp,
895                 linter
896             }));
897         }
898
899         // Collect used deprecated rules.
900         const usedDeprecatedRules = Array.from(
901             iterateRuleDeprecationWarnings(lastConfigArrays)
902         );
903
904         debug(`Linting complete in: ${Date.now() - startTime}ms`);
905         return {
906             results,
907             ...calculateStatsPerRun(results),
908             usedDeprecatedRules
909         };
910     }
911
912     /**
913      * Returns a configuration object for the given file based on the CLI options.
914      * This is the same logic used by the ESLint CLI executable to determine
915      * configuration for each file it processes.
916      * @param {string} filePath The path of the file to retrieve a config object for.
917      * @returns {ConfigData} A configuration object for the file.
918      */
919     getConfigForFile(filePath) {
920         const { configArrayFactory, options } = internalSlotsMap.get(this);
921         const absolutePath = path.resolve(options.cwd, filePath);
922
923         if (directoryExists(absolutePath)) {
924             throw Object.assign(
925                 new Error("'filePath' should not be a directory path."),
926                 { messageTemplate: "print-config-with-directory-path" }
927             );
928         }
929
930         return configArrayFactory
931             .getConfigArrayForFile(absolutePath)
932             .extractConfig(absolutePath)
933             .toCompatibleObjectAsConfigFileContent();
934     }
935
936     /**
937      * Checks if a given path is ignored by ESLint.
938      * @param {string} filePath The path of the file to check.
939      * @returns {boolean} Whether or not the given path is ignored.
940      */
941     isPathIgnored(filePath) {
942         const {
943             configArrayFactory,
944             defaultIgnores,
945             options: { cwd, ignore }
946         } = internalSlotsMap.get(this);
947         const absolutePath = path.resolve(cwd, filePath);
948
949         if (ignore) {
950             const config = configArrayFactory
951                 .getConfigArrayForFile(absolutePath)
952                 .extractConfig(absolutePath);
953             const ignores = config.ignores || defaultIgnores;
954
955             return ignores(absolutePath);
956         }
957
958         return defaultIgnores(absolutePath);
959     }
960
961     /**
962      * Returns the formatter representing the given format or null if no formatter
963      * with the given name can be found.
964      * @param {string} [format] The name of the format to load or the path to a
965      *      custom formatter.
966      * @returns {Function} The formatter function or null if not found.
967      */
968     getFormatter(format) {
969
970         // default is stylish
971         const resolvedFormatName = format || "stylish";
972
973         // only strings are valid formatters
974         if (typeof resolvedFormatName === "string") {
975
976             // replace \ with / for Windows compatibility
977             const normalizedFormatName = resolvedFormatName.replace(/\\/gu, "/");
978
979             const slots = internalSlotsMap.get(this);
980             const cwd = slots ? slots.options.cwd : process.cwd();
981             const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
982
983             let formatterPath;
984
985             // if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
986             if (!namespace && normalizedFormatName.indexOf("/") > -1) {
987                 formatterPath = path.resolve(cwd, normalizedFormatName);
988             } else {
989                 try {
990                     const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
991
992                     formatterPath = ModuleResolver.resolve(npmFormat, path.join(cwd, "__placeholder__.js"));
993                 } catch (e) {
994                     formatterPath = path.resolve(__dirname, "formatters", normalizedFormatName);
995                 }
996             }
997
998             try {
999                 return require(formatterPath);
1000             } catch (ex) {
1001                 ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
1002                 throw ex;
1003             }
1004
1005         } else {
1006             return null;
1007         }
1008     }
1009 }
1010
1011 CLIEngine.version = pkg.version;
1012 CLIEngine.getFormatter = CLIEngine.prototype.getFormatter;
1013
1014 module.exports = {
1015     CLIEngine,
1016
1017     /**
1018      * Get the internal slots of a given CLIEngine instance for tests.
1019      * @param {CLIEngine} instance The CLIEngine instance to get.
1020      * @returns {CLIEngineInternalSlots} The internal slots.
1021      */
1022     getCLIEngineInternalSlots(instance) {
1023         return internalSlotsMap.get(instance);
1024     }
1025 };