2 * @fileoverview Main CLI object.
3 * @author Nicholas C. Zakas
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.
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
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");
33 const debug
= require("debug")("eslint:cli-engine");
34 const validFixTypes
= new Set(["problem", "suggestion", "layout"]);
36 //------------------------------------------------------------------------------
38 //------------------------------------------------------------------------------
40 // For VSCode IntelliSense
41 /** @typedef {import("../shared/types").ConfigData} ConfigData */
42 /** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
43 /** @typedef {import("../shared/types").LintMessage} LintMessage */
44 /** @typedef {import("../shared/types").ParserOptions} ParserOptions */
45 /** @typedef {import("../shared/types").Plugin} Plugin */
46 /** @typedef {import("../shared/types").RuleConf} RuleConf */
47 /** @typedef {import("../shared/types").Rule} Rule */
48 /** @typedef {ReturnType<CascadingConfigArrayFactory["getConfigArrayForFile"]>} ConfigArray */
49 /** @typedef {ReturnType<ConfigArray["extractConfig"]>} ExtractedConfig */
52 * The options to configure a CLI engine with.
53 * @typedef {Object} CLIEngineOptions
54 * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
55 * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this CLIEngine instance
56 * @property {boolean} [cache] Enable result caching.
57 * @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
58 * @property {string} [configFile] The configuration file to use.
59 * @property {string} [cwd] The value to use for the current working directory.
60 * @property {string[]} [envs] An array of environments to load.
61 * @property {string[]|null} [extensions] An array of file extensions to check.
62 * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
63 * @property {string[]} [fixTypes] Array of rule types to apply fixes for.
64 * @property {string[]} [globals] An array of global variables to declare.
65 * @property {boolean} [ignore] False disables use of .eslintignore.
66 * @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
67 * @property {string|string[]} [ignorePattern] One or more glob patterns to ignore.
68 * @property {boolean} [useEslintrc] False disables looking for .eslintrc
69 * @property {string} [parser] The name of the parser to use.
70 * @property {ParserOptions} [parserOptions] An object of parserOption settings to use.
71 * @property {string[]} [plugins] An array of plugins to load.
72 * @property {Record<string,RuleConf>} [rules] An object of rules to use.
73 * @property {string[]} [rulePaths] An array of directories to load custom rules from.
74 * @property {boolean} [reportUnusedDisableDirectives] `true` adds reports for unused eslint-disable directives
75 * @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.
76 * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD
81 * @typedef {Object} LintResult
82 * @property {string} filePath The path to the file that was linted.
83 * @property {LintMessage[]} messages All of the messages for the result.
84 * @property {number} errorCount Number of errors for the result.
85 * @property {number} warningCount Number of warnings for the result.
86 * @property {number} fixableErrorCount Number of fixable errors for the result.
87 * @property {number} fixableWarningCount Number of fixable warnings for the result.
88 * @property {string} [source] The source code of the file that was linted.
89 * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
94 * @typedef {Object} LintReport
95 * @property {LintResult[]} results All of the result.
96 * @property {number} errorCount Number of errors for the result.
97 * @property {number} warningCount Number of warnings for the result.
98 * @property {number} fixableErrorCount Number of fixable errors for the result.
99 * @property {number} fixableWarningCount Number of fixable warnings for the result.
100 * @property {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules.
104 * Private data for CLIEngine.
105 * @typedef {Object} CLIEngineInternalSlots
106 * @property {Map<string, Plugin>} additionalPluginPool The map for additional plugins.
107 * @property {string} cacheFilePath The path to the cache of lint results.
108 * @property {CascadingConfigArrayFactory} configArrayFactory The factory of configs.
109 * @property {(filePath: string) => boolean} defaultIgnores The default predicate function to check if a file ignored or not.
110 * @property {FileEnumerator} fileEnumerator The file enumerator.
111 * @property {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used.
112 * @property {LintResultCache|null} lintResultCache The cache of lint results.
113 * @property {Linter} linter The linter instance which has loaded rules.
114 * @property {CLIEngineOptions} options The normalized options of this instance.
117 //------------------------------------------------------------------------------
119 //------------------------------------------------------------------------------
121 /** @type {WeakMap<CLIEngine, CLIEngineInternalSlots>} */
122 const internalSlotsMap
= new WeakMap();
125 * Determines if each fix type in an array is supported by ESLint and throws
127 * @param {string[]} fixTypes An array of fix types to check.
129 * @throws {Error} If an invalid fix type is found.
131 function validateFixTypes(fixTypes
) {
132 for (const fixType
of fixTypes
) {
133 if (!validFixTypes
.has(fixType
)) {
134 throw new Error(`Invalid fix type "${fixType}" found.`);
140 * It will calculate the error and warning count for collection of messages per file
141 * @param {LintMessage[]} messages Collection of messages
142 * @returns {Object} Contains the stats
145 function calculateStatsPerFile(messages
) {
146 return messages
.reduce((stat
, message
) => {
147 if (message
.fatal
|| message
.severity
=== 2) {
150 stat
.fixableErrorCount
++;
155 stat
.fixableWarningCount
++;
162 fixableErrorCount
: 0,
163 fixableWarningCount
: 0
168 * It will calculate the error and warning count for collection of results from all files
169 * @param {LintResult[]} results Collection of messages from all the files
170 * @returns {Object} Contains the stats
173 function calculateStatsPerRun(results
) {
174 return results
.reduce((stat
, result
) => {
175 stat
.errorCount
+= result
.errorCount
;
176 stat
.warningCount
+= result
.warningCount
;
177 stat
.fixableErrorCount
+= result
.fixableErrorCount
;
178 stat
.fixableWarningCount
+= result
.fixableWarningCount
;
183 fixableErrorCount
: 0,
184 fixableWarningCount
: 0
189 * Processes an source code using ESLint.
190 * @param {Object} config The config object.
191 * @param {string} config.text The source code to verify.
192 * @param {string} config.cwd The path to the current working directory.
193 * @param {string|undefined} config.filePath The path to the file of `text`. If this is undefined, it uses `<text>`.
194 * @param {ConfigArray} config.config The config.
195 * @param {boolean} config.fix If `true` then it does fix.
196 * @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
197 * @param {boolean} config.reportUnusedDisableDirectives If `true` then it reports unused `eslint-disable` comments.
198 * @param {FileEnumerator} config.fileEnumerator The file enumerator to check if a path is a target or not.
199 * @param {Linter} config.linter The linter instance to verify.
200 * @returns {LintResult} The result of linting.
203 function verifyText({
206 filePath
: providedFilePath
,
210 reportUnusedDisableDirectives
,
214 const filePath
= providedFilePath
|| "<text>";
216 debug(`Lint ${filePath}`);
220 * `config.extractConfig(filePath)` requires an absolute path, but `linter`
221 * doesn't know CWD, so it gives `linter` an absolute path always.
223 const filePathToVerify
= filePath
=== "<text>" ? path
.join(cwd
, filePath
) : filePath
;
224 const { fixed
, messages
, output
} = linter
.verifyAndFix(
229 filename
: filePathToVerify
,
231 reportUnusedDisableDirectives
,
234 * Check if the linter should adopt a given code block or not.
235 * @param {string} blockFilename The virtual filename of a code block.
236 * @returns {boolean} `true` if the linter should adopt the code block.
238 filterCodeBlock(blockFilename
) {
239 return fileEnumerator
.isTargetPath(blockFilename
);
248 ...calculateStatsPerFile(messages
)
252 result
.output
= output
;
255 result
.errorCount
+ result
.warningCount
> 0 &&
256 typeof result
.output
=== "undefined"
258 result
.source
= text
;
265 * Returns result with warning by ignore settings
266 * @param {string} filePath File path of checked code
267 * @param {string} baseDir Absolute path of base directory
268 * @returns {LintResult} Result with single warning
271 function createIgnoreResult(filePath
, baseDir
) {
273 const isHidden
= filePath
.split(path
.sep
)
274 .find(segment
=> /^\./u.test(segment
));
275 const isInNodeModules
= baseDir
&& path
.relative(baseDir
, filePath
).startsWith("node_modules");
278 message
= "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
279 } else if (isInNodeModules
) {
280 message
= "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
282 message
= "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
286 filePath
: path
.resolve(filePath
),
296 fixableErrorCount
: 0,
297 fixableWarningCount
: 0
303 * @param {string} ruleId The rule ID to get.
304 * @param {ConfigArray[]} configArrays The config arrays that have plugin rules.
305 * @returns {Rule|null} The rule or null.
307 function getRule(ruleId
, configArrays
) {
308 for (const configArray
of configArrays
) {
309 const rule
= configArray
.pluginRules
.get(ruleId
);
315 return builtInRules
.get(ruleId
) || null;
319 * Collect used deprecated rules.
320 * @param {ConfigArray[]} usedConfigArrays The config arrays which were used.
321 * @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules.
323 function *iterateRuleDeprecationWarnings(usedConfigArrays
) {
324 const processedRuleIds
= new Set();
326 // Flatten used configs.
327 /** @type {ExtractedConfig[]} */
328 const configs
= [].concat(
329 ...usedConfigArrays
.map(getUsedExtractedConfigs
)
332 // Traverse rule configs.
333 for (const config
of configs
) {
334 for (const [ruleId
, ruleConfig
] of Object
.entries(config
.rules
)) {
336 // Skip if it was processed.
337 if (processedRuleIds
.has(ruleId
)) {
340 processedRuleIds
.add(ruleId
);
342 // Skip if it's not used.
343 if (!ConfigOps
.getRuleSeverity(ruleConfig
)) {
346 const rule
= getRule(ruleId
, usedConfigArrays
);
348 // Skip if it's not deprecated.
349 if (!(rule
&& rule
.meta
&& rule
.meta
.deprecated
)) {
353 // This rule was used and deprecated.
356 replacedBy
: rule
.meta
.replacedBy
|| []
363 * Checks if the given message is an error message.
364 * @param {LintMessage} message The message to check.
365 * @returns {boolean} Whether or not the message is an error message.
368 function isErrorMessage(message
) {
369 return message
.severity
=== 2;
374 * return the cacheFile to be used by eslint, based on whether the provided parameter is
375 * a directory or looks like a directory (ends in `path.sep`), in which case the file
376 * name will be the `cacheFile/.cache_hashOfCWD`
378 * if cacheFile points to a file or looks like a file then in will just use that file
379 * @param {string} cacheFile The name of file to be used to store the cache
380 * @param {string} cwd Current working directory
381 * @returns {string} the resolved path to the cache file
383 function getCacheFile(cacheFile
, cwd
) {
386 * make sure the path separators are normalized for the environment/os
387 * keeping the trailing path separator if present
389 const normalizedCacheFile
= path
.normalize(cacheFile
);
391 const resolvedCacheFile
= path
.resolve(cwd
, normalizedCacheFile
);
392 const looksLikeADirectory
= normalizedCacheFile
.slice(-1) === path
.sep
;
395 * return the name for the cache file in case the provided parameter is a directory
396 * @returns {string} the resolved path to the cacheFile
398 function getCacheFileForDirectory() {
399 return path
.join(resolvedCacheFile
, `.cache_${hash(cwd)}`);
405 fileStats
= fs
.lstatSync(resolvedCacheFile
);
412 * in case the file exists we need to verify if the provided path
413 * is a directory or a file. If it is a directory we want to create a file
414 * inside that directory
419 * is a directory or is a file, but the original file the user provided
420 * looks like a directory but `path.resolve` removed the `last path.sep`
421 * so we need to still treat this like a directory
423 if (fileStats
.isDirectory() || looksLikeADirectory
) {
424 return getCacheFileForDirectory();
427 // is file so just use that file
428 return resolvedCacheFile
;
432 * here we known the file or directory doesn't exist,
433 * so we will try to infer if its a directory if it looks like a directory
434 * for the current operating system.
437 // if the last character passed is a path separator we assume is a directory
438 if (looksLikeADirectory
) {
439 return getCacheFileForDirectory();
442 return resolvedCacheFile
;
446 * Convert a string array to a boolean map.
447 * @param {string[]|null} keys The keys to assign true.
448 * @param {boolean} defaultValue The default value for each property.
449 * @param {string} displayName The property name which is used in error message.
450 * @returns {Record<string,boolean>} The boolean map.
452 function toBooleanMap(keys
, defaultValue
, displayName
) {
453 if (keys
&& !Array
.isArray(keys
)) {
454 throw new Error(`${displayName} must be an array.`);
456 if (keys
&& keys
.length
> 0) {
457 return keys
.reduce((map
, def
) => {
458 const [key
, value
] = def
.split(":");
460 if (key
!== "__proto__") {
461 map
[key
] = value
=== void 0
473 * Create a config data from CLI options.
474 * @param {CLIEngineOptions} options The options
475 * @returns {ConfigData|null} The created config data.
477 function createConfigDataFromOptions(options
) {
485 const env
= toBooleanMap(options
.envs
, true, "envs");
486 const globals
= toBooleanMap(options
.globals
, false, "globals");
490 globals
=== void 0 &&
491 (ignorePattern
=== void 0 || ignorePattern
.length
=== 0) &&
493 parserOptions
=== void 0 &&
494 plugins
=== void 0 &&
502 ignorePatterns
: ignorePattern
,
511 * Checks whether a directory exists at the given location
512 * @param {string} resolvedPath A path from the CWD
513 * @returns {boolean} `true` if a directory exists
515 function directoryExists(resolvedPath
) {
517 return fs
.statSync(resolvedPath
).isDirectory();
519 if (error
&& error
.code
=== "ENOENT") {
526 //------------------------------------------------------------------------------
528 //------------------------------------------------------------------------------
533 * Creates a new instance of the core CLI engine.
534 * @param {CLIEngineOptions} providedOptions The options for this instance.
536 constructor(providedOptions
) {
537 const options
= Object
.assign(
540 { cwd
: process
.cwd() },
544 if (options
.fix
=== void 0) {
548 const additionalPluginPool
= new Map();
549 const cacheFilePath
= getCacheFile(
550 options
.cacheLocation
|| options
.cacheFile
,
553 const configArrayFactory
= new CascadingConfigArrayFactory({
554 additionalPluginPool
,
555 baseConfig
: options
.baseConfig
|| null,
556 cliConfig
: createConfigDataFromOptions(options
),
558 ignorePath
: options
.ignorePath
,
559 resolvePluginsRelativeTo
: options
.resolvePluginsRelativeTo
,
560 rulePaths
: options
.rulePaths
,
561 specificConfigPath
: options
.configFile
,
562 useEslintrc
: options
.useEslintrc
564 const fileEnumerator
= new FileEnumerator({
567 extensions
: options
.extensions
,
568 globInputPaths
: options
.globInputPaths
,
569 errorOnUnmatchedPattern
: options
.errorOnUnmatchedPattern
,
570 ignore
: options
.ignore
572 const lintResultCache
=
573 options
.cache
? new LintResultCache(cacheFilePath
) : null;
574 const linter
= new Linter({ cwd
: options
.cwd
});
576 /** @type {ConfigArray[]} */
577 const lastConfigArrays
= [configArrayFactory
.getConfigArrayForFile()];
579 // Store private data.
580 internalSlotsMap
.set(this, {
581 additionalPluginPool
,
584 defaultIgnores
: IgnorePattern
.createDefaultIgnore(options
.cwd
),
592 // setup special filter for fixes
593 if (options
.fix
&& options
.fixTypes
&& options
.fixTypes
.length
> 0) {
594 debug(`Using fix types ${options.fixTypes}`);
596 // throw an error if any invalid fix types are found
597 validateFixTypes(options
.fixTypes
);
599 // convert to Set for faster lookup
600 const fixTypes
= new Set(options
.fixTypes
);
602 // save original value of options.fix in case it's a function
603 const originalFix
= (typeof options
.fix
=== "function")
604 ? options
.fix
: () => true;
606 options
.fix
= message
=> {
607 const rule
= message
.ruleId
&& getRule(message
.ruleId
, lastConfigArrays
);
608 const matches
= rule
&& rule
.meta
&& fixTypes
.has(rule
.meta
.type
);
610 return matches
&& originalFix(message
);
616 const { lastConfigArrays
} = internalSlotsMap
.get(this);
618 return new Map(function *() {
621 for (const configArray
of lastConfigArrays
) {
622 yield* configArray
.pluginRules
;
628 * Returns results that only contains errors.
629 * @param {LintResult[]} results The results to filter.
630 * @returns {LintResult[]} The filtered results.
632 static getErrorResults(results
) {
635 results
.forEach(result
=> {
636 const filteredMessages
= result
.messages
.filter(isErrorMessage
);
638 if (filteredMessages
.length
> 0) {
641 messages
: filteredMessages
,
642 errorCount
: filteredMessages
.length
,
644 fixableErrorCount
: result
.fixableErrorCount
,
645 fixableWarningCount
: 0
654 * Outputs fixes from the given results to files.
655 * @param {LintReport} report The report object created by CLIEngine.
658 static outputFixes(report
) {
659 report
.results
.filter(result
=> Object
.prototype.hasOwnProperty
.call(result
, "output")).forEach(result
=> {
660 fs
.writeFileSync(result
.filePath
, result
.output
);
666 * Add a plugin by passing its configuration
667 * @param {string} name Name of the plugin.
668 * @param {Plugin} pluginObject Plugin configuration object.
671 addPlugin(name
, pluginObject
) {
673 additionalPluginPool
,
676 } = internalSlotsMap
.get(this);
678 additionalPluginPool
.set(name
, pluginObject
);
679 configArrayFactory
.clearCache();
680 lastConfigArrays
.length
= 1;
681 lastConfigArrays
[0] = configArrayFactory
.getConfigArrayForFile();
685 * Resolves the patterns passed into executeOnFiles() into glob-based patterns
686 * for easier handling.
687 * @param {string[]} patterns The file patterns passed on the command line.
688 * @returns {string[]} The equivalent glob patterns.
690 resolveFileGlobPatterns(patterns
) {
691 const { options
} = internalSlotsMap
.get(this);
693 if (options
.globInputPaths
=== false) {
694 return patterns
.filter(Boolean
);
697 const extensions
= (options
.extensions
|| [".js"]).map(ext
=> ext
.replace(/^\./u, ""));
698 const dirSuffix
= `/**/*.{${extensions.join(",")}}`;
700 return patterns
.filter(Boolean
).map(pathname
=> {
701 const resolvedPath
= path
.resolve(options
.cwd
, pathname
);
702 const newPath
= directoryExists(resolvedPath
)
703 ? pathname
.replace(/[/\\]$/u, "") + dirSuffix
706 return path
.normalize(newPath
).replace(/\\/gu, "/");
711 * Executes the current configuration on an array of file and directory names.
712 * @param {string[]} patterns An array of file and directory names.
713 * @returns {LintReport} The results for all files that were linted.
715 executeOnFiles(patterns) {
727 reportUnusedDisableDirectives
729 } = internalSlotsMap.get(this);
731 const startTime = Date.now();
733 // Clear the last used config arrays.
734 lastConfigArrays.length = 0;
736 // Delete cache file; should this do here?
739 fs.unlinkSync(cacheFilePath);
741 const errorCode = error && error.code;
743 // Ignore errors when no such file exists or file system is read only (and cache file does not exist)
744 if (errorCode !== "ENOENT
" && !(errorCode === "EROFS
" && !fs.existsSync(cacheFilePath))) {
750 // Iterate source code files.
751 for (const { config, filePath, ignored } of fileEnumerator.iterateFiles(patterns)) {
753 results.push(createIgnoreResult(filePath, cwd));
758 * Store used configs for:
759 * - this method uses to collect used deprecated rules.
760 * - `getRules()` method uses to collect all loaded rules.
761 * - `--fix-type` option uses to get the loaded rule's meta data.
763 if (!lastConfigArrays.includes(config)) {
764 lastConfigArrays.push(config);
767 // Skip if there is cached result.
768 if (lintResultCache) {
770 lintResultCache.getCachedLintResults(filePath, config);
774 cachedResult.messages &&
775 cachedResult.messages.length > 0;
777 if (hadMessages && fix) {
778 debug(`Reprocessing cached file to allow autofix: ${filePath}`);
780 debug(`Skipping file since it hasn't changed: ${filePath}`);
781 results.push(cachedResult);
788 const result = verifyText({
789 text: fs.readFileSync(filePath, "utf8
"),
795 reportUnusedDisableDirectives,
800 results.push(result);
803 * Store the lint result in the LintResultCache.
804 * NOTE: The LintResultCache will remove the file source and any
805 * other properties that are difficult to serialize, and will
806 * hydrate those properties back in on future lint runs.
808 if (lintResultCache) {
809 lintResultCache.setCachedLintResults(filePath, config, result);
813 // Persist the cache to disk.
814 if (lintResultCache) {
815 lintResultCache.reconcile();
818 debug(`Linting complete in: ${Date.now() - startTime}ms`);
819 let usedDeprecatedRules;
823 ...calculateStatsPerRun(results),
825 // Initialize it lazily because CLI and `ESLint` API don't use it.
826 get usedDeprecatedRules() {
827 if (!usedDeprecatedRules) {
828 usedDeprecatedRules = Array.from(
829 iterateRuleDeprecationWarnings(lastConfigArrays)
832 return usedDeprecatedRules;
838 * Executes the current configuration on text.
839 * @param {string} text A string of JavaScript code to lint.
840 * @param {string} [filename] An optional string representing the texts filename.
841 * @param {boolean} [warnIgnored] Always warn when a file is ignored
842 * @returns {LintReport} The results for the linting.
844 executeOnText(text, filename, warnIgnored) {
854 reportUnusedDisableDirectives
856 } = internalSlotsMap.get(this);
858 const startTime = Date.now();
859 const resolvedFilename = filename && path.resolve(cwd, filename);
862 // Clear the last used config arrays.
863 lastConfigArrays.length = 0;
864 if (resolvedFilename && this.isPathIgnored(resolvedFilename)) {
866 results.push(createIgnoreResult(resolvedFilename, cwd));
869 const config = configArrayFactory.getConfigArrayForFile(
870 resolvedFilename || "__placeholder__
.js
"
874 * Store used configs for:
875 * - this method uses to collect used deprecated rules.
876 * - `getRules()` method uses to collect all loaded rules.
877 * - `--fix-type` option uses to get the loaded rule's meta data.
879 lastConfigArrays.push(config);
882 results.push(verifyText({
884 filePath: resolvedFilename,
889 reportUnusedDisableDirectives,
895 debug(`Linting complete in: ${Date.now() - startTime}ms`);
896 let usedDeprecatedRules;
900 ...calculateStatsPerRun(results),
902 // Initialize it lazily because CLI and `ESLint` API don't use it.
903 get usedDeprecatedRules() {
904 if (!usedDeprecatedRules) {
905 usedDeprecatedRules = Array.from(
906 iterateRuleDeprecationWarnings(lastConfigArrays)
909 return usedDeprecatedRules;
915 * Returns a configuration object for the given file based on the CLI options.
916 * This is the same logic used by the ESLint CLI executable to determine
917 * configuration for each file it processes.
918 * @param {string} filePath The path of the file to retrieve a config object for.
919 * @returns {ConfigData} A configuration object for the file.
921 getConfigForFile(filePath) {
922 const { configArrayFactory, options } = internalSlotsMap.get(this);
923 const absolutePath = path.resolve(options.cwd, filePath);
925 if (directoryExists(absolutePath)) {
927 new Error("'filePath' should not be a directory path
."),
928 { messageTemplate: "print
-config
-with-directory
-path
" }
932 return configArrayFactory
933 .getConfigArrayForFile(absolutePath)
934 .extractConfig(absolutePath)
935 .toCompatibleObjectAsConfigFileContent();
939 * Checks if a given path is ignored by ESLint.
940 * @param {string} filePath The path of the file to check.
941 * @returns {boolean} Whether or not the given path is ignored.
943 isPathIgnored(filePath) {
947 options: { cwd, ignore }
948 } = internalSlotsMap.get(this);
949 const absolutePath = path.resolve(cwd, filePath);
952 const config = configArrayFactory
953 .getConfigArrayForFile(absolutePath)
954 .extractConfig(absolutePath);
955 const ignores = config.ignores || defaultIgnores;
957 return ignores(absolutePath);
960 return defaultIgnores(absolutePath);
964 * Returns the formatter representing the given format or null if the `format` is not a string.
965 * @param {string} [format] The name of the format to load or the path to a
967 * @returns {(Function|null)} The formatter function or null if the `format` is not a string.
969 getFormatter(format) {
971 // default is stylish
972 const resolvedFormatName = format || "stylish
";
974 // only strings are valid formatters
975 if (typeof resolvedFormatName === "string
") {
977 // replace \ with / for Windows compatibility
978 const normalizedFormatName = resolvedFormatName.replace(/\\/gu, "/");
980 const slots = internalSlotsMap.get(this);
981 const cwd = slots ? slots.options.cwd : process.cwd();
982 const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
986 // if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
987 if (!namespace && normalizedFormatName.indexOf("/") > -1) {
988 formatterPath = path.resolve(cwd, normalizedFormatName);
991 const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint
-formatter
");
993 formatterPath = ModuleResolver.resolve(npmFormat, path.join(cwd, "__placeholder__
.js
"));
995 formatterPath = path.resolve(__dirname, "formatters
", normalizedFormatName);
1000 return require(formatterPath);
1002 ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
1012 CLIEngine.version = pkg.version;
1013 CLIEngine.getFormatter = CLIEngine.prototype.getFormatter;
1019 * Get the internal slots of a given CLIEngine instance for tests.
1020 * @param {CLIEngine} instance The CLIEngine instance to get.
1021 * @returns {CLIEngineInternalSlots} The internal slots.
1023 getCLIEngineInternalSlots(instance) {
1024 return internalSlotsMap.get(instance);