]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/eslint/eslint.js
import 8.41.0 source
[pve-eslint.git] / eslint / lib / eslint / eslint.js
CommitLineData
56c4a2cb
DC
1/**
2 * @fileoverview Main API Class
3 * @author Kai Cataldo
4 * @author Toru Nagashima
5 */
6
7"use strict";
8
9//------------------------------------------------------------------------------
10// Requirements
11//------------------------------------------------------------------------------
12
13const path = require("path");
14const fs = require("fs");
15const { promisify } = require("util");
16const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine");
17const BuiltinRules = require("../rules");
6f036462
TL
18const {
19 Legacy: {
20 ConfigOps: {
21 getRuleSeverity
22 }
23 }
24} = require("@eslint/eslintrc");
56c4a2cb
DC
25const { version } = require("../../package.json");
26
27//------------------------------------------------------------------------------
28// Typedefs
29//------------------------------------------------------------------------------
30
31/** @typedef {import("../cli-engine/cli-engine").LintReport} CLIEngineLintReport */
32/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
33/** @typedef {import("../shared/types").ConfigData} ConfigData */
34/** @typedef {import("../shared/types").LintMessage} LintMessage */
8f9d1d4d 35/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
56c4a2cb
DC
36/** @typedef {import("../shared/types").Plugin} Plugin */
37/** @typedef {import("../shared/types").Rule} Rule */
8f9d1d4d 38/** @typedef {import("../shared/types").LintResult} LintResult */
f2a92ac6 39/** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
34eeec05
TL
40
41/**
42 * The main formatter object.
8f9d1d4d 43 * @typedef LoadedFormatter
f2a92ac6 44 * @property {(results: LintResult[], resultsMeta: ResultsMeta) => string | Promise<string>} format format function.
34eeec05 45 */
56c4a2cb
DC
46
47/**
48 * The options with which to configure the ESLint instance.
49 * @typedef {Object} ESLintOptions
50 * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
51 * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this instance
52 * @property {boolean} [cache] Enable result caching.
53 * @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
5422a9cc 54 * @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
56c4a2cb
DC
55 * @property {string} [cwd] The value to use for the current working directory.
56 * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
57 * @property {string[]} [extensions] An array of file extensions to check.
58 * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
59 * @property {string[]} [fixTypes] Array of rule types to apply fixes for.
60 * @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.
61 * @property {boolean} [ignore] False disables use of .eslintignore.
62 * @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
63 * @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
64 * @property {string} [overrideConfigFile] The configuration file to use.
609c276f 65 * @property {Record<string,Plugin>|null} [plugins] Preloaded plugins. This is a map-like object, keys are plugin IDs and each value is implementation.
56c4a2cb
DC
66 * @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.
67 * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD.
68 * @property {string[]} [rulePaths] An array of directories to load custom rules from.
69 * @property {boolean} [useEslintrc] False disables looking for .eslintrc.* files.
70 */
71
72/**
73 * A rules metadata object.
74 * @typedef {Object} RulesMeta
75 * @property {string} id The plugin ID.
76 * @property {Object} definition The plugin definition.
77 */
78
56c4a2cb
DC
79/**
80 * Private members for the `ESLint` instance.
81 * @typedef {Object} ESLintPrivateMembers
82 * @property {CLIEngine} cliEngine The wrapped CLIEngine instance.
83 * @property {ESLintOptions} options The options used to instantiate the ESLint instance.
84 */
85
86//------------------------------------------------------------------------------
87// Helpers
88//------------------------------------------------------------------------------
89
90const writeFile = promisify(fs.writeFile);
91
92/**
93 * The map with which to store private class members.
94 * @type {WeakMap<ESLint, ESLintPrivateMembers>}
95 */
96const privateMembersMap = new WeakMap();
97
98/**
99 * Check if a given value is a non-empty string or not.
100 * @param {any} x The value to check.
101 * @returns {boolean} `true` if `x` is a non-empty string.
102 */
103function isNonEmptyString(x) {
104 return typeof x === "string" && x.trim() !== "";
105}
106
107/**
8f9d1d4d 108 * Check if a given value is an array of non-empty strings or not.
56c4a2cb 109 * @param {any} x The value to check.
8f9d1d4d 110 * @returns {boolean} `true` if `x` is an array of non-empty strings.
56c4a2cb
DC
111 */
112function isArrayOfNonEmptyString(x) {
113 return Array.isArray(x) && x.every(isNonEmptyString);
114}
115
116/**
117 * Check if a given value is a valid fix type or not.
118 * @param {any} x The value to check.
119 * @returns {boolean} `true` if `x` is valid fix type.
120 */
121function isFixType(x) {
609c276f 122 return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
56c4a2cb
DC
123}
124
125/**
126 * Check if a given value is an array of fix types or not.
127 * @param {any} x The value to check.
128 * @returns {boolean} `true` if `x` is an array of fix types.
129 */
130function isFixTypeArray(x) {
131 return Array.isArray(x) && x.every(isFixType);
132}
133
134/**
135 * The error for invalid options.
136 */
137class ESLintInvalidOptionsError extends Error {
138 constructor(messages) {
139 super(`Invalid Options:\n- ${messages.join("\n- ")}`);
140 this.code = "ESLINT_INVALID_OPTIONS";
141 Error.captureStackTrace(this, ESLintInvalidOptionsError);
142 }
143}
144
145/**
146 * Validates and normalizes options for the wrapped CLIEngine instance.
147 * @param {ESLintOptions} options The options to process.
609c276f 148 * @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
56c4a2cb
DC
149 * @returns {ESLintOptions} The normalized options.
150 */
151function processOptions({
152 allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
153 baseConfig = null,
154 cache = false,
155 cacheLocation = ".eslintcache",
5422a9cc 156 cacheStrategy = "metadata",
56c4a2cb
DC
157 cwd = process.cwd(),
158 errorOnUnmatchedPattern = true,
159 extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
160 fix = false,
161 fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
162 globInputPaths = true,
163 ignore = true,
164 ignorePath = null, // ← should be null by default because if it's a string then it may throw ENOENT.
165 overrideConfig = null,
166 overrideConfigFile = null,
167 plugins = {},
168 reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that.
169 resolvePluginsRelativeTo = null, // ← should be null by default because if it's a string then it suppresses RFC47 feature.
170 rulePaths = [],
171 useEslintrc = true,
172 ...unknownOptions
173}) {
174 const errors = [];
175 const unknownOptionKeys = Object.keys(unknownOptions);
176
177 if (unknownOptionKeys.length >= 1) {
178 errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
179 if (unknownOptionKeys.includes("cacheFile")) {
180 errors.push("'cacheFile' has been removed. Please use the 'cacheLocation' option instead.");
181 }
182 if (unknownOptionKeys.includes("configFile")) {
183 errors.push("'configFile' has been removed. Please use the 'overrideConfigFile' option instead.");
184 }
185 if (unknownOptionKeys.includes("envs")) {
186 errors.push("'envs' has been removed. Please use the 'overrideConfig.env' option instead.");
187 }
188 if (unknownOptionKeys.includes("globals")) {
189 errors.push("'globals' has been removed. Please use the 'overrideConfig.globals' option instead.");
190 }
191 if (unknownOptionKeys.includes("ignorePattern")) {
192 errors.push("'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.");
193 }
194 if (unknownOptionKeys.includes("parser")) {
195 errors.push("'parser' has been removed. Please use the 'overrideConfig.parser' option instead.");
196 }
197 if (unknownOptionKeys.includes("parserOptions")) {
198 errors.push("'parserOptions' has been removed. Please use the 'overrideConfig.parserOptions' option instead.");
199 }
200 if (unknownOptionKeys.includes("rules")) {
201 errors.push("'rules' has been removed. Please use the 'overrideConfig.rules' option instead.");
202 }
203 }
204 if (typeof allowInlineConfig !== "boolean") {
205 errors.push("'allowInlineConfig' must be a boolean.");
206 }
207 if (typeof baseConfig !== "object") {
208 errors.push("'baseConfig' must be an object or null.");
209 }
210 if (typeof cache !== "boolean") {
211 errors.push("'cache' must be a boolean.");
212 }
213 if (!isNonEmptyString(cacheLocation)) {
214 errors.push("'cacheLocation' must be a non-empty string.");
215 }
5422a9cc
TL
216 if (
217 cacheStrategy !== "metadata" &&
218 cacheStrategy !== "content"
219 ) {
220 errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
221 }
56c4a2cb
DC
222 if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
223 errors.push("'cwd' must be an absolute path.");
224 }
225 if (typeof errorOnUnmatchedPattern !== "boolean") {
226 errors.push("'errorOnUnmatchedPattern' must be a boolean.");
227 }
228 if (!isArrayOfNonEmptyString(extensions) && extensions !== null) {
229 errors.push("'extensions' must be an array of non-empty strings or null.");
230 }
231 if (typeof fix !== "boolean" && typeof fix !== "function") {
232 errors.push("'fix' must be a boolean or a function.");
233 }
234 if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
609c276f 235 errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
56c4a2cb
DC
236 }
237 if (typeof globInputPaths !== "boolean") {
238 errors.push("'globInputPaths' must be a boolean.");
239 }
240 if (typeof ignore !== "boolean") {
241 errors.push("'ignore' must be a boolean.");
242 }
243 if (!isNonEmptyString(ignorePath) && ignorePath !== null) {
244 errors.push("'ignorePath' must be a non-empty string or null.");
245 }
246 if (typeof overrideConfig !== "object") {
247 errors.push("'overrideConfig' must be an object or null.");
248 }
249 if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null) {
250 errors.push("'overrideConfigFile' must be a non-empty string or null.");
251 }
252 if (typeof plugins !== "object") {
253 errors.push("'plugins' must be an object or null.");
254 } else if (plugins !== null && Object.keys(plugins).includes("")) {
255 errors.push("'plugins' must not include an empty string.");
256 }
257 if (Array.isArray(plugins)) {
258 errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
259 }
260 if (
261 reportUnusedDisableDirectives !== "error" &&
262 reportUnusedDisableDirectives !== "warn" &&
263 reportUnusedDisableDirectives !== "off" &&
264 reportUnusedDisableDirectives !== null
265 ) {
266 errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.");
267 }
268 if (
269 !isNonEmptyString(resolvePluginsRelativeTo) &&
270 resolvePluginsRelativeTo !== null
271 ) {
272 errors.push("'resolvePluginsRelativeTo' must be a non-empty string or null.");
273 }
274 if (!isArrayOfNonEmptyString(rulePaths)) {
275 errors.push("'rulePaths' must be an array of non-empty strings.");
276 }
277 if (typeof useEslintrc !== "boolean") {
456be15e 278 errors.push("'useEslintrc' must be a boolean.");
56c4a2cb
DC
279 }
280
281 if (errors.length > 0) {
282 throw new ESLintInvalidOptionsError(errors);
283 }
284
285 return {
286 allowInlineConfig,
287 baseConfig,
288 cache,
289 cacheLocation,
5422a9cc 290 cacheStrategy,
56c4a2cb
DC
291 configFile: overrideConfigFile,
292 cwd,
293 errorOnUnmatchedPattern,
294 extensions,
295 fix,
296 fixTypes,
297 globInputPaths,
298 ignore,
299 ignorePath,
300 reportUnusedDisableDirectives,
301 resolvePluginsRelativeTo,
302 rulePaths,
303 useEslintrc
304 };
305}
306
307/**
308 * Check if a value has one or more properties and that value is not undefined.
309 * @param {any} obj The value to check.
310 * @returns {boolean} `true` if `obj` has one or more properties that that value is not undefined.
311 */
312function hasDefinedProperty(obj) {
313 if (typeof obj === "object" && obj !== null) {
314 for (const key in obj) {
315 if (typeof obj[key] !== "undefined") {
316 return true;
317 }
318 }
319 }
320 return false;
321}
322
323/**
324 * Create rulesMeta object.
325 * @param {Map<string,Rule>} rules a map of rules from which to generate the object.
326 * @returns {Object} metadata for all enabled rules.
327 */
328function createRulesMeta(rules) {
329 return Array.from(rules).reduce((retVal, [id, rule]) => {
330 retVal[id] = rule.meta;
331 return retVal;
332 }, {});
333}
334
335/** @type {WeakMap<ExtractedConfig, DeprecatedRuleInfo[]>} */
336const usedDeprecatedRulesCache = new WeakMap();
337
338/**
339 * Create used deprecated rule list.
340 * @param {CLIEngine} cliEngine The CLIEngine instance.
341 * @param {string} maybeFilePath The absolute path to a lint target file or `"<text>"`.
342 * @returns {DeprecatedRuleInfo[]} The used deprecated rule list.
343 */
344function getOrFindUsedDeprecatedRules(cliEngine, maybeFilePath) {
345 const {
346 configArrayFactory,
347 options: { cwd }
348 } = getCLIEngineInternalSlots(cliEngine);
349 const filePath = path.isAbsolute(maybeFilePath)
350 ? maybeFilePath
351 : path.join(cwd, "__placeholder__.js");
352 const configArray = configArrayFactory.getConfigArrayForFile(filePath);
353 const config = configArray.extractConfig(filePath);
354
355 // Most files use the same config, so cache it.
356 if (!usedDeprecatedRulesCache.has(config)) {
357 const pluginRules = configArray.pluginRules;
358 const retv = [];
359
360 for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
361 if (getRuleSeverity(ruleConf) === 0) {
362 continue;
363 }
364 const rule = pluginRules.get(ruleId) || BuiltinRules.get(ruleId);
365 const meta = rule && rule.meta;
366
367 if (meta && meta.deprecated) {
368 retv.push({ ruleId, replacedBy: meta.replacedBy || [] });
369 }
370 }
371
372 usedDeprecatedRulesCache.set(config, Object.freeze(retv));
373 }
374
375 return usedDeprecatedRulesCache.get(config);
376}
377
378/**
379 * Processes the linting results generated by a CLIEngine linting report to
380 * match the ESLint class's API.
381 * @param {CLIEngine} cliEngine The CLIEngine instance.
382 * @param {CLIEngineLintReport} report The CLIEngine linting report to process.
383 * @returns {LintResult[]} The processed linting results.
384 */
385function processCLIEngineLintReport(cliEngine, { results }) {
386 const descriptor = {
387 configurable: true,
388 enumerable: true,
389 get() {
390 return getOrFindUsedDeprecatedRules(cliEngine, this.filePath);
391 }
392 };
393
394 for (const result of results) {
395 Object.defineProperty(result, "usedDeprecatedRules", descriptor);
396 }
397
398 return results;
399}
400
401/**
402 * An Array.prototype.sort() compatible compare function to order results by their file path.
403 * @param {LintResult} a The first lint result.
404 * @param {LintResult} b The second lint result.
405 * @returns {number} An integer representing the order in which the two results should occur.
406 */
407function compareResultsByFilePath(a, b) {
408 if (a.filePath < b.filePath) {
409 return -1;
410 }
411
412 if (a.filePath > b.filePath) {
413 return 1;
414 }
415
416 return 0;
417}
418
609c276f
TL
419/**
420 * Main API.
421 */
56c4a2cb
DC
422class ESLint {
423
424 /**
425 * Creates a new instance of the main ESLint API.
426 * @param {ESLintOptions} options The options for this instance.
427 */
428 constructor(options = {}) {
429 const processedOptions = processOptions(options);
609c276f 430 const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins });
56c4a2cb 431 const {
56c4a2cb
DC
432 configArrayFactory,
433 lastConfigArrays
434 } = getCLIEngineInternalSlots(cliEngine);
435 let updated = false;
436
56c4a2cb
DC
437 /*
438 * Address `overrideConfig` to set override config.
439 * Operate the `configArrayFactory` internal slot directly because this
440 * functionality doesn't exist as the public API of CLIEngine.
441 */
442 if (hasDefinedProperty(options.overrideConfig)) {
443 configArrayFactory.setOverrideConfig(options.overrideConfig);
444 updated = true;
445 }
446
447 // Update caches.
448 if (updated) {
449 configArrayFactory.clearCache();
450 lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
451 }
452
453 // Initialize private properties.
454 privateMembersMap.set(this, {
455 cliEngine,
456 options: processedOptions
457 });
458 }
459
460 /**
461 * The version text.
462 * @type {string}
463 */
464 static get version() {
465 return version;
466 }
467
468 /**
469 * Outputs fixes from the given results to files.
470 * @param {LintResult[]} results The lint results.
471 * @returns {Promise<void>} Returns a promise that is used to track side effects.
472 */
473 static async outputFixes(results) {
474 if (!Array.isArray(results)) {
475 throw new Error("'results' must be an array");
476 }
477
478 await Promise.all(
479 results
480 .filter(result => {
481 if (typeof result !== "object" || result === null) {
482 throw new Error("'results' must include only objects");
483 }
484 return (
485 typeof result.output === "string" &&
486 path.isAbsolute(result.filePath)
487 );
488 })
489 .map(r => writeFile(r.filePath, r.output))
490 );
491 }
492
493 /**
494 * Returns results that only contains errors.
495 * @param {LintResult[]} results The results to filter.
496 * @returns {LintResult[]} The filtered results.
497 */
498 static getErrorResults(results) {
499 return CLIEngine.getErrorResults(results);
500 }
501
609c276f
TL
502 /**
503 * Returns meta objects for each rule represented in the lint results.
504 * @param {LintResult[]} results The results to fetch rules meta for.
505 * @returns {Object} A mapping of ruleIds to rule meta objects.
506 */
507 getRulesMetaForResults(results) {
508
509 const resultRuleIds = new Set();
510
511 // first gather all ruleIds from all results
512
513 for (const result of results) {
514 for (const { ruleId } of result.messages) {
515 resultRuleIds.add(ruleId);
516 }
8f9d1d4d
DC
517 for (const { ruleId } of result.suppressedMessages) {
518 resultRuleIds.add(ruleId);
519 }
609c276f
TL
520 }
521
522 // create a map of all rules in the results
523
524 const { cliEngine } = privateMembersMap.get(this);
525 const rules = cliEngine.getRules();
526 const resultRules = new Map();
527
528 for (const [ruleId, rule] of rules) {
529 if (resultRuleIds.has(ruleId)) {
530 resultRules.set(ruleId, rule);
531 }
532 }
533
534 return createRulesMeta(resultRules);
535
536 }
537
56c4a2cb
DC
538 /**
539 * Executes the current configuration on an array of file and directory names.
540 * @param {string[]} patterns An array of file and directory names.
541 * @returns {Promise<LintResult[]>} The results of linting the file patterns given.
542 */
543 async lintFiles(patterns) {
544 if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {
545 throw new Error("'patterns' must be a non-empty string or an array of non-empty strings");
546 }
547 const { cliEngine } = privateMembersMap.get(this);
548
549 return processCLIEngineLintReport(
550 cliEngine,
551 cliEngine.executeOnFiles(patterns)
552 );
553 }
554
555 /**
556 * Executes the current configuration on text.
557 * @param {string} code A string of JavaScript code to lint.
558 * @param {Object} [options] The options.
559 * @param {string} [options.filePath] The path to the file of the source code.
560 * @param {boolean} [options.warnIgnored] When set to true, warn if given filePath is an ignored path.
561 * @returns {Promise<LintResult[]>} The results of linting the string of code given.
562 */
563 async lintText(code, options = {}) {
564 if (typeof code !== "string") {
565 throw new Error("'code' must be a string");
566 }
567 if (typeof options !== "object") {
568 throw new Error("'options' must be an object, null, or undefined");
569 }
570 const {
571 filePath,
572 warnIgnored = false,
573 ...unknownOptions
574 } = options || {};
575
609c276f
TL
576 const unknownOptionKeys = Object.keys(unknownOptions);
577
578 if (unknownOptionKeys.length > 0) {
579 throw new Error(`'options' must not include the unknown option(s): ${unknownOptionKeys.join(", ")}`);
56c4a2cb 580 }
609c276f 581
56c4a2cb
DC
582 if (filePath !== void 0 && !isNonEmptyString(filePath)) {
583 throw new Error("'options.filePath' must be a non-empty string or undefined");
584 }
585 if (typeof warnIgnored !== "boolean") {
586 throw new Error("'options.warnIgnored' must be a boolean or undefined");
587 }
588
589 const { cliEngine } = privateMembersMap.get(this);
590
591 return processCLIEngineLintReport(
592 cliEngine,
593 cliEngine.executeOnText(code, filePath, warnIgnored)
594 );
595 }
596
597 /**
598 * Returns the formatter representing the given formatter name.
456be15e 599 * @param {string} [name] The name of the formatter to load.
56c4a2cb
DC
600 * The following values are allowed:
601 * - `undefined` ... Load `stylish` builtin formatter.
602 * - A builtin formatter name ... Load the builtin formatter.
8f9d1d4d 603 * - A third-party formatter name:
56c4a2cb
DC
604 * - `foo` → `eslint-formatter-foo`
605 * - `@foo` → `@foo/eslint-formatter`
606 * - `@foo/bar` → `@foo/eslint-formatter-bar`
607 * - A file path ... Load the file.
8f9d1d4d 608 * @returns {Promise<LoadedFormatter>} A promise resolving to the formatter object.
56c4a2cb
DC
609 * This promise will be rejected if the given formatter was not found or not
610 * a function.
611 */
612 async loadFormatter(name = "stylish") {
613 if (typeof name !== "string") {
614 throw new Error("'name' must be a string");
615 }
616
34eeec05 617 const { cliEngine, options } = privateMembersMap.get(this);
56c4a2cb
DC
618 const formatter = cliEngine.getFormatter(name);
619
620 if (typeof formatter !== "function") {
621 throw new Error(`Formatter must be a function, but got a ${typeof formatter}.`);
622 }
623
624 return {
625
626 /**
627 * The main formatter method.
8f9d1d4d 628 * @param {LintResult[]} results The lint results to format.
f2a92ac6 629 * @param {ResultsMeta} resultsMeta Warning count and max threshold.
34eeec05 630 * @returns {string | Promise<string>} The formatted lint results.
56c4a2cb 631 */
f2a92ac6 632 format(results, resultsMeta) {
56c4a2cb
DC
633 let rulesMeta = null;
634
635 results.sort(compareResultsByFilePath);
636
637 return formatter(results, {
f2a92ac6 638 ...resultsMeta,
34eeec05
TL
639 get cwd() {
640 return options.cwd;
641 },
56c4a2cb
DC
642 get rulesMeta() {
643 if (!rulesMeta) {
644 rulesMeta = createRulesMeta(cliEngine.getRules());
645 }
646
647 return rulesMeta;
648 }
649 });
650 }
651 };
652 }
653
654 /**
655 * Returns a configuration object for the given file based on the CLI options.
656 * This is the same logic used by the ESLint CLI executable to determine
657 * configuration for each file it processes.
658 * @param {string} filePath The path of the file to retrieve a config object for.
659 * @returns {Promise<ConfigData>} A configuration object for the file.
660 */
661 async calculateConfigForFile(filePath) {
662 if (!isNonEmptyString(filePath)) {
663 throw new Error("'filePath' must be a non-empty string");
664 }
665 const { cliEngine } = privateMembersMap.get(this);
666
667 return cliEngine.getConfigForFile(filePath);
668 }
669
670 /**
671 * Checks if a given path is ignored by ESLint.
672 * @param {string} filePath The path of the file to check.
673 * @returns {Promise<boolean>} Whether or not the given path is ignored.
674 */
675 async isPathIgnored(filePath) {
676 if (!isNonEmptyString(filePath)) {
677 throw new Error("'filePath' must be a non-empty string");
678 }
679 const { cliEngine } = privateMembersMap.get(this);
680
681 return cliEngine.isPathIgnored(filePath);
682 }
683}
684
685//------------------------------------------------------------------------------
686// Public Interface
687//------------------------------------------------------------------------------
688
689module.exports = {
690 ESLint,
691
692 /**
693 * Get the private class members of a given ESLint instance for tests.
694 * @param {ESLint} instance The ESLint instance to get.
695 * @returns {ESLintPrivateMembers} The instance's private class members.
696 */
697 getESLintPrivateMembers(instance) {
698 return privateMembersMap.get(instance);
699 }
700};