[d565449] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | Object.defineProperty(exports, '__esModule', { value: true });
|
---|
| 4 |
|
---|
| 5 | var debugOrig = require('debug');
|
---|
| 6 | var fs = require('fs');
|
---|
| 7 | var importFresh = require('import-fresh');
|
---|
| 8 | var Module = require('module');
|
---|
| 9 | var path = require('path');
|
---|
| 10 | var stripComments = require('strip-json-comments');
|
---|
| 11 | var assert = require('assert');
|
---|
| 12 | var ignore = require('ignore');
|
---|
| 13 | var util = require('util');
|
---|
| 14 | var minimatch = require('minimatch');
|
---|
| 15 | var Ajv = require('ajv');
|
---|
| 16 | var globals = require('globals');
|
---|
| 17 | var os = require('os');
|
---|
| 18 |
|
---|
| 19 | function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
---|
| 20 |
|
---|
| 21 | var debugOrig__default = /*#__PURE__*/_interopDefaultLegacy(debugOrig);
|
---|
| 22 | var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
---|
| 23 | var importFresh__default = /*#__PURE__*/_interopDefaultLegacy(importFresh);
|
---|
| 24 | var Module__default = /*#__PURE__*/_interopDefaultLegacy(Module);
|
---|
| 25 | var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
---|
| 26 | var stripComments__default = /*#__PURE__*/_interopDefaultLegacy(stripComments);
|
---|
| 27 | var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
|
---|
| 28 | var ignore__default = /*#__PURE__*/_interopDefaultLegacy(ignore);
|
---|
| 29 | var util__default = /*#__PURE__*/_interopDefaultLegacy(util);
|
---|
| 30 | var minimatch__default = /*#__PURE__*/_interopDefaultLegacy(minimatch);
|
---|
| 31 | var Ajv__default = /*#__PURE__*/_interopDefaultLegacy(Ajv);
|
---|
| 32 | var globals__default = /*#__PURE__*/_interopDefaultLegacy(globals);
|
---|
| 33 | var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
|
---|
| 34 |
|
---|
| 35 | /**
|
---|
| 36 | * @fileoverview `IgnorePattern` class.
|
---|
| 37 | *
|
---|
| 38 | * `IgnorePattern` class has the set of glob patterns and the base path.
|
---|
| 39 | *
|
---|
| 40 | * It provides two static methods.
|
---|
| 41 | *
|
---|
| 42 | * - `IgnorePattern.createDefaultIgnore(cwd)`
|
---|
| 43 | * Create the default predicate function.
|
---|
| 44 | * - `IgnorePattern.createIgnore(ignorePatterns)`
|
---|
| 45 | * Create the predicate function from multiple `IgnorePattern` objects.
|
---|
| 46 | *
|
---|
| 47 | * It provides two properties and a method.
|
---|
| 48 | *
|
---|
| 49 | * - `patterns`
|
---|
| 50 | * The glob patterns that ignore to lint.
|
---|
| 51 | * - `basePath`
|
---|
| 52 | * The base path of the glob patterns. If absolute paths existed in the
|
---|
| 53 | * glob patterns, those are handled as relative paths to the base path.
|
---|
| 54 | * - `getPatternsRelativeTo(basePath)`
|
---|
| 55 | * Get `patterns` as modified for a given base path. It modifies the
|
---|
| 56 | * absolute paths in the patterns as prepending the difference of two base
|
---|
| 57 | * paths.
|
---|
| 58 | *
|
---|
| 59 | * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes
|
---|
| 60 | * `ignorePatterns` properties.
|
---|
| 61 | *
|
---|
| 62 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 63 | */
|
---|
| 64 |
|
---|
| 65 | const debug$3 = debugOrig__default["default"]("eslintrc:ignore-pattern");
|
---|
| 66 |
|
---|
| 67 | /** @typedef {ReturnType<import("ignore").default>} Ignore */
|
---|
| 68 |
|
---|
| 69 | //------------------------------------------------------------------------------
|
---|
| 70 | // Helpers
|
---|
| 71 | //------------------------------------------------------------------------------
|
---|
| 72 |
|
---|
| 73 | /**
|
---|
| 74 | * Get the path to the common ancestor directory of given paths.
|
---|
| 75 | * @param {string[]} sourcePaths The paths to calculate the common ancestor.
|
---|
| 76 | * @returns {string} The path to the common ancestor directory.
|
---|
| 77 | */
|
---|
| 78 | function getCommonAncestorPath(sourcePaths) {
|
---|
| 79 | let result = sourcePaths[0];
|
---|
| 80 |
|
---|
| 81 | for (let i = 1; i < sourcePaths.length; ++i) {
|
---|
| 82 | const a = result;
|
---|
| 83 | const b = sourcePaths[i];
|
---|
| 84 |
|
---|
| 85 | // Set the shorter one (it's the common ancestor if one includes the other).
|
---|
| 86 | result = a.length < b.length ? a : b;
|
---|
| 87 |
|
---|
| 88 | // Set the common ancestor.
|
---|
| 89 | for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) {
|
---|
| 90 | if (a[j] !== b[j]) {
|
---|
| 91 | result = a.slice(0, lastSepPos);
|
---|
| 92 | break;
|
---|
| 93 | }
|
---|
| 94 | if (a[j] === path__default["default"].sep) {
|
---|
| 95 | lastSepPos = j;
|
---|
| 96 | }
|
---|
| 97 | }
|
---|
| 98 | }
|
---|
| 99 |
|
---|
| 100 | let resolvedResult = result || path__default["default"].sep;
|
---|
| 101 |
|
---|
| 102 | // if Windows common ancestor is root of drive must have trailing slash to be absolute.
|
---|
| 103 | if (resolvedResult && resolvedResult.endsWith(":") && process.platform === "win32") {
|
---|
| 104 | resolvedResult += path__default["default"].sep;
|
---|
| 105 | }
|
---|
| 106 | return resolvedResult;
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 | /**
|
---|
| 110 | * Make relative path.
|
---|
| 111 | * @param {string} from The source path to get relative path.
|
---|
| 112 | * @param {string} to The destination path to get relative path.
|
---|
| 113 | * @returns {string} The relative path.
|
---|
| 114 | */
|
---|
| 115 | function relative(from, to) {
|
---|
| 116 | const relPath = path__default["default"].relative(from, to);
|
---|
| 117 |
|
---|
| 118 | if (path__default["default"].sep === "/") {
|
---|
| 119 | return relPath;
|
---|
| 120 | }
|
---|
| 121 | return relPath.split(path__default["default"].sep).join("/");
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | /**
|
---|
| 125 | * Get the trailing slash if existed.
|
---|
| 126 | * @param {string} filePath The path to check.
|
---|
| 127 | * @returns {string} The trailing slash if existed.
|
---|
| 128 | */
|
---|
| 129 | function dirSuffix(filePath) {
|
---|
| 130 | const isDir = (
|
---|
| 131 | filePath.endsWith(path__default["default"].sep) ||
|
---|
| 132 | (process.platform === "win32" && filePath.endsWith("/"))
|
---|
| 133 | );
|
---|
| 134 |
|
---|
| 135 | return isDir ? "/" : "";
|
---|
| 136 | }
|
---|
| 137 |
|
---|
| 138 | const DefaultPatterns = Object.freeze(["/**/node_modules/*"]);
|
---|
| 139 | const DotPatterns = Object.freeze([".*", "!.eslintrc.*", "!../"]);
|
---|
| 140 |
|
---|
| 141 | //------------------------------------------------------------------------------
|
---|
| 142 | // Public
|
---|
| 143 | //------------------------------------------------------------------------------
|
---|
| 144 |
|
---|
| 145 | class IgnorePattern {
|
---|
| 146 |
|
---|
| 147 | /**
|
---|
| 148 | * The default patterns.
|
---|
| 149 | * @type {string[]}
|
---|
| 150 | */
|
---|
| 151 | static get DefaultPatterns() {
|
---|
| 152 | return DefaultPatterns;
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | /**
|
---|
| 156 | * Create the default predicate function.
|
---|
| 157 | * @param {string} cwd The current working directory.
|
---|
| 158 | * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}}
|
---|
| 159 | * The preficate function.
|
---|
| 160 | * The first argument is an absolute path that is checked.
|
---|
| 161 | * The second argument is the flag to not ignore dotfiles.
|
---|
| 162 | * If the predicate function returned `true`, it means the path should be ignored.
|
---|
| 163 | */
|
---|
| 164 | static createDefaultIgnore(cwd) {
|
---|
| 165 | return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]);
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | /**
|
---|
| 169 | * Create the predicate function from multiple `IgnorePattern` objects.
|
---|
| 170 | * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns.
|
---|
| 171 | * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}}
|
---|
| 172 | * The preficate function.
|
---|
| 173 | * The first argument is an absolute path that is checked.
|
---|
| 174 | * The second argument is the flag to not ignore dotfiles.
|
---|
| 175 | * If the predicate function returned `true`, it means the path should be ignored.
|
---|
| 176 | */
|
---|
| 177 | static createIgnore(ignorePatterns) {
|
---|
| 178 | debug$3("Create with: %o", ignorePatterns);
|
---|
| 179 |
|
---|
| 180 | const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath));
|
---|
| 181 | const patterns = [].concat(
|
---|
| 182 | ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath))
|
---|
| 183 | );
|
---|
| 184 | const ig = ignore__default["default"]({ allowRelativePaths: true }).add([...DotPatterns, ...patterns]);
|
---|
| 185 | const dotIg = ignore__default["default"]({ allowRelativePaths: true }).add(patterns);
|
---|
| 186 |
|
---|
| 187 | debug$3(" processed: %o", { basePath, patterns });
|
---|
| 188 |
|
---|
| 189 | return Object.assign(
|
---|
| 190 | (filePath, dot = false) => {
|
---|
| 191 | assert__default["default"](path__default["default"].isAbsolute(filePath), "'filePath' should be an absolute path.");
|
---|
| 192 | const relPathRaw = relative(basePath, filePath);
|
---|
| 193 | const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath));
|
---|
| 194 | const adoptedIg = dot ? dotIg : ig;
|
---|
| 195 | const result = relPath !== "" && adoptedIg.ignores(relPath);
|
---|
| 196 |
|
---|
| 197 | debug$3("Check", { filePath, dot, relativePath: relPath, result });
|
---|
| 198 | return result;
|
---|
| 199 | },
|
---|
| 200 | { basePath, patterns }
|
---|
| 201 | );
|
---|
| 202 | }
|
---|
| 203 |
|
---|
| 204 | /**
|
---|
| 205 | * Initialize a new `IgnorePattern` instance.
|
---|
| 206 | * @param {string[]} patterns The glob patterns that ignore to lint.
|
---|
| 207 | * @param {string} basePath The base path of `patterns`.
|
---|
| 208 | */
|
---|
| 209 | constructor(patterns, basePath) {
|
---|
| 210 | assert__default["default"](path__default["default"].isAbsolute(basePath), "'basePath' should be an absolute path.");
|
---|
| 211 |
|
---|
| 212 | /**
|
---|
| 213 | * The glob patterns that ignore to lint.
|
---|
| 214 | * @type {string[]}
|
---|
| 215 | */
|
---|
| 216 | this.patterns = patterns;
|
---|
| 217 |
|
---|
| 218 | /**
|
---|
| 219 | * The base path of `patterns`.
|
---|
| 220 | * @type {string}
|
---|
| 221 | */
|
---|
| 222 | this.basePath = basePath;
|
---|
| 223 |
|
---|
| 224 | /**
|
---|
| 225 | * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`.
|
---|
| 226 | *
|
---|
| 227 | * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility.
|
---|
| 228 | * It's `false` as-is for `ignorePatterns` property in config files.
|
---|
| 229 | * @type {boolean}
|
---|
| 230 | */
|
---|
| 231 | this.loose = false;
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | /**
|
---|
| 235 | * Get `patterns` as modified for a given base path. It modifies the
|
---|
| 236 | * absolute paths in the patterns as prepending the difference of two base
|
---|
| 237 | * paths.
|
---|
| 238 | * @param {string} newBasePath The base path.
|
---|
| 239 | * @returns {string[]} Modifired patterns.
|
---|
| 240 | */
|
---|
| 241 | getPatternsRelativeTo(newBasePath) {
|
---|
| 242 | assert__default["default"](path__default["default"].isAbsolute(newBasePath), "'newBasePath' should be an absolute path.");
|
---|
| 243 | const { basePath, loose, patterns } = this;
|
---|
| 244 |
|
---|
| 245 | if (newBasePath === basePath) {
|
---|
| 246 | return patterns;
|
---|
| 247 | }
|
---|
| 248 | const prefix = `/${relative(newBasePath, basePath)}`;
|
---|
| 249 |
|
---|
| 250 | return patterns.map(pattern => {
|
---|
| 251 | const negative = pattern.startsWith("!");
|
---|
| 252 | const head = negative ? "!" : "";
|
---|
| 253 | const body = negative ? pattern.slice(1) : pattern;
|
---|
| 254 |
|
---|
| 255 | if (body.startsWith("/") || body.startsWith("../")) {
|
---|
| 256 | return `${head}${prefix}${body}`;
|
---|
| 257 | }
|
---|
| 258 | return loose ? pattern : `${head}${prefix}/**/${body}`;
|
---|
| 259 | });
|
---|
| 260 | }
|
---|
| 261 | }
|
---|
| 262 |
|
---|
| 263 | /**
|
---|
| 264 | * @fileoverview `ExtractedConfig` class.
|
---|
| 265 | *
|
---|
| 266 | * `ExtractedConfig` class expresses a final configuration for a specific file.
|
---|
| 267 | *
|
---|
| 268 | * It provides one method.
|
---|
| 269 | *
|
---|
| 270 | * - `toCompatibleObjectAsConfigFileContent()`
|
---|
| 271 | * Convert this configuration to the compatible object as the content of
|
---|
| 272 | * config files. It converts the loaded parser and plugins to strings.
|
---|
| 273 | * `CLIEngine#getConfigForFile(filePath)` method uses this method.
|
---|
| 274 | *
|
---|
| 275 | * `ConfigArray#extractConfig(filePath)` creates a `ExtractedConfig` instance.
|
---|
| 276 | *
|
---|
| 277 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 278 | */
|
---|
| 279 |
|
---|
| 280 | // For VSCode intellisense
|
---|
| 281 | /** @typedef {import("../../shared/types").ConfigData} ConfigData */
|
---|
| 282 | /** @typedef {import("../../shared/types").GlobalConf} GlobalConf */
|
---|
| 283 | /** @typedef {import("../../shared/types").SeverityConf} SeverityConf */
|
---|
| 284 | /** @typedef {import("./config-dependency").DependentParser} DependentParser */
|
---|
| 285 | /** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */
|
---|
| 286 |
|
---|
| 287 | /**
|
---|
| 288 | * Check if `xs` starts with `ys`.
|
---|
| 289 | * @template T
|
---|
| 290 | * @param {T[]} xs The array to check.
|
---|
| 291 | * @param {T[]} ys The array that may be the first part of `xs`.
|
---|
| 292 | * @returns {boolean} `true` if `xs` starts with `ys`.
|
---|
| 293 | */
|
---|
| 294 | function startsWith(xs, ys) {
|
---|
| 295 | return xs.length >= ys.length && ys.every((y, i) => y === xs[i]);
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | /**
|
---|
| 299 | * The class for extracted config data.
|
---|
| 300 | */
|
---|
| 301 | class ExtractedConfig {
|
---|
| 302 | constructor() {
|
---|
| 303 |
|
---|
| 304 | /**
|
---|
| 305 | * The config name what `noInlineConfig` setting came from.
|
---|
| 306 | * @type {string}
|
---|
| 307 | */
|
---|
| 308 | this.configNameOfNoInlineConfig = "";
|
---|
| 309 |
|
---|
| 310 | /**
|
---|
| 311 | * Environments.
|
---|
| 312 | * @type {Record<string, boolean>}
|
---|
| 313 | */
|
---|
| 314 | this.env = {};
|
---|
| 315 |
|
---|
| 316 | /**
|
---|
| 317 | * Global variables.
|
---|
| 318 | * @type {Record<string, GlobalConf>}
|
---|
| 319 | */
|
---|
| 320 | this.globals = {};
|
---|
| 321 |
|
---|
| 322 | /**
|
---|
| 323 | * The glob patterns that ignore to lint.
|
---|
| 324 | * @type {(((filePath:string, dot?:boolean) => boolean) & { basePath:string; patterns:string[] }) | undefined}
|
---|
| 325 | */
|
---|
| 326 | this.ignores = void 0;
|
---|
| 327 |
|
---|
| 328 | /**
|
---|
| 329 | * The flag that disables directive comments.
|
---|
| 330 | * @type {boolean|undefined}
|
---|
| 331 | */
|
---|
| 332 | this.noInlineConfig = void 0;
|
---|
| 333 |
|
---|
| 334 | /**
|
---|
| 335 | * Parser definition.
|
---|
| 336 | * @type {DependentParser|null}
|
---|
| 337 | */
|
---|
| 338 | this.parser = null;
|
---|
| 339 |
|
---|
| 340 | /**
|
---|
| 341 | * Options for the parser.
|
---|
| 342 | * @type {Object}
|
---|
| 343 | */
|
---|
| 344 | this.parserOptions = {};
|
---|
| 345 |
|
---|
| 346 | /**
|
---|
| 347 | * Plugin definitions.
|
---|
| 348 | * @type {Record<string, DependentPlugin>}
|
---|
| 349 | */
|
---|
| 350 | this.plugins = {};
|
---|
| 351 |
|
---|
| 352 | /**
|
---|
| 353 | * Processor ID.
|
---|
| 354 | * @type {string|null}
|
---|
| 355 | */
|
---|
| 356 | this.processor = null;
|
---|
| 357 |
|
---|
| 358 | /**
|
---|
| 359 | * The flag that reports unused `eslint-disable` directive comments.
|
---|
| 360 | * @type {boolean|undefined}
|
---|
| 361 | */
|
---|
| 362 | this.reportUnusedDisableDirectives = void 0;
|
---|
| 363 |
|
---|
| 364 | /**
|
---|
| 365 | * Rule settings.
|
---|
| 366 | * @type {Record<string, [SeverityConf, ...any[]]>}
|
---|
| 367 | */
|
---|
| 368 | this.rules = {};
|
---|
| 369 |
|
---|
| 370 | /**
|
---|
| 371 | * Shared settings.
|
---|
| 372 | * @type {Object}
|
---|
| 373 | */
|
---|
| 374 | this.settings = {};
|
---|
| 375 | }
|
---|
| 376 |
|
---|
| 377 | /**
|
---|
| 378 | * Convert this config to the compatible object as a config file content.
|
---|
| 379 | * @returns {ConfigData} The converted object.
|
---|
| 380 | */
|
---|
| 381 | toCompatibleObjectAsConfigFileContent() {
|
---|
| 382 | const {
|
---|
| 383 | /* eslint-disable no-unused-vars */
|
---|
| 384 | configNameOfNoInlineConfig: _ignore1,
|
---|
| 385 | processor: _ignore2,
|
---|
| 386 | /* eslint-enable no-unused-vars */
|
---|
| 387 | ignores,
|
---|
| 388 | ...config
|
---|
| 389 | } = this;
|
---|
| 390 |
|
---|
| 391 | config.parser = config.parser && config.parser.filePath;
|
---|
| 392 | config.plugins = Object.keys(config.plugins).filter(Boolean).reverse();
|
---|
| 393 | config.ignorePatterns = ignores ? ignores.patterns : [];
|
---|
| 394 |
|
---|
| 395 | // Strip the default patterns from `ignorePatterns`.
|
---|
| 396 | if (startsWith(config.ignorePatterns, IgnorePattern.DefaultPatterns)) {
|
---|
| 397 | config.ignorePatterns =
|
---|
| 398 | config.ignorePatterns.slice(IgnorePattern.DefaultPatterns.length);
|
---|
| 399 | }
|
---|
| 400 |
|
---|
| 401 | return config;
|
---|
| 402 | }
|
---|
| 403 | }
|
---|
| 404 |
|
---|
| 405 | /**
|
---|
| 406 | * @fileoverview `ConfigArray` class.
|
---|
| 407 | *
|
---|
| 408 | * `ConfigArray` class expresses the full of a configuration. It has the entry
|
---|
| 409 | * config file, base config files that were extended, loaded parsers, and loaded
|
---|
| 410 | * plugins.
|
---|
| 411 | *
|
---|
| 412 | * `ConfigArray` class provides three properties and two methods.
|
---|
| 413 | *
|
---|
| 414 | * - `pluginEnvironments`
|
---|
| 415 | * - `pluginProcessors`
|
---|
| 416 | * - `pluginRules`
|
---|
| 417 | * The `Map` objects that contain the members of all plugins that this
|
---|
| 418 | * config array contains. Those map objects don't have mutation methods.
|
---|
| 419 | * Those keys are the member ID such as `pluginId/memberName`.
|
---|
| 420 | * - `isRoot()`
|
---|
| 421 | * If `true` then this configuration has `root:true` property.
|
---|
| 422 | * - `extractConfig(filePath)`
|
---|
| 423 | * Extract the final configuration for a given file. This means merging
|
---|
| 424 | * every config array element which that `criteria` property matched. The
|
---|
| 425 | * `filePath` argument must be an absolute path.
|
---|
| 426 | *
|
---|
| 427 | * `ConfigArrayFactory` provides the loading logic of config files.
|
---|
| 428 | *
|
---|
| 429 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 430 | */
|
---|
| 431 |
|
---|
| 432 | //------------------------------------------------------------------------------
|
---|
| 433 | // Helpers
|
---|
| 434 | //------------------------------------------------------------------------------
|
---|
| 435 |
|
---|
| 436 | // Define types for VSCode IntelliSense.
|
---|
| 437 | /** @typedef {import("../../shared/types").Environment} Environment */
|
---|
| 438 | /** @typedef {import("../../shared/types").GlobalConf} GlobalConf */
|
---|
| 439 | /** @typedef {import("../../shared/types").RuleConf} RuleConf */
|
---|
| 440 | /** @typedef {import("../../shared/types").Rule} Rule */
|
---|
| 441 | /** @typedef {import("../../shared/types").Plugin} Plugin */
|
---|
| 442 | /** @typedef {import("../../shared/types").Processor} Processor */
|
---|
| 443 | /** @typedef {import("./config-dependency").DependentParser} DependentParser */
|
---|
| 444 | /** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */
|
---|
| 445 | /** @typedef {import("./override-tester")["OverrideTester"]} OverrideTester */
|
---|
| 446 |
|
---|
| 447 | /**
|
---|
| 448 | * @typedef {Object} ConfigArrayElement
|
---|
| 449 | * @property {string} name The name of this config element.
|
---|
| 450 | * @property {string} filePath The path to the source file of this config element.
|
---|
| 451 | * @property {InstanceType<OverrideTester>|null} criteria The tester for the `files` and `excludedFiles` of this config element.
|
---|
| 452 | * @property {Record<string, boolean>|undefined} env The environment settings.
|
---|
| 453 | * @property {Record<string, GlobalConf>|undefined} globals The global variable settings.
|
---|
| 454 | * @property {IgnorePattern|undefined} ignorePattern The ignore patterns.
|
---|
| 455 | * @property {boolean|undefined} noInlineConfig The flag that disables directive comments.
|
---|
| 456 | * @property {DependentParser|undefined} parser The parser loader.
|
---|
| 457 | * @property {Object|undefined} parserOptions The parser options.
|
---|
| 458 | * @property {Record<string, DependentPlugin>|undefined} plugins The plugin loaders.
|
---|
| 459 | * @property {string|undefined} processor The processor name to refer plugin's processor.
|
---|
| 460 | * @property {boolean|undefined} reportUnusedDisableDirectives The flag to report unused `eslint-disable` comments.
|
---|
| 461 | * @property {boolean|undefined} root The flag to express root.
|
---|
| 462 | * @property {Record<string, RuleConf>|undefined} rules The rule settings
|
---|
| 463 | * @property {Object|undefined} settings The shared settings.
|
---|
| 464 | * @property {"config" | "ignore" | "implicit-processor"} type The element type.
|
---|
| 465 | */
|
---|
| 466 |
|
---|
| 467 | /**
|
---|
| 468 | * @typedef {Object} ConfigArrayInternalSlots
|
---|
| 469 | * @property {Map<string, ExtractedConfig>} cache The cache to extract configs.
|
---|
| 470 | * @property {ReadonlyMap<string, Environment>|null} envMap The map from environment ID to environment definition.
|
---|
| 471 | * @property {ReadonlyMap<string, Processor>|null} processorMap The map from processor ID to environment definition.
|
---|
| 472 | * @property {ReadonlyMap<string, Rule>|null} ruleMap The map from rule ID to rule definition.
|
---|
| 473 | */
|
---|
| 474 |
|
---|
| 475 | /** @type {WeakMap<ConfigArray, ConfigArrayInternalSlots>} */
|
---|
| 476 | const internalSlotsMap$2 = new class extends WeakMap {
|
---|
| 477 | get(key) {
|
---|
| 478 | let value = super.get(key);
|
---|
| 479 |
|
---|
| 480 | if (!value) {
|
---|
| 481 | value = {
|
---|
| 482 | cache: new Map(),
|
---|
| 483 | envMap: null,
|
---|
| 484 | processorMap: null,
|
---|
| 485 | ruleMap: null
|
---|
| 486 | };
|
---|
| 487 | super.set(key, value);
|
---|
| 488 | }
|
---|
| 489 |
|
---|
| 490 | return value;
|
---|
| 491 | }
|
---|
| 492 | }();
|
---|
| 493 |
|
---|
| 494 | /**
|
---|
| 495 | * Get the indices which are matched to a given file.
|
---|
| 496 | * @param {ConfigArrayElement[]} elements The elements.
|
---|
| 497 | * @param {string} filePath The path to a target file.
|
---|
| 498 | * @returns {number[]} The indices.
|
---|
| 499 | */
|
---|
| 500 | function getMatchedIndices(elements, filePath) {
|
---|
| 501 | const indices = [];
|
---|
| 502 |
|
---|
| 503 | for (let i = elements.length - 1; i >= 0; --i) {
|
---|
| 504 | const element = elements[i];
|
---|
| 505 |
|
---|
| 506 | if (!element.criteria || (filePath && element.criteria.test(filePath))) {
|
---|
| 507 | indices.push(i);
|
---|
| 508 | }
|
---|
| 509 | }
|
---|
| 510 |
|
---|
| 511 | return indices;
|
---|
| 512 | }
|
---|
| 513 |
|
---|
| 514 | /**
|
---|
| 515 | * Check if a value is a non-null object.
|
---|
| 516 | * @param {any} x The value to check.
|
---|
| 517 | * @returns {boolean} `true` if the value is a non-null object.
|
---|
| 518 | */
|
---|
| 519 | function isNonNullObject(x) {
|
---|
| 520 | return typeof x === "object" && x !== null;
|
---|
| 521 | }
|
---|
| 522 |
|
---|
| 523 | /**
|
---|
| 524 | * Merge two objects.
|
---|
| 525 | *
|
---|
| 526 | * Assign every property values of `y` to `x` if `x` doesn't have the property.
|
---|
| 527 | * If `x`'s property value is an object, it does recursive.
|
---|
| 528 | * @param {Object} target The destination to merge
|
---|
| 529 | * @param {Object|undefined} source The source to merge.
|
---|
| 530 | * @returns {void}
|
---|
| 531 | */
|
---|
| 532 | function mergeWithoutOverwrite(target, source) {
|
---|
| 533 | if (!isNonNullObject(source)) {
|
---|
| 534 | return;
|
---|
| 535 | }
|
---|
| 536 |
|
---|
| 537 | for (const key of Object.keys(source)) {
|
---|
| 538 | if (key === "__proto__") {
|
---|
| 539 | continue;
|
---|
| 540 | }
|
---|
| 541 |
|
---|
| 542 | if (isNonNullObject(target[key])) {
|
---|
| 543 | mergeWithoutOverwrite(target[key], source[key]);
|
---|
| 544 | } else if (target[key] === void 0) {
|
---|
| 545 | if (isNonNullObject(source[key])) {
|
---|
| 546 | target[key] = Array.isArray(source[key]) ? [] : {};
|
---|
| 547 | mergeWithoutOverwrite(target[key], source[key]);
|
---|
| 548 | } else if (source[key] !== void 0) {
|
---|
| 549 | target[key] = source[key];
|
---|
| 550 | }
|
---|
| 551 | }
|
---|
| 552 | }
|
---|
| 553 | }
|
---|
| 554 |
|
---|
| 555 | /**
|
---|
| 556 | * The error for plugin conflicts.
|
---|
| 557 | */
|
---|
| 558 | class PluginConflictError extends Error {
|
---|
| 559 |
|
---|
| 560 | /**
|
---|
| 561 | * Initialize this error object.
|
---|
| 562 | * @param {string} pluginId The plugin ID.
|
---|
| 563 | * @param {{filePath:string, importerName:string}[]} plugins The resolved plugins.
|
---|
| 564 | */
|
---|
| 565 | constructor(pluginId, plugins) {
|
---|
| 566 | super(`Plugin "${pluginId}" was conflicted between ${plugins.map(p => `"${p.importerName}"`).join(" and ")}.`);
|
---|
| 567 | this.messageTemplate = "plugin-conflict";
|
---|
| 568 | this.messageData = { pluginId, plugins };
|
---|
| 569 | }
|
---|
| 570 | }
|
---|
| 571 |
|
---|
| 572 | /**
|
---|
| 573 | * Merge plugins.
|
---|
| 574 | * `target`'s definition is prior to `source`'s.
|
---|
| 575 | * @param {Record<string, DependentPlugin>} target The destination to merge
|
---|
| 576 | * @param {Record<string, DependentPlugin>|undefined} source The source to merge.
|
---|
| 577 | * @returns {void}
|
---|
| 578 | */
|
---|
| 579 | function mergePlugins(target, source) {
|
---|
| 580 | if (!isNonNullObject(source)) {
|
---|
| 581 | return;
|
---|
| 582 | }
|
---|
| 583 |
|
---|
| 584 | for (const key of Object.keys(source)) {
|
---|
| 585 | if (key === "__proto__") {
|
---|
| 586 | continue;
|
---|
| 587 | }
|
---|
| 588 | const targetValue = target[key];
|
---|
| 589 | const sourceValue = source[key];
|
---|
| 590 |
|
---|
| 591 | // Adopt the plugin which was found at first.
|
---|
| 592 | if (targetValue === void 0) {
|
---|
| 593 | if (sourceValue.error) {
|
---|
| 594 | throw sourceValue.error;
|
---|
| 595 | }
|
---|
| 596 | target[key] = sourceValue;
|
---|
| 597 | } else if (sourceValue.filePath !== targetValue.filePath) {
|
---|
| 598 | throw new PluginConflictError(key, [
|
---|
| 599 | {
|
---|
| 600 | filePath: targetValue.filePath,
|
---|
| 601 | importerName: targetValue.importerName
|
---|
| 602 | },
|
---|
| 603 | {
|
---|
| 604 | filePath: sourceValue.filePath,
|
---|
| 605 | importerName: sourceValue.importerName
|
---|
| 606 | }
|
---|
| 607 | ]);
|
---|
| 608 | }
|
---|
| 609 | }
|
---|
| 610 | }
|
---|
| 611 |
|
---|
| 612 | /**
|
---|
| 613 | * Merge rule configs.
|
---|
| 614 | * `target`'s definition is prior to `source`'s.
|
---|
| 615 | * @param {Record<string, Array>} target The destination to merge
|
---|
| 616 | * @param {Record<string, RuleConf>|undefined} source The source to merge.
|
---|
| 617 | * @returns {void}
|
---|
| 618 | */
|
---|
| 619 | function mergeRuleConfigs(target, source) {
|
---|
| 620 | if (!isNonNullObject(source)) {
|
---|
| 621 | return;
|
---|
| 622 | }
|
---|
| 623 |
|
---|
| 624 | for (const key of Object.keys(source)) {
|
---|
| 625 | if (key === "__proto__") {
|
---|
| 626 | continue;
|
---|
| 627 | }
|
---|
| 628 | const targetDef = target[key];
|
---|
| 629 | const sourceDef = source[key];
|
---|
| 630 |
|
---|
| 631 | // Adopt the rule config which was found at first.
|
---|
| 632 | if (targetDef === void 0) {
|
---|
| 633 | if (Array.isArray(sourceDef)) {
|
---|
| 634 | target[key] = [...sourceDef];
|
---|
| 635 | } else {
|
---|
| 636 | target[key] = [sourceDef];
|
---|
| 637 | }
|
---|
| 638 |
|
---|
| 639 | /*
|
---|
| 640 | * If the first found rule config is severity only and the current rule
|
---|
| 641 | * config has options, merge the severity and the options.
|
---|
| 642 | */
|
---|
| 643 | } else if (
|
---|
| 644 | targetDef.length === 1 &&
|
---|
| 645 | Array.isArray(sourceDef) &&
|
---|
| 646 | sourceDef.length >= 2
|
---|
| 647 | ) {
|
---|
| 648 | targetDef.push(...sourceDef.slice(1));
|
---|
| 649 | }
|
---|
| 650 | }
|
---|
| 651 | }
|
---|
| 652 |
|
---|
| 653 | /**
|
---|
| 654 | * Create the extracted config.
|
---|
| 655 | * @param {ConfigArray} instance The config elements.
|
---|
| 656 | * @param {number[]} indices The indices to use.
|
---|
| 657 | * @returns {ExtractedConfig} The extracted config.
|
---|
| 658 | */
|
---|
| 659 | function createConfig(instance, indices) {
|
---|
| 660 | const config = new ExtractedConfig();
|
---|
| 661 | const ignorePatterns = [];
|
---|
| 662 |
|
---|
| 663 | // Merge elements.
|
---|
| 664 | for (const index of indices) {
|
---|
| 665 | const element = instance[index];
|
---|
| 666 |
|
---|
| 667 | // Adopt the parser which was found at first.
|
---|
| 668 | if (!config.parser && element.parser) {
|
---|
| 669 | if (element.parser.error) {
|
---|
| 670 | throw element.parser.error;
|
---|
| 671 | }
|
---|
| 672 | config.parser = element.parser;
|
---|
| 673 | }
|
---|
| 674 |
|
---|
| 675 | // Adopt the processor which was found at first.
|
---|
| 676 | if (!config.processor && element.processor) {
|
---|
| 677 | config.processor = element.processor;
|
---|
| 678 | }
|
---|
| 679 |
|
---|
| 680 | // Adopt the noInlineConfig which was found at first.
|
---|
| 681 | if (config.noInlineConfig === void 0 && element.noInlineConfig !== void 0) {
|
---|
| 682 | config.noInlineConfig = element.noInlineConfig;
|
---|
| 683 | config.configNameOfNoInlineConfig = element.name;
|
---|
| 684 | }
|
---|
| 685 |
|
---|
| 686 | // Adopt the reportUnusedDisableDirectives which was found at first.
|
---|
| 687 | if (config.reportUnusedDisableDirectives === void 0 && element.reportUnusedDisableDirectives !== void 0) {
|
---|
| 688 | config.reportUnusedDisableDirectives = element.reportUnusedDisableDirectives;
|
---|
| 689 | }
|
---|
| 690 |
|
---|
| 691 | // Collect ignorePatterns
|
---|
| 692 | if (element.ignorePattern) {
|
---|
| 693 | ignorePatterns.push(element.ignorePattern);
|
---|
| 694 | }
|
---|
| 695 |
|
---|
| 696 | // Merge others.
|
---|
| 697 | mergeWithoutOverwrite(config.env, element.env);
|
---|
| 698 | mergeWithoutOverwrite(config.globals, element.globals);
|
---|
| 699 | mergeWithoutOverwrite(config.parserOptions, element.parserOptions);
|
---|
| 700 | mergeWithoutOverwrite(config.settings, element.settings);
|
---|
| 701 | mergePlugins(config.plugins, element.plugins);
|
---|
| 702 | mergeRuleConfigs(config.rules, element.rules);
|
---|
| 703 | }
|
---|
| 704 |
|
---|
| 705 | // Create the predicate function for ignore patterns.
|
---|
| 706 | if (ignorePatterns.length > 0) {
|
---|
| 707 | config.ignores = IgnorePattern.createIgnore(ignorePatterns.reverse());
|
---|
| 708 | }
|
---|
| 709 |
|
---|
| 710 | return config;
|
---|
| 711 | }
|
---|
| 712 |
|
---|
| 713 | /**
|
---|
| 714 | * Collect definitions.
|
---|
| 715 | * @template T, U
|
---|
| 716 | * @param {string} pluginId The plugin ID for prefix.
|
---|
| 717 | * @param {Record<string,T>} defs The definitions to collect.
|
---|
| 718 | * @param {Map<string, U>} map The map to output.
|
---|
| 719 | * @param {function(T): U} [normalize] The normalize function for each value.
|
---|
| 720 | * @returns {void}
|
---|
| 721 | */
|
---|
| 722 | function collect(pluginId, defs, map, normalize) {
|
---|
| 723 | if (defs) {
|
---|
| 724 | const prefix = pluginId && `${pluginId}/`;
|
---|
| 725 |
|
---|
| 726 | for (const [key, value] of Object.entries(defs)) {
|
---|
| 727 | map.set(
|
---|
| 728 | `${prefix}${key}`,
|
---|
| 729 | normalize ? normalize(value) : value
|
---|
| 730 | );
|
---|
| 731 | }
|
---|
| 732 | }
|
---|
| 733 | }
|
---|
| 734 |
|
---|
| 735 | /**
|
---|
| 736 | * Normalize a rule definition.
|
---|
| 737 | * @param {Function|Rule} rule The rule definition to normalize.
|
---|
| 738 | * @returns {Rule} The normalized rule definition.
|
---|
| 739 | */
|
---|
| 740 | function normalizePluginRule(rule) {
|
---|
| 741 | return typeof rule === "function" ? { create: rule } : rule;
|
---|
| 742 | }
|
---|
| 743 |
|
---|
| 744 | /**
|
---|
| 745 | * Delete the mutation methods from a given map.
|
---|
| 746 | * @param {Map<any, any>} map The map object to delete.
|
---|
| 747 | * @returns {void}
|
---|
| 748 | */
|
---|
| 749 | function deleteMutationMethods(map) {
|
---|
| 750 | Object.defineProperties(map, {
|
---|
| 751 | clear: { configurable: true, value: void 0 },
|
---|
| 752 | delete: { configurable: true, value: void 0 },
|
---|
| 753 | set: { configurable: true, value: void 0 }
|
---|
| 754 | });
|
---|
| 755 | }
|
---|
| 756 |
|
---|
| 757 | /**
|
---|
| 758 | * Create `envMap`, `processorMap`, `ruleMap` with the plugins in the config array.
|
---|
| 759 | * @param {ConfigArrayElement[]} elements The config elements.
|
---|
| 760 | * @param {ConfigArrayInternalSlots} slots The internal slots.
|
---|
| 761 | * @returns {void}
|
---|
| 762 | */
|
---|
| 763 | function initPluginMemberMaps(elements, slots) {
|
---|
| 764 | const processed = new Set();
|
---|
| 765 |
|
---|
| 766 | slots.envMap = new Map();
|
---|
| 767 | slots.processorMap = new Map();
|
---|
| 768 | slots.ruleMap = new Map();
|
---|
| 769 |
|
---|
| 770 | for (const element of elements) {
|
---|
| 771 | if (!element.plugins) {
|
---|
| 772 | continue;
|
---|
| 773 | }
|
---|
| 774 |
|
---|
| 775 | for (const [pluginId, value] of Object.entries(element.plugins)) {
|
---|
| 776 | const plugin = value.definition;
|
---|
| 777 |
|
---|
| 778 | if (!plugin || processed.has(pluginId)) {
|
---|
| 779 | continue;
|
---|
| 780 | }
|
---|
| 781 | processed.add(pluginId);
|
---|
| 782 |
|
---|
| 783 | collect(pluginId, plugin.environments, slots.envMap);
|
---|
| 784 | collect(pluginId, plugin.processors, slots.processorMap);
|
---|
| 785 | collect(pluginId, plugin.rules, slots.ruleMap, normalizePluginRule);
|
---|
| 786 | }
|
---|
| 787 | }
|
---|
| 788 |
|
---|
| 789 | deleteMutationMethods(slots.envMap);
|
---|
| 790 | deleteMutationMethods(slots.processorMap);
|
---|
| 791 | deleteMutationMethods(slots.ruleMap);
|
---|
| 792 | }
|
---|
| 793 |
|
---|
| 794 | /**
|
---|
| 795 | * Create `envMap`, `processorMap`, `ruleMap` with the plugins in the config array.
|
---|
| 796 | * @param {ConfigArray} instance The config elements.
|
---|
| 797 | * @returns {ConfigArrayInternalSlots} The extracted config.
|
---|
| 798 | */
|
---|
| 799 | function ensurePluginMemberMaps(instance) {
|
---|
| 800 | const slots = internalSlotsMap$2.get(instance);
|
---|
| 801 |
|
---|
| 802 | if (!slots.ruleMap) {
|
---|
| 803 | initPluginMemberMaps(instance, slots);
|
---|
| 804 | }
|
---|
| 805 |
|
---|
| 806 | return slots;
|
---|
| 807 | }
|
---|
| 808 |
|
---|
| 809 | //------------------------------------------------------------------------------
|
---|
| 810 | // Public Interface
|
---|
| 811 | //------------------------------------------------------------------------------
|
---|
| 812 |
|
---|
| 813 | /**
|
---|
| 814 | * The Config Array.
|
---|
| 815 | *
|
---|
| 816 | * `ConfigArray` instance contains all settings, parsers, and plugins.
|
---|
| 817 | * You need to call `ConfigArray#extractConfig(filePath)` method in order to
|
---|
| 818 | * extract, merge and get only the config data which is related to an arbitrary
|
---|
| 819 | * file.
|
---|
| 820 | * @extends {Array<ConfigArrayElement>}
|
---|
| 821 | */
|
---|
| 822 | class ConfigArray extends Array {
|
---|
| 823 |
|
---|
| 824 | /**
|
---|
| 825 | * Get the plugin environments.
|
---|
| 826 | * The returned map cannot be mutated.
|
---|
| 827 | * @type {ReadonlyMap<string, Environment>} The plugin environments.
|
---|
| 828 | */
|
---|
| 829 | get pluginEnvironments() {
|
---|
| 830 | return ensurePluginMemberMaps(this).envMap;
|
---|
| 831 | }
|
---|
| 832 |
|
---|
| 833 | /**
|
---|
| 834 | * Get the plugin processors.
|
---|
| 835 | * The returned map cannot be mutated.
|
---|
| 836 | * @type {ReadonlyMap<string, Processor>} The plugin processors.
|
---|
| 837 | */
|
---|
| 838 | get pluginProcessors() {
|
---|
| 839 | return ensurePluginMemberMaps(this).processorMap;
|
---|
| 840 | }
|
---|
| 841 |
|
---|
| 842 | /**
|
---|
| 843 | * Get the plugin rules.
|
---|
| 844 | * The returned map cannot be mutated.
|
---|
| 845 | * @returns {ReadonlyMap<string, Rule>} The plugin rules.
|
---|
| 846 | */
|
---|
| 847 | get pluginRules() {
|
---|
| 848 | return ensurePluginMemberMaps(this).ruleMap;
|
---|
| 849 | }
|
---|
| 850 |
|
---|
| 851 | /**
|
---|
| 852 | * Check if this config has `root` flag.
|
---|
| 853 | * @returns {boolean} `true` if this config array is root.
|
---|
| 854 | */
|
---|
| 855 | isRoot() {
|
---|
| 856 | for (let i = this.length - 1; i >= 0; --i) {
|
---|
| 857 | const root = this[i].root;
|
---|
| 858 |
|
---|
| 859 | if (typeof root === "boolean") {
|
---|
| 860 | return root;
|
---|
| 861 | }
|
---|
| 862 | }
|
---|
| 863 | return false;
|
---|
| 864 | }
|
---|
| 865 |
|
---|
| 866 | /**
|
---|
| 867 | * Extract the config data which is related to a given file.
|
---|
| 868 | * @param {string} filePath The absolute path to the target file.
|
---|
| 869 | * @returns {ExtractedConfig} The extracted config data.
|
---|
| 870 | */
|
---|
| 871 | extractConfig(filePath) {
|
---|
| 872 | const { cache } = internalSlotsMap$2.get(this);
|
---|
| 873 | const indices = getMatchedIndices(this, filePath);
|
---|
| 874 | const cacheKey = indices.join(",");
|
---|
| 875 |
|
---|
| 876 | if (!cache.has(cacheKey)) {
|
---|
| 877 | cache.set(cacheKey, createConfig(this, indices));
|
---|
| 878 | }
|
---|
| 879 |
|
---|
| 880 | return cache.get(cacheKey);
|
---|
| 881 | }
|
---|
| 882 |
|
---|
| 883 | /**
|
---|
| 884 | * Check if a given path is an additional lint target.
|
---|
| 885 | * @param {string} filePath The absolute path to the target file.
|
---|
| 886 | * @returns {boolean} `true` if the file is an additional lint target.
|
---|
| 887 | */
|
---|
| 888 | isAdditionalTargetPath(filePath) {
|
---|
| 889 | for (const { criteria, type } of this) {
|
---|
| 890 | if (
|
---|
| 891 | type === "config" &&
|
---|
| 892 | criteria &&
|
---|
| 893 | !criteria.endsWithWildcard &&
|
---|
| 894 | criteria.test(filePath)
|
---|
| 895 | ) {
|
---|
| 896 | return true;
|
---|
| 897 | }
|
---|
| 898 | }
|
---|
| 899 | return false;
|
---|
| 900 | }
|
---|
| 901 | }
|
---|
| 902 |
|
---|
| 903 | /**
|
---|
| 904 | * Get the used extracted configs.
|
---|
| 905 | * CLIEngine will use this method to collect used deprecated rules.
|
---|
| 906 | * @param {ConfigArray} instance The config array object to get.
|
---|
| 907 | * @returns {ExtractedConfig[]} The used extracted configs.
|
---|
| 908 | * @private
|
---|
| 909 | */
|
---|
| 910 | function getUsedExtractedConfigs(instance) {
|
---|
| 911 | const { cache } = internalSlotsMap$2.get(instance);
|
---|
| 912 |
|
---|
| 913 | return Array.from(cache.values());
|
---|
| 914 | }
|
---|
| 915 |
|
---|
| 916 | /**
|
---|
| 917 | * @fileoverview `ConfigDependency` class.
|
---|
| 918 | *
|
---|
| 919 | * `ConfigDependency` class expresses a loaded parser or plugin.
|
---|
| 920 | *
|
---|
| 921 | * If the parser or plugin was loaded successfully, it has `definition` property
|
---|
| 922 | * and `filePath` property. Otherwise, it has `error` property.
|
---|
| 923 | *
|
---|
| 924 | * When `JSON.stringify()` converted a `ConfigDependency` object to a JSON, it
|
---|
| 925 | * omits `definition` property.
|
---|
| 926 | *
|
---|
| 927 | * `ConfigArrayFactory` creates `ConfigDependency` objects when it loads parsers
|
---|
| 928 | * or plugins.
|
---|
| 929 | *
|
---|
| 930 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 931 | */
|
---|
| 932 |
|
---|
| 933 | /**
|
---|
| 934 | * The class is to store parsers or plugins.
|
---|
| 935 | * This class hides the loaded object from `JSON.stringify()` and `console.log`.
|
---|
| 936 | * @template T
|
---|
| 937 | */
|
---|
| 938 | class ConfigDependency {
|
---|
| 939 |
|
---|
| 940 | /**
|
---|
| 941 | * Initialize this instance.
|
---|
| 942 | * @param {Object} data The dependency data.
|
---|
| 943 | * @param {T} [data.definition] The dependency if the loading succeeded.
|
---|
| 944 | * @param {T} [data.original] The original, non-normalized dependency if the loading succeeded.
|
---|
| 945 | * @param {Error} [data.error] The error object if the loading failed.
|
---|
| 946 | * @param {string} [data.filePath] The actual path to the dependency if the loading succeeded.
|
---|
| 947 | * @param {string} data.id The ID of this dependency.
|
---|
| 948 | * @param {string} data.importerName The name of the config file which loads this dependency.
|
---|
| 949 | * @param {string} data.importerPath The path to the config file which loads this dependency.
|
---|
| 950 | */
|
---|
| 951 | constructor({
|
---|
| 952 | definition = null,
|
---|
| 953 | original = null,
|
---|
| 954 | error = null,
|
---|
| 955 | filePath = null,
|
---|
| 956 | id,
|
---|
| 957 | importerName,
|
---|
| 958 | importerPath
|
---|
| 959 | }) {
|
---|
| 960 |
|
---|
| 961 | /**
|
---|
| 962 | * The loaded dependency if the loading succeeded.
|
---|
| 963 | * @type {T|null}
|
---|
| 964 | */
|
---|
| 965 | this.definition = definition;
|
---|
| 966 |
|
---|
| 967 | /**
|
---|
| 968 | * The original dependency as loaded directly from disk if the loading succeeded.
|
---|
| 969 | * @type {T|null}
|
---|
| 970 | */
|
---|
| 971 | this.original = original;
|
---|
| 972 |
|
---|
| 973 | /**
|
---|
| 974 | * The error object if the loading failed.
|
---|
| 975 | * @type {Error|null}
|
---|
| 976 | */
|
---|
| 977 | this.error = error;
|
---|
| 978 |
|
---|
| 979 | /**
|
---|
| 980 | * The loaded dependency if the loading succeeded.
|
---|
| 981 | * @type {string|null}
|
---|
| 982 | */
|
---|
| 983 | this.filePath = filePath;
|
---|
| 984 |
|
---|
| 985 | /**
|
---|
| 986 | * The ID of this dependency.
|
---|
| 987 | * @type {string}
|
---|
| 988 | */
|
---|
| 989 | this.id = id;
|
---|
| 990 |
|
---|
| 991 | /**
|
---|
| 992 | * The name of the config file which loads this dependency.
|
---|
| 993 | * @type {string}
|
---|
| 994 | */
|
---|
| 995 | this.importerName = importerName;
|
---|
| 996 |
|
---|
| 997 | /**
|
---|
| 998 | * The path to the config file which loads this dependency.
|
---|
| 999 | * @type {string}
|
---|
| 1000 | */
|
---|
| 1001 | this.importerPath = importerPath;
|
---|
| 1002 | }
|
---|
| 1003 |
|
---|
| 1004 | // eslint-disable-next-line jsdoc/require-description
|
---|
| 1005 | /**
|
---|
| 1006 | * @returns {Object} a JSON compatible object.
|
---|
| 1007 | */
|
---|
| 1008 | toJSON() {
|
---|
| 1009 | const obj = this[util__default["default"].inspect.custom]();
|
---|
| 1010 |
|
---|
| 1011 | // Display `error.message` (`Error#message` is unenumerable).
|
---|
| 1012 | if (obj.error instanceof Error) {
|
---|
| 1013 | obj.error = { ...obj.error, message: obj.error.message };
|
---|
| 1014 | }
|
---|
| 1015 |
|
---|
| 1016 | return obj;
|
---|
| 1017 | }
|
---|
| 1018 |
|
---|
| 1019 | // eslint-disable-next-line jsdoc/require-description
|
---|
| 1020 | /**
|
---|
| 1021 | * @returns {Object} an object to display by `console.log()`.
|
---|
| 1022 | */
|
---|
| 1023 | [util__default["default"].inspect.custom]() {
|
---|
| 1024 | const {
|
---|
| 1025 | definition: _ignore1, // eslint-disable-line no-unused-vars
|
---|
| 1026 | original: _ignore2, // eslint-disable-line no-unused-vars
|
---|
| 1027 | ...obj
|
---|
| 1028 | } = this;
|
---|
| 1029 |
|
---|
| 1030 | return obj;
|
---|
| 1031 | }
|
---|
| 1032 | }
|
---|
| 1033 |
|
---|
| 1034 | /**
|
---|
| 1035 | * @fileoverview `OverrideTester` class.
|
---|
| 1036 | *
|
---|
| 1037 | * `OverrideTester` class handles `files` property and `excludedFiles` property
|
---|
| 1038 | * of `overrides` config.
|
---|
| 1039 | *
|
---|
| 1040 | * It provides one method.
|
---|
| 1041 | *
|
---|
| 1042 | * - `test(filePath)`
|
---|
| 1043 | * Test if a file path matches the pair of `files` property and
|
---|
| 1044 | * `excludedFiles` property. The `filePath` argument must be an absolute
|
---|
| 1045 | * path.
|
---|
| 1046 | *
|
---|
| 1047 | * `ConfigArrayFactory` creates `OverrideTester` objects when it processes
|
---|
| 1048 | * `overrides` properties.
|
---|
| 1049 | *
|
---|
| 1050 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 1051 | */
|
---|
| 1052 |
|
---|
| 1053 | const { Minimatch } = minimatch__default["default"];
|
---|
| 1054 |
|
---|
| 1055 | const minimatchOpts = { dot: true, matchBase: true };
|
---|
| 1056 |
|
---|
| 1057 | /**
|
---|
| 1058 | * @typedef {Object} Pattern
|
---|
| 1059 | * @property {InstanceType<Minimatch>[] | null} includes The positive matchers.
|
---|
| 1060 | * @property {InstanceType<Minimatch>[] | null} excludes The negative matchers.
|
---|
| 1061 | */
|
---|
| 1062 |
|
---|
| 1063 | /**
|
---|
| 1064 | * Normalize a given pattern to an array.
|
---|
| 1065 | * @param {string|string[]|undefined} patterns A glob pattern or an array of glob patterns.
|
---|
| 1066 | * @returns {string[]|null} Normalized patterns.
|
---|
| 1067 | * @private
|
---|
| 1068 | */
|
---|
| 1069 | function normalizePatterns(patterns) {
|
---|
| 1070 | if (Array.isArray(patterns)) {
|
---|
| 1071 | return patterns.filter(Boolean);
|
---|
| 1072 | }
|
---|
| 1073 | if (typeof patterns === "string" && patterns) {
|
---|
| 1074 | return [patterns];
|
---|
| 1075 | }
|
---|
| 1076 | return [];
|
---|
| 1077 | }
|
---|
| 1078 |
|
---|
| 1079 | /**
|
---|
| 1080 | * Create the matchers of given patterns.
|
---|
| 1081 | * @param {string[]} patterns The patterns.
|
---|
| 1082 | * @returns {InstanceType<Minimatch>[] | null} The matchers.
|
---|
| 1083 | */
|
---|
| 1084 | function toMatcher(patterns) {
|
---|
| 1085 | if (patterns.length === 0) {
|
---|
| 1086 | return null;
|
---|
| 1087 | }
|
---|
| 1088 | return patterns.map(pattern => {
|
---|
| 1089 | if (/^\.[/\\]/u.test(pattern)) {
|
---|
| 1090 | return new Minimatch(
|
---|
| 1091 | pattern.slice(2),
|
---|
| 1092 |
|
---|
| 1093 | // `./*.js` should not match with `subdir/foo.js`
|
---|
| 1094 | { ...minimatchOpts, matchBase: false }
|
---|
| 1095 | );
|
---|
| 1096 | }
|
---|
| 1097 | return new Minimatch(pattern, minimatchOpts);
|
---|
| 1098 | });
|
---|
| 1099 | }
|
---|
| 1100 |
|
---|
| 1101 | /**
|
---|
| 1102 | * Convert a given matcher to string.
|
---|
| 1103 | * @param {Pattern} matchers The matchers.
|
---|
| 1104 | * @returns {string} The string expression of the matcher.
|
---|
| 1105 | */
|
---|
| 1106 | function patternToJson({ includes, excludes }) {
|
---|
| 1107 | return {
|
---|
| 1108 | includes: includes && includes.map(m => m.pattern),
|
---|
| 1109 | excludes: excludes && excludes.map(m => m.pattern)
|
---|
| 1110 | };
|
---|
| 1111 | }
|
---|
| 1112 |
|
---|
| 1113 | /**
|
---|
| 1114 | * The class to test given paths are matched by the patterns.
|
---|
| 1115 | */
|
---|
| 1116 | class OverrideTester {
|
---|
| 1117 |
|
---|
| 1118 | /**
|
---|
| 1119 | * Create a tester with given criteria.
|
---|
| 1120 | * If there are no criteria, returns `null`.
|
---|
| 1121 | * @param {string|string[]} files The glob patterns for included files.
|
---|
| 1122 | * @param {string|string[]} excludedFiles The glob patterns for excluded files.
|
---|
| 1123 | * @param {string} basePath The path to the base directory to test paths.
|
---|
| 1124 | * @returns {OverrideTester|null} The created instance or `null`.
|
---|
| 1125 | */
|
---|
| 1126 | static create(files, excludedFiles, basePath) {
|
---|
| 1127 | const includePatterns = normalizePatterns(files);
|
---|
| 1128 | const excludePatterns = normalizePatterns(excludedFiles);
|
---|
| 1129 | let endsWithWildcard = false;
|
---|
| 1130 |
|
---|
| 1131 | if (includePatterns.length === 0) {
|
---|
| 1132 | return null;
|
---|
| 1133 | }
|
---|
| 1134 |
|
---|
| 1135 | // Rejects absolute paths or relative paths to parents.
|
---|
| 1136 | for (const pattern of includePatterns) {
|
---|
| 1137 | if (path__default["default"].isAbsolute(pattern) || pattern.includes("..")) {
|
---|
| 1138 | throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
|
---|
| 1139 | }
|
---|
| 1140 | if (pattern.endsWith("*")) {
|
---|
| 1141 | endsWithWildcard = true;
|
---|
| 1142 | }
|
---|
| 1143 | }
|
---|
| 1144 | for (const pattern of excludePatterns) {
|
---|
| 1145 | if (path__default["default"].isAbsolute(pattern) || pattern.includes("..")) {
|
---|
| 1146 | throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
|
---|
| 1147 | }
|
---|
| 1148 | }
|
---|
| 1149 |
|
---|
| 1150 | const includes = toMatcher(includePatterns);
|
---|
| 1151 | const excludes = toMatcher(excludePatterns);
|
---|
| 1152 |
|
---|
| 1153 | return new OverrideTester(
|
---|
| 1154 | [{ includes, excludes }],
|
---|
| 1155 | basePath,
|
---|
| 1156 | endsWithWildcard
|
---|
| 1157 | );
|
---|
| 1158 | }
|
---|
| 1159 |
|
---|
| 1160 | /**
|
---|
| 1161 | * Combine two testers by logical and.
|
---|
| 1162 | * If either of the testers was `null`, returns the other tester.
|
---|
| 1163 | * The `basePath` property of the two must be the same value.
|
---|
| 1164 | * @param {OverrideTester|null} a A tester.
|
---|
| 1165 | * @param {OverrideTester|null} b Another tester.
|
---|
| 1166 | * @returns {OverrideTester|null} Combined tester.
|
---|
| 1167 | */
|
---|
| 1168 | static and(a, b) {
|
---|
| 1169 | if (!b) {
|
---|
| 1170 | return a && new OverrideTester(
|
---|
| 1171 | a.patterns,
|
---|
| 1172 | a.basePath,
|
---|
| 1173 | a.endsWithWildcard
|
---|
| 1174 | );
|
---|
| 1175 | }
|
---|
| 1176 | if (!a) {
|
---|
| 1177 | return new OverrideTester(
|
---|
| 1178 | b.patterns,
|
---|
| 1179 | b.basePath,
|
---|
| 1180 | b.endsWithWildcard
|
---|
| 1181 | );
|
---|
| 1182 | }
|
---|
| 1183 |
|
---|
| 1184 | assert__default["default"].strictEqual(a.basePath, b.basePath);
|
---|
| 1185 | return new OverrideTester(
|
---|
| 1186 | a.patterns.concat(b.patterns),
|
---|
| 1187 | a.basePath,
|
---|
| 1188 | a.endsWithWildcard || b.endsWithWildcard
|
---|
| 1189 | );
|
---|
| 1190 | }
|
---|
| 1191 |
|
---|
| 1192 | /**
|
---|
| 1193 | * Initialize this instance.
|
---|
| 1194 | * @param {Pattern[]} patterns The matchers.
|
---|
| 1195 | * @param {string} basePath The base path.
|
---|
| 1196 | * @param {boolean} endsWithWildcard If `true` then a pattern ends with `*`.
|
---|
| 1197 | */
|
---|
| 1198 | constructor(patterns, basePath, endsWithWildcard = false) {
|
---|
| 1199 |
|
---|
| 1200 | /** @type {Pattern[]} */
|
---|
| 1201 | this.patterns = patterns;
|
---|
| 1202 |
|
---|
| 1203 | /** @type {string} */
|
---|
| 1204 | this.basePath = basePath;
|
---|
| 1205 |
|
---|
| 1206 | /** @type {boolean} */
|
---|
| 1207 | this.endsWithWildcard = endsWithWildcard;
|
---|
| 1208 | }
|
---|
| 1209 |
|
---|
| 1210 | /**
|
---|
| 1211 | * Test if a given path is matched or not.
|
---|
| 1212 | * @param {string} filePath The absolute path to the target file.
|
---|
| 1213 | * @returns {boolean} `true` if the path was matched.
|
---|
| 1214 | */
|
---|
| 1215 | test(filePath) {
|
---|
| 1216 | if (typeof filePath !== "string" || !path__default["default"].isAbsolute(filePath)) {
|
---|
| 1217 | throw new Error(`'filePath' should be an absolute path, but got ${filePath}.`);
|
---|
| 1218 | }
|
---|
| 1219 | const relativePath = path__default["default"].relative(this.basePath, filePath);
|
---|
| 1220 |
|
---|
| 1221 | return this.patterns.every(({ includes, excludes }) => (
|
---|
| 1222 | (!includes || includes.some(m => m.match(relativePath))) &&
|
---|
| 1223 | (!excludes || !excludes.some(m => m.match(relativePath)))
|
---|
| 1224 | ));
|
---|
| 1225 | }
|
---|
| 1226 |
|
---|
| 1227 | // eslint-disable-next-line jsdoc/require-description
|
---|
| 1228 | /**
|
---|
| 1229 | * @returns {Object} a JSON compatible object.
|
---|
| 1230 | */
|
---|
| 1231 | toJSON() {
|
---|
| 1232 | if (this.patterns.length === 1) {
|
---|
| 1233 | return {
|
---|
| 1234 | ...patternToJson(this.patterns[0]),
|
---|
| 1235 | basePath: this.basePath
|
---|
| 1236 | };
|
---|
| 1237 | }
|
---|
| 1238 | return {
|
---|
| 1239 | AND: this.patterns.map(patternToJson),
|
---|
| 1240 | basePath: this.basePath
|
---|
| 1241 | };
|
---|
| 1242 | }
|
---|
| 1243 |
|
---|
| 1244 | // eslint-disable-next-line jsdoc/require-description
|
---|
| 1245 | /**
|
---|
| 1246 | * @returns {Object} an object to display by `console.log()`.
|
---|
| 1247 | */
|
---|
| 1248 | [util__default["default"].inspect.custom]() {
|
---|
| 1249 | return this.toJSON();
|
---|
| 1250 | }
|
---|
| 1251 | }
|
---|
| 1252 |
|
---|
| 1253 | /**
|
---|
| 1254 | * @fileoverview `ConfigArray` class.
|
---|
| 1255 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 1256 | */
|
---|
| 1257 |
|
---|
| 1258 | /**
|
---|
| 1259 | * @fileoverview Config file operations. This file must be usable in the browser,
|
---|
| 1260 | * so no Node-specific code can be here.
|
---|
| 1261 | * @author Nicholas C. Zakas
|
---|
| 1262 | */
|
---|
| 1263 |
|
---|
| 1264 | //------------------------------------------------------------------------------
|
---|
| 1265 | // Private
|
---|
| 1266 | //------------------------------------------------------------------------------
|
---|
| 1267 |
|
---|
| 1268 | const RULE_SEVERITY_STRINGS = ["off", "warn", "error"],
|
---|
| 1269 | RULE_SEVERITY = RULE_SEVERITY_STRINGS.reduce((map, value, index) => {
|
---|
| 1270 | map[value] = index;
|
---|
| 1271 | return map;
|
---|
| 1272 | }, {}),
|
---|
| 1273 | VALID_SEVERITIES = [0, 1, 2, "off", "warn", "error"];
|
---|
| 1274 |
|
---|
| 1275 | //------------------------------------------------------------------------------
|
---|
| 1276 | // Public Interface
|
---|
| 1277 | //------------------------------------------------------------------------------
|
---|
| 1278 |
|
---|
| 1279 | /**
|
---|
| 1280 | * Normalizes the severity value of a rule's configuration to a number
|
---|
| 1281 | * @param {(number|string|[number, ...*]|[string, ...*])} ruleConfig A rule's configuration value, generally
|
---|
| 1282 | * received from the user. A valid config value is either 0, 1, 2, the string "off" (treated the same as 0),
|
---|
| 1283 | * the string "warn" (treated the same as 1), the string "error" (treated the same as 2), or an array
|
---|
| 1284 | * whose first element is one of the above values. Strings are matched case-insensitively.
|
---|
| 1285 | * @returns {(0|1|2)} The numeric severity value if the config value was valid, otherwise 0.
|
---|
| 1286 | */
|
---|
| 1287 | function getRuleSeverity(ruleConfig) {
|
---|
| 1288 | const severityValue = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
|
---|
| 1289 |
|
---|
| 1290 | if (severityValue === 0 || severityValue === 1 || severityValue === 2) {
|
---|
| 1291 | return severityValue;
|
---|
| 1292 | }
|
---|
| 1293 |
|
---|
| 1294 | if (typeof severityValue === "string") {
|
---|
| 1295 | return RULE_SEVERITY[severityValue.toLowerCase()] || 0;
|
---|
| 1296 | }
|
---|
| 1297 |
|
---|
| 1298 | return 0;
|
---|
| 1299 | }
|
---|
| 1300 |
|
---|
| 1301 | /**
|
---|
| 1302 | * Converts old-style severity settings (0, 1, 2) into new-style
|
---|
| 1303 | * severity settings (off, warn, error) for all rules. Assumption is that severity
|
---|
| 1304 | * values have already been validated as correct.
|
---|
| 1305 | * @param {Object} config The config object to normalize.
|
---|
| 1306 | * @returns {void}
|
---|
| 1307 | */
|
---|
| 1308 | function normalizeToStrings(config) {
|
---|
| 1309 |
|
---|
| 1310 | if (config.rules) {
|
---|
| 1311 | Object.keys(config.rules).forEach(ruleId => {
|
---|
| 1312 | const ruleConfig = config.rules[ruleId];
|
---|
| 1313 |
|
---|
| 1314 | if (typeof ruleConfig === "number") {
|
---|
| 1315 | config.rules[ruleId] = RULE_SEVERITY_STRINGS[ruleConfig] || RULE_SEVERITY_STRINGS[0];
|
---|
| 1316 | } else if (Array.isArray(ruleConfig) && typeof ruleConfig[0] === "number") {
|
---|
| 1317 | ruleConfig[0] = RULE_SEVERITY_STRINGS[ruleConfig[0]] || RULE_SEVERITY_STRINGS[0];
|
---|
| 1318 | }
|
---|
| 1319 | });
|
---|
| 1320 | }
|
---|
| 1321 | }
|
---|
| 1322 |
|
---|
| 1323 | /**
|
---|
| 1324 | * Determines if the severity for the given rule configuration represents an error.
|
---|
| 1325 | * @param {int|string|Array} ruleConfig The configuration for an individual rule.
|
---|
| 1326 | * @returns {boolean} True if the rule represents an error, false if not.
|
---|
| 1327 | */
|
---|
| 1328 | function isErrorSeverity(ruleConfig) {
|
---|
| 1329 | return getRuleSeverity(ruleConfig) === 2;
|
---|
| 1330 | }
|
---|
| 1331 |
|
---|
| 1332 | /**
|
---|
| 1333 | * Checks whether a given config has valid severity or not.
|
---|
| 1334 | * @param {number|string|Array} ruleConfig The configuration for an individual rule.
|
---|
| 1335 | * @returns {boolean} `true` if the configuration has valid severity.
|
---|
| 1336 | */
|
---|
| 1337 | function isValidSeverity(ruleConfig) {
|
---|
| 1338 | let severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
|
---|
| 1339 |
|
---|
| 1340 | if (typeof severity === "string") {
|
---|
| 1341 | severity = severity.toLowerCase();
|
---|
| 1342 | }
|
---|
| 1343 | return VALID_SEVERITIES.indexOf(severity) !== -1;
|
---|
| 1344 | }
|
---|
| 1345 |
|
---|
| 1346 | /**
|
---|
| 1347 | * Checks whether every rule of a given config has valid severity or not.
|
---|
| 1348 | * @param {Object} config The configuration for rules.
|
---|
| 1349 | * @returns {boolean} `true` if the configuration has valid severity.
|
---|
| 1350 | */
|
---|
| 1351 | function isEverySeverityValid(config) {
|
---|
| 1352 | return Object.keys(config).every(ruleId => isValidSeverity(config[ruleId]));
|
---|
| 1353 | }
|
---|
| 1354 |
|
---|
| 1355 | /**
|
---|
| 1356 | * Normalizes a value for a global in a config
|
---|
| 1357 | * @param {(boolean|string|null)} configuredValue The value given for a global in configuration or in
|
---|
| 1358 | * a global directive comment
|
---|
| 1359 | * @returns {("readable"|"writeable"|"off")} The value normalized as a string
|
---|
| 1360 | * @throws Error if global value is invalid
|
---|
| 1361 | */
|
---|
| 1362 | function normalizeConfigGlobal(configuredValue) {
|
---|
| 1363 | switch (configuredValue) {
|
---|
| 1364 | case "off":
|
---|
| 1365 | return "off";
|
---|
| 1366 |
|
---|
| 1367 | case true:
|
---|
| 1368 | case "true":
|
---|
| 1369 | case "writeable":
|
---|
| 1370 | case "writable":
|
---|
| 1371 | return "writable";
|
---|
| 1372 |
|
---|
| 1373 | case null:
|
---|
| 1374 | case false:
|
---|
| 1375 | case "false":
|
---|
| 1376 | case "readable":
|
---|
| 1377 | case "readonly":
|
---|
| 1378 | return "readonly";
|
---|
| 1379 |
|
---|
| 1380 | default:
|
---|
| 1381 | throw new Error(`'${configuredValue}' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')`);
|
---|
| 1382 | }
|
---|
| 1383 | }
|
---|
| 1384 |
|
---|
| 1385 | var ConfigOps = {
|
---|
| 1386 | __proto__: null,
|
---|
| 1387 | getRuleSeverity: getRuleSeverity,
|
---|
| 1388 | normalizeToStrings: normalizeToStrings,
|
---|
| 1389 | isErrorSeverity: isErrorSeverity,
|
---|
| 1390 | isValidSeverity: isValidSeverity,
|
---|
| 1391 | isEverySeverityValid: isEverySeverityValid,
|
---|
| 1392 | normalizeConfigGlobal: normalizeConfigGlobal
|
---|
| 1393 | };
|
---|
| 1394 |
|
---|
| 1395 | /**
|
---|
| 1396 | * @fileoverview Provide the function that emits deprecation warnings.
|
---|
| 1397 | * @author Toru Nagashima <http://github.com/mysticatea>
|
---|
| 1398 | */
|
---|
| 1399 |
|
---|
| 1400 | //------------------------------------------------------------------------------
|
---|
| 1401 | // Private
|
---|
| 1402 | //------------------------------------------------------------------------------
|
---|
| 1403 |
|
---|
| 1404 | // Defitions for deprecation warnings.
|
---|
| 1405 | const deprecationWarningMessages = {
|
---|
| 1406 | ESLINT_LEGACY_ECMAFEATURES:
|
---|
| 1407 | "The 'ecmaFeatures' config file property is deprecated and has no effect.",
|
---|
| 1408 | ESLINT_PERSONAL_CONFIG_LOAD:
|
---|
| 1409 | "'~/.eslintrc.*' config files have been deprecated. " +
|
---|
| 1410 | "Please use a config file per project or the '--config' option.",
|
---|
| 1411 | ESLINT_PERSONAL_CONFIG_SUPPRESS:
|
---|
| 1412 | "'~/.eslintrc.*' config files have been deprecated. " +
|
---|
| 1413 | "Please remove it or add 'root:true' to the config files in your " +
|
---|
| 1414 | "projects in order to avoid loading '~/.eslintrc.*' accidentally."
|
---|
| 1415 | };
|
---|
| 1416 |
|
---|
| 1417 | const sourceFileErrorCache = new Set();
|
---|
| 1418 |
|
---|
| 1419 | /**
|
---|
| 1420 | * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted
|
---|
| 1421 | * for each unique file path, but repeated invocations with the same file path have no effect.
|
---|
| 1422 | * No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active.
|
---|
| 1423 | * @param {string} source The name of the configuration source to report the warning for.
|
---|
| 1424 | * @param {string} errorCode The warning message to show.
|
---|
| 1425 | * @returns {void}
|
---|
| 1426 | */
|
---|
| 1427 | function emitDeprecationWarning(source, errorCode) {
|
---|
| 1428 | const cacheKey = JSON.stringify({ source, errorCode });
|
---|
| 1429 |
|
---|
| 1430 | if (sourceFileErrorCache.has(cacheKey)) {
|
---|
| 1431 | return;
|
---|
| 1432 | }
|
---|
| 1433 | sourceFileErrorCache.add(cacheKey);
|
---|
| 1434 |
|
---|
| 1435 | const rel = path__default["default"].relative(process.cwd(), source);
|
---|
| 1436 | const message = deprecationWarningMessages[errorCode];
|
---|
| 1437 |
|
---|
| 1438 | process.emitWarning(
|
---|
| 1439 | `${message} (found in "${rel}")`,
|
---|
| 1440 | "DeprecationWarning",
|
---|
| 1441 | errorCode
|
---|
| 1442 | );
|
---|
| 1443 | }
|
---|
| 1444 |
|
---|
| 1445 | /**
|
---|
| 1446 | * @fileoverview The instance of Ajv validator.
|
---|
| 1447 | * @author Evgeny Poberezkin
|
---|
| 1448 | */
|
---|
| 1449 |
|
---|
| 1450 | //-----------------------------------------------------------------------------
|
---|
| 1451 | // Helpers
|
---|
| 1452 | //-----------------------------------------------------------------------------
|
---|
| 1453 |
|
---|
| 1454 | /*
|
---|
| 1455 | * Copied from ajv/lib/refs/json-schema-draft-04.json
|
---|
| 1456 | * The MIT License (MIT)
|
---|
| 1457 | * Copyright (c) 2015-2017 Evgeny Poberezkin
|
---|
| 1458 | */
|
---|
| 1459 | const metaSchema = {
|
---|
| 1460 | id: "http://json-schema.org/draft-04/schema#",
|
---|
| 1461 | $schema: "http://json-schema.org/draft-04/schema#",
|
---|
| 1462 | description: "Core schema meta-schema",
|
---|
| 1463 | definitions: {
|
---|
| 1464 | schemaArray: {
|
---|
| 1465 | type: "array",
|
---|
| 1466 | minItems: 1,
|
---|
| 1467 | items: { $ref: "#" }
|
---|
| 1468 | },
|
---|
| 1469 | positiveInteger: {
|
---|
| 1470 | type: "integer",
|
---|
| 1471 | minimum: 0
|
---|
| 1472 | },
|
---|
| 1473 | positiveIntegerDefault0: {
|
---|
| 1474 | allOf: [{ $ref: "#/definitions/positiveInteger" }, { default: 0 }]
|
---|
| 1475 | },
|
---|
| 1476 | simpleTypes: {
|
---|
| 1477 | enum: ["array", "boolean", "integer", "null", "number", "object", "string"]
|
---|
| 1478 | },
|
---|
| 1479 | stringArray: {
|
---|
| 1480 | type: "array",
|
---|
| 1481 | items: { type: "string" },
|
---|
| 1482 | minItems: 1,
|
---|
| 1483 | uniqueItems: true
|
---|
| 1484 | }
|
---|
| 1485 | },
|
---|
| 1486 | type: "object",
|
---|
| 1487 | properties: {
|
---|
| 1488 | id: {
|
---|
| 1489 | type: "string"
|
---|
| 1490 | },
|
---|
| 1491 | $schema: {
|
---|
| 1492 | type: "string"
|
---|
| 1493 | },
|
---|
| 1494 | title: {
|
---|
| 1495 | type: "string"
|
---|
| 1496 | },
|
---|
| 1497 | description: {
|
---|
| 1498 | type: "string"
|
---|
| 1499 | },
|
---|
| 1500 | default: { },
|
---|
| 1501 | multipleOf: {
|
---|
| 1502 | type: "number",
|
---|
| 1503 | minimum: 0,
|
---|
| 1504 | exclusiveMinimum: true
|
---|
| 1505 | },
|
---|
| 1506 | maximum: {
|
---|
| 1507 | type: "number"
|
---|
| 1508 | },
|
---|
| 1509 | exclusiveMaximum: {
|
---|
| 1510 | type: "boolean",
|
---|
| 1511 | default: false
|
---|
| 1512 | },
|
---|
| 1513 | minimum: {
|
---|
| 1514 | type: "number"
|
---|
| 1515 | },
|
---|
| 1516 | exclusiveMinimum: {
|
---|
| 1517 | type: "boolean",
|
---|
| 1518 | default: false
|
---|
| 1519 | },
|
---|
| 1520 | maxLength: { $ref: "#/definitions/positiveInteger" },
|
---|
| 1521 | minLength: { $ref: "#/definitions/positiveIntegerDefault0" },
|
---|
| 1522 | pattern: {
|
---|
| 1523 | type: "string",
|
---|
| 1524 | format: "regex"
|
---|
| 1525 | },
|
---|
| 1526 | additionalItems: {
|
---|
| 1527 | anyOf: [
|
---|
| 1528 | { type: "boolean" },
|
---|
| 1529 | { $ref: "#" }
|
---|
| 1530 | ],
|
---|
| 1531 | default: { }
|
---|
| 1532 | },
|
---|
| 1533 | items: {
|
---|
| 1534 | anyOf: [
|
---|
| 1535 | { $ref: "#" },
|
---|
| 1536 | { $ref: "#/definitions/schemaArray" }
|
---|
| 1537 | ],
|
---|
| 1538 | default: { }
|
---|
| 1539 | },
|
---|
| 1540 | maxItems: { $ref: "#/definitions/positiveInteger" },
|
---|
| 1541 | minItems: { $ref: "#/definitions/positiveIntegerDefault0" },
|
---|
| 1542 | uniqueItems: {
|
---|
| 1543 | type: "boolean",
|
---|
| 1544 | default: false
|
---|
| 1545 | },
|
---|
| 1546 | maxProperties: { $ref: "#/definitions/positiveInteger" },
|
---|
| 1547 | minProperties: { $ref: "#/definitions/positiveIntegerDefault0" },
|
---|
| 1548 | required: { $ref: "#/definitions/stringArray" },
|
---|
| 1549 | additionalProperties: {
|
---|
| 1550 | anyOf: [
|
---|
| 1551 | { type: "boolean" },
|
---|
| 1552 | { $ref: "#" }
|
---|
| 1553 | ],
|
---|
| 1554 | default: { }
|
---|
| 1555 | },
|
---|
| 1556 | definitions: {
|
---|
| 1557 | type: "object",
|
---|
| 1558 | additionalProperties: { $ref: "#" },
|
---|
| 1559 | default: { }
|
---|
| 1560 | },
|
---|
| 1561 | properties: {
|
---|
| 1562 | type: "object",
|
---|
| 1563 | additionalProperties: { $ref: "#" },
|
---|
| 1564 | default: { }
|
---|
| 1565 | },
|
---|
| 1566 | patternProperties: {
|
---|
| 1567 | type: "object",
|
---|
| 1568 | additionalProperties: { $ref: "#" },
|
---|
| 1569 | default: { }
|
---|
| 1570 | },
|
---|
| 1571 | dependencies: {
|
---|
| 1572 | type: "object",
|
---|
| 1573 | additionalProperties: {
|
---|
| 1574 | anyOf: [
|
---|
| 1575 | { $ref: "#" },
|
---|
| 1576 | { $ref: "#/definitions/stringArray" }
|
---|
| 1577 | ]
|
---|
| 1578 | }
|
---|
| 1579 | },
|
---|
| 1580 | enum: {
|
---|
| 1581 | type: "array",
|
---|
| 1582 | minItems: 1,
|
---|
| 1583 | uniqueItems: true
|
---|
| 1584 | },
|
---|
| 1585 | type: {
|
---|
| 1586 | anyOf: [
|
---|
| 1587 | { $ref: "#/definitions/simpleTypes" },
|
---|
| 1588 | {
|
---|
| 1589 | type: "array",
|
---|
| 1590 | items: { $ref: "#/definitions/simpleTypes" },
|
---|
| 1591 | minItems: 1,
|
---|
| 1592 | uniqueItems: true
|
---|
| 1593 | }
|
---|
| 1594 | ]
|
---|
| 1595 | },
|
---|
| 1596 | format: { type: "string" },
|
---|
| 1597 | allOf: { $ref: "#/definitions/schemaArray" },
|
---|
| 1598 | anyOf: { $ref: "#/definitions/schemaArray" },
|
---|
| 1599 | oneOf: { $ref: "#/definitions/schemaArray" },
|
---|
| 1600 | not: { $ref: "#" }
|
---|
| 1601 | },
|
---|
| 1602 | dependencies: {
|
---|
| 1603 | exclusiveMaximum: ["maximum"],
|
---|
| 1604 | exclusiveMinimum: ["minimum"]
|
---|
| 1605 | },
|
---|
| 1606 | default: { }
|
---|
| 1607 | };
|
---|
| 1608 |
|
---|
| 1609 | //------------------------------------------------------------------------------
|
---|
| 1610 | // Public Interface
|
---|
| 1611 | //------------------------------------------------------------------------------
|
---|
| 1612 |
|
---|
| 1613 | var ajvOrig = (additionalOptions = {}) => {
|
---|
| 1614 | const ajv = new Ajv__default["default"]({
|
---|
| 1615 | meta: false,
|
---|
| 1616 | useDefaults: true,
|
---|
| 1617 | validateSchema: false,
|
---|
| 1618 | missingRefs: "ignore",
|
---|
| 1619 | verbose: true,
|
---|
| 1620 | schemaId: "auto",
|
---|
| 1621 | ...additionalOptions
|
---|
| 1622 | });
|
---|
| 1623 |
|
---|
| 1624 | ajv.addMetaSchema(metaSchema);
|
---|
| 1625 | // eslint-disable-next-line no-underscore-dangle
|
---|
| 1626 | ajv._opts.defaultMeta = metaSchema.id;
|
---|
| 1627 |
|
---|
| 1628 | return ajv;
|
---|
| 1629 | };
|
---|
| 1630 |
|
---|
| 1631 | /**
|
---|
| 1632 | * @fileoverview Defines a schema for configs.
|
---|
| 1633 | * @author Sylvan Mably
|
---|
| 1634 | */
|
---|
| 1635 |
|
---|
| 1636 | const baseConfigProperties = {
|
---|
| 1637 | $schema: { type: "string" },
|
---|
| 1638 | env: { type: "object" },
|
---|
| 1639 | extends: { $ref: "#/definitions/stringOrStrings" },
|
---|
| 1640 | globals: { type: "object" },
|
---|
| 1641 | overrides: {
|
---|
| 1642 | type: "array",
|
---|
| 1643 | items: { $ref: "#/definitions/overrideConfig" },
|
---|
| 1644 | additionalItems: false
|
---|
| 1645 | },
|
---|
| 1646 | parser: { type: ["string", "null"] },
|
---|
| 1647 | parserOptions: { type: "object" },
|
---|
| 1648 | plugins: { type: "array" },
|
---|
| 1649 | processor: { type: "string" },
|
---|
| 1650 | rules: { type: "object" },
|
---|
| 1651 | settings: { type: "object" },
|
---|
| 1652 | noInlineConfig: { type: "boolean" },
|
---|
| 1653 | reportUnusedDisableDirectives: { type: "boolean" },
|
---|
| 1654 |
|
---|
| 1655 | ecmaFeatures: { type: "object" } // deprecated; logs a warning when used
|
---|
| 1656 | };
|
---|
| 1657 |
|
---|
| 1658 | const configSchema = {
|
---|
| 1659 | definitions: {
|
---|
| 1660 | stringOrStrings: {
|
---|
| 1661 | oneOf: [
|
---|
| 1662 | { type: "string" },
|
---|
| 1663 | {
|
---|
| 1664 | type: "array",
|
---|
| 1665 | items: { type: "string" },
|
---|
| 1666 | additionalItems: false
|
---|
| 1667 | }
|
---|
| 1668 | ]
|
---|
| 1669 | },
|
---|
| 1670 | stringOrStringsRequired: {
|
---|
| 1671 | oneOf: [
|
---|
| 1672 | { type: "string" },
|
---|
| 1673 | {
|
---|
| 1674 | type: "array",
|
---|
| 1675 | items: { type: "string" },
|
---|
| 1676 | additionalItems: false,
|
---|
| 1677 | minItems: 1
|
---|
| 1678 | }
|
---|
| 1679 | ]
|
---|
| 1680 | },
|
---|
| 1681 |
|
---|
| 1682 | // Config at top-level.
|
---|
| 1683 | objectConfig: {
|
---|
| 1684 | type: "object",
|
---|
| 1685 | properties: {
|
---|
| 1686 | root: { type: "boolean" },
|
---|
| 1687 | ignorePatterns: { $ref: "#/definitions/stringOrStrings" },
|
---|
| 1688 | ...baseConfigProperties
|
---|
| 1689 | },
|
---|
| 1690 | additionalProperties: false
|
---|
| 1691 | },
|
---|
| 1692 |
|
---|
| 1693 | // Config in `overrides`.
|
---|
| 1694 | overrideConfig: {
|
---|
| 1695 | type: "object",
|
---|
| 1696 | properties: {
|
---|
| 1697 | excludedFiles: { $ref: "#/definitions/stringOrStrings" },
|
---|
| 1698 | files: { $ref: "#/definitions/stringOrStringsRequired" },
|
---|
| 1699 | ...baseConfigProperties
|
---|
| 1700 | },
|
---|
| 1701 | required: ["files"],
|
---|
| 1702 | additionalProperties: false
|
---|
| 1703 | }
|
---|
| 1704 | },
|
---|
| 1705 |
|
---|
| 1706 | $ref: "#/definitions/objectConfig"
|
---|
| 1707 | };
|
---|
| 1708 |
|
---|
| 1709 | /**
|
---|
| 1710 | * @fileoverview Defines environment settings and globals.
|
---|
| 1711 | * @author Elan Shanker
|
---|
| 1712 | */
|
---|
| 1713 |
|
---|
| 1714 | //------------------------------------------------------------------------------
|
---|
| 1715 | // Helpers
|
---|
| 1716 | //------------------------------------------------------------------------------
|
---|
| 1717 |
|
---|
| 1718 | /**
|
---|
| 1719 | * Get the object that has difference.
|
---|
| 1720 | * @param {Record<string,boolean>} current The newer object.
|
---|
| 1721 | * @param {Record<string,boolean>} prev The older object.
|
---|
| 1722 | * @returns {Record<string,boolean>} The difference object.
|
---|
| 1723 | */
|
---|
| 1724 | function getDiff(current, prev) {
|
---|
| 1725 | const retv = {};
|
---|
| 1726 |
|
---|
| 1727 | for (const [key, value] of Object.entries(current)) {
|
---|
| 1728 | if (!Object.hasOwnProperty.call(prev, key)) {
|
---|
| 1729 | retv[key] = value;
|
---|
| 1730 | }
|
---|
| 1731 | }
|
---|
| 1732 |
|
---|
| 1733 | return retv;
|
---|
| 1734 | }
|
---|
| 1735 |
|
---|
| 1736 | const newGlobals2015 = getDiff(globals__default["default"].es2015, globals__default["default"].es5); // 19 variables such as Promise, Map, ...
|
---|
| 1737 | const newGlobals2017 = {
|
---|
| 1738 | Atomics: false,
|
---|
| 1739 | SharedArrayBuffer: false
|
---|
| 1740 | };
|
---|
| 1741 | const newGlobals2020 = {
|
---|
| 1742 | BigInt: false,
|
---|
| 1743 | BigInt64Array: false,
|
---|
| 1744 | BigUint64Array: false,
|
---|
| 1745 | globalThis: false
|
---|
| 1746 | };
|
---|
| 1747 |
|
---|
| 1748 | const newGlobals2021 = {
|
---|
| 1749 | AggregateError: false,
|
---|
| 1750 | FinalizationRegistry: false,
|
---|
| 1751 | WeakRef: false
|
---|
| 1752 | };
|
---|
| 1753 |
|
---|
| 1754 | //------------------------------------------------------------------------------
|
---|
| 1755 | // Public Interface
|
---|
| 1756 | //------------------------------------------------------------------------------
|
---|
| 1757 |
|
---|
| 1758 | /** @type {Map<string, import("../lib/shared/types").Environment>} */
|
---|
| 1759 | var environments = new Map(Object.entries({
|
---|
| 1760 |
|
---|
| 1761 | // Language
|
---|
| 1762 | builtin: {
|
---|
| 1763 | globals: globals__default["default"].es5
|
---|
| 1764 | },
|
---|
| 1765 | es6: {
|
---|
| 1766 | globals: newGlobals2015,
|
---|
| 1767 | parserOptions: {
|
---|
| 1768 | ecmaVersion: 6
|
---|
| 1769 | }
|
---|
| 1770 | },
|
---|
| 1771 | es2015: {
|
---|
| 1772 | globals: newGlobals2015,
|
---|
| 1773 | parserOptions: {
|
---|
| 1774 | ecmaVersion: 6
|
---|
| 1775 | }
|
---|
| 1776 | },
|
---|
| 1777 | es2016: {
|
---|
| 1778 | globals: newGlobals2015,
|
---|
| 1779 | parserOptions: {
|
---|
| 1780 | ecmaVersion: 7
|
---|
| 1781 | }
|
---|
| 1782 | },
|
---|
| 1783 | es2017: {
|
---|
| 1784 | globals: { ...newGlobals2015, ...newGlobals2017 },
|
---|
| 1785 | parserOptions: {
|
---|
| 1786 | ecmaVersion: 8
|
---|
| 1787 | }
|
---|
| 1788 | },
|
---|
| 1789 | es2018: {
|
---|
| 1790 | globals: { ...newGlobals2015, ...newGlobals2017 },
|
---|
| 1791 | parserOptions: {
|
---|
| 1792 | ecmaVersion: 9
|
---|
| 1793 | }
|
---|
| 1794 | },
|
---|
| 1795 | es2019: {
|
---|
| 1796 | globals: { ...newGlobals2015, ...newGlobals2017 },
|
---|
| 1797 | parserOptions: {
|
---|
| 1798 | ecmaVersion: 10
|
---|
| 1799 | }
|
---|
| 1800 | },
|
---|
| 1801 | es2020: {
|
---|
| 1802 | globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020 },
|
---|
| 1803 | parserOptions: {
|
---|
| 1804 | ecmaVersion: 11
|
---|
| 1805 | }
|
---|
| 1806 | },
|
---|
| 1807 | es2021: {
|
---|
| 1808 | globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020, ...newGlobals2021 },
|
---|
| 1809 | parserOptions: {
|
---|
| 1810 | ecmaVersion: 12
|
---|
| 1811 | }
|
---|
| 1812 | },
|
---|
| 1813 | es2022: {
|
---|
| 1814 | globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020, ...newGlobals2021 },
|
---|
| 1815 | parserOptions: {
|
---|
| 1816 | ecmaVersion: 13
|
---|
| 1817 | }
|
---|
| 1818 | },
|
---|
| 1819 | es2023: {
|
---|
| 1820 | globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020, ...newGlobals2021 },
|
---|
| 1821 | parserOptions: {
|
---|
| 1822 | ecmaVersion: 14
|
---|
| 1823 | }
|
---|
| 1824 | },
|
---|
| 1825 | es2024: {
|
---|
| 1826 | globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020, ...newGlobals2021 },
|
---|
| 1827 | parserOptions: {
|
---|
| 1828 | ecmaVersion: 15
|
---|
| 1829 | }
|
---|
| 1830 | },
|
---|
| 1831 |
|
---|
| 1832 | // Platforms
|
---|
| 1833 | browser: {
|
---|
| 1834 | globals: globals__default["default"].browser
|
---|
| 1835 | },
|
---|
| 1836 | node: {
|
---|
| 1837 | globals: globals__default["default"].node,
|
---|
| 1838 | parserOptions: {
|
---|
| 1839 | ecmaFeatures: {
|
---|
| 1840 | globalReturn: true
|
---|
| 1841 | }
|
---|
| 1842 | }
|
---|
| 1843 | },
|
---|
| 1844 | "shared-node-browser": {
|
---|
| 1845 | globals: globals__default["default"]["shared-node-browser"]
|
---|
| 1846 | },
|
---|
| 1847 | worker: {
|
---|
| 1848 | globals: globals__default["default"].worker
|
---|
| 1849 | },
|
---|
| 1850 | serviceworker: {
|
---|
| 1851 | globals: globals__default["default"].serviceworker
|
---|
| 1852 | },
|
---|
| 1853 |
|
---|
| 1854 | // Frameworks
|
---|
| 1855 | commonjs: {
|
---|
| 1856 | globals: globals__default["default"].commonjs,
|
---|
| 1857 | parserOptions: {
|
---|
| 1858 | ecmaFeatures: {
|
---|
| 1859 | globalReturn: true
|
---|
| 1860 | }
|
---|
| 1861 | }
|
---|
| 1862 | },
|
---|
| 1863 | amd: {
|
---|
| 1864 | globals: globals__default["default"].amd
|
---|
| 1865 | },
|
---|
| 1866 | mocha: {
|
---|
| 1867 | globals: globals__default["default"].mocha
|
---|
| 1868 | },
|
---|
| 1869 | jasmine: {
|
---|
| 1870 | globals: globals__default["default"].jasmine
|
---|
| 1871 | },
|
---|
| 1872 | jest: {
|
---|
| 1873 | globals: globals__default["default"].jest
|
---|
| 1874 | },
|
---|
| 1875 | phantomjs: {
|
---|
| 1876 | globals: globals__default["default"].phantomjs
|
---|
| 1877 | },
|
---|
| 1878 | jquery: {
|
---|
| 1879 | globals: globals__default["default"].jquery
|
---|
| 1880 | },
|
---|
| 1881 | qunit: {
|
---|
| 1882 | globals: globals__default["default"].qunit
|
---|
| 1883 | },
|
---|
| 1884 | prototypejs: {
|
---|
| 1885 | globals: globals__default["default"].prototypejs
|
---|
| 1886 | },
|
---|
| 1887 | shelljs: {
|
---|
| 1888 | globals: globals__default["default"].shelljs
|
---|
| 1889 | },
|
---|
| 1890 | meteor: {
|
---|
| 1891 | globals: globals__default["default"].meteor
|
---|
| 1892 | },
|
---|
| 1893 | mongo: {
|
---|
| 1894 | globals: globals__default["default"].mongo
|
---|
| 1895 | },
|
---|
| 1896 | protractor: {
|
---|
| 1897 | globals: globals__default["default"].protractor
|
---|
| 1898 | },
|
---|
| 1899 | applescript: {
|
---|
| 1900 | globals: globals__default["default"].applescript
|
---|
| 1901 | },
|
---|
| 1902 | nashorn: {
|
---|
| 1903 | globals: globals__default["default"].nashorn
|
---|
| 1904 | },
|
---|
| 1905 | atomtest: {
|
---|
| 1906 | globals: globals__default["default"].atomtest
|
---|
| 1907 | },
|
---|
| 1908 | embertest: {
|
---|
| 1909 | globals: globals__default["default"].embertest
|
---|
| 1910 | },
|
---|
| 1911 | webextensions: {
|
---|
| 1912 | globals: globals__default["default"].webextensions
|
---|
| 1913 | },
|
---|
| 1914 | greasemonkey: {
|
---|
| 1915 | globals: globals__default["default"].greasemonkey
|
---|
| 1916 | }
|
---|
| 1917 | }));
|
---|
| 1918 |
|
---|
| 1919 | /**
|
---|
| 1920 | * @fileoverview Validates configs.
|
---|
| 1921 | * @author Brandon Mills
|
---|
| 1922 | */
|
---|
| 1923 |
|
---|
| 1924 | const ajv = ajvOrig();
|
---|
| 1925 |
|
---|
| 1926 | const ruleValidators = new WeakMap();
|
---|
| 1927 | const noop = Function.prototype;
|
---|
| 1928 |
|
---|
| 1929 | //------------------------------------------------------------------------------
|
---|
| 1930 | // Private
|
---|
| 1931 | //------------------------------------------------------------------------------
|
---|
| 1932 | let validateSchema;
|
---|
| 1933 | const severityMap = {
|
---|
| 1934 | error: 2,
|
---|
| 1935 | warn: 1,
|
---|
| 1936 | off: 0
|
---|
| 1937 | };
|
---|
| 1938 |
|
---|
| 1939 | const validated = new WeakSet();
|
---|
| 1940 |
|
---|
| 1941 | //-----------------------------------------------------------------------------
|
---|
| 1942 | // Exports
|
---|
| 1943 | //-----------------------------------------------------------------------------
|
---|
| 1944 |
|
---|
| 1945 | class ConfigValidator {
|
---|
| 1946 | constructor({ builtInRules = new Map() } = {}) {
|
---|
| 1947 | this.builtInRules = builtInRules;
|
---|
| 1948 | }
|
---|
| 1949 |
|
---|
| 1950 | /**
|
---|
| 1951 | * Gets a complete options schema for a rule.
|
---|
| 1952 | * @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
|
---|
| 1953 | * @returns {Object} JSON Schema for the rule's options.
|
---|
| 1954 | */
|
---|
| 1955 | getRuleOptionsSchema(rule) {
|
---|
| 1956 | if (!rule) {
|
---|
| 1957 | return null;
|
---|
| 1958 | }
|
---|
| 1959 |
|
---|
| 1960 | const schema = rule.schema || rule.meta && rule.meta.schema;
|
---|
| 1961 |
|
---|
| 1962 | // Given a tuple of schemas, insert warning level at the beginning
|
---|
| 1963 | if (Array.isArray(schema)) {
|
---|
| 1964 | if (schema.length) {
|
---|
| 1965 | return {
|
---|
| 1966 | type: "array",
|
---|
| 1967 | items: schema,
|
---|
| 1968 | minItems: 0,
|
---|
| 1969 | maxItems: schema.length
|
---|
| 1970 | };
|
---|
| 1971 | }
|
---|
| 1972 | return {
|
---|
| 1973 | type: "array",
|
---|
| 1974 | minItems: 0,
|
---|
| 1975 | maxItems: 0
|
---|
| 1976 | };
|
---|
| 1977 |
|
---|
| 1978 | }
|
---|
| 1979 |
|
---|
| 1980 | // Given a full schema, leave it alone
|
---|
| 1981 | return schema || null;
|
---|
| 1982 | }
|
---|
| 1983 |
|
---|
| 1984 | /**
|
---|
| 1985 | * Validates a rule's severity and returns the severity value. Throws an error if the severity is invalid.
|
---|
| 1986 | * @param {options} options The given options for the rule.
|
---|
| 1987 | * @returns {number|string} The rule's severity value
|
---|
| 1988 | */
|
---|
| 1989 | validateRuleSeverity(options) {
|
---|
| 1990 | const severity = Array.isArray(options) ? options[0] : options;
|
---|
| 1991 | const normSeverity = typeof severity === "string" ? severityMap[severity.toLowerCase()] : severity;
|
---|
| 1992 |
|
---|
| 1993 | if (normSeverity === 0 || normSeverity === 1 || normSeverity === 2) {
|
---|
| 1994 | return normSeverity;
|
---|
| 1995 | }
|
---|
| 1996 |
|
---|
| 1997 | throw new Error(`\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '${util__default["default"].inspect(severity).replace(/'/gu, "\"").replace(/\n/gu, "")}').\n`);
|
---|
| 1998 |
|
---|
| 1999 | }
|
---|
| 2000 |
|
---|
| 2001 | /**
|
---|
| 2002 | * Validates the non-severity options passed to a rule, based on its schema.
|
---|
| 2003 | * @param {{create: Function}} rule The rule to validate
|
---|
| 2004 | * @param {Array} localOptions The options for the rule, excluding severity
|
---|
| 2005 | * @returns {void}
|
---|
| 2006 | */
|
---|
| 2007 | validateRuleSchema(rule, localOptions) {
|
---|
| 2008 | if (!ruleValidators.has(rule)) {
|
---|
| 2009 | const schema = this.getRuleOptionsSchema(rule);
|
---|
| 2010 |
|
---|
| 2011 | if (schema) {
|
---|
| 2012 | ruleValidators.set(rule, ajv.compile(schema));
|
---|
| 2013 | }
|
---|
| 2014 | }
|
---|
| 2015 |
|
---|
| 2016 | const validateRule = ruleValidators.get(rule);
|
---|
| 2017 |
|
---|
| 2018 | if (validateRule) {
|
---|
| 2019 | validateRule(localOptions);
|
---|
| 2020 | if (validateRule.errors) {
|
---|
| 2021 | throw new Error(validateRule.errors.map(
|
---|
| 2022 | error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
|
---|
| 2023 | ).join(""));
|
---|
| 2024 | }
|
---|
| 2025 | }
|
---|
| 2026 | }
|
---|
| 2027 |
|
---|
| 2028 | /**
|
---|
| 2029 | * Validates a rule's options against its schema.
|
---|
| 2030 | * @param {{create: Function}|null} rule The rule that the config is being validated for
|
---|
| 2031 | * @param {string} ruleId The rule's unique name.
|
---|
| 2032 | * @param {Array|number} options The given options for the rule.
|
---|
| 2033 | * @param {string|null} source The name of the configuration source to report in any errors. If null or undefined,
|
---|
| 2034 | * no source is prepended to the message.
|
---|
| 2035 | * @returns {void}
|
---|
| 2036 | */
|
---|
| 2037 | validateRuleOptions(rule, ruleId, options, source = null) {
|
---|
| 2038 | try {
|
---|
| 2039 | const severity = this.validateRuleSeverity(options);
|
---|
| 2040 |
|
---|
| 2041 | if (severity !== 0) {
|
---|
| 2042 | this.validateRuleSchema(rule, Array.isArray(options) ? options.slice(1) : []);
|
---|
| 2043 | }
|
---|
| 2044 | } catch (err) {
|
---|
| 2045 | const enhancedMessage = `Configuration for rule "${ruleId}" is invalid:\n${err.message}`;
|
---|
| 2046 |
|
---|
| 2047 | if (typeof source === "string") {
|
---|
| 2048 | throw new Error(`${source}:\n\t${enhancedMessage}`);
|
---|
| 2049 | } else {
|
---|
| 2050 | throw new Error(enhancedMessage);
|
---|
| 2051 | }
|
---|
| 2052 | }
|
---|
| 2053 | }
|
---|
| 2054 |
|
---|
| 2055 | /**
|
---|
| 2056 | * Validates an environment object
|
---|
| 2057 | * @param {Object} environment The environment config object to validate.
|
---|
| 2058 | * @param {string} source The name of the configuration source to report in any errors.
|
---|
| 2059 | * @param {function(envId:string): Object} [getAdditionalEnv] A map from strings to loaded environments.
|
---|
| 2060 | * @returns {void}
|
---|
| 2061 | */
|
---|
| 2062 | validateEnvironment(
|
---|
| 2063 | environment,
|
---|
| 2064 | source,
|
---|
| 2065 | getAdditionalEnv = noop
|
---|
| 2066 | ) {
|
---|
| 2067 |
|
---|
| 2068 | // not having an environment is ok
|
---|
| 2069 | if (!environment) {
|
---|
| 2070 | return;
|
---|
| 2071 | }
|
---|
| 2072 |
|
---|
| 2073 | Object.keys(environment).forEach(id => {
|
---|
| 2074 | const env = getAdditionalEnv(id) || environments.get(id) || null;
|
---|
| 2075 |
|
---|
| 2076 | if (!env) {
|
---|
| 2077 | const message = `${source}:\n\tEnvironment key "${id}" is unknown\n`;
|
---|
| 2078 |
|
---|
| 2079 | throw new Error(message);
|
---|
| 2080 | }
|
---|
| 2081 | });
|
---|
| 2082 | }
|
---|
| 2083 |
|
---|
| 2084 | /**
|
---|
| 2085 | * Validates a rules config object
|
---|
| 2086 | * @param {Object} rulesConfig The rules config object to validate.
|
---|
| 2087 | * @param {string} source The name of the configuration source to report in any errors.
|
---|
| 2088 | * @param {function(ruleId:string): Object} getAdditionalRule A map from strings to loaded rules
|
---|
| 2089 | * @returns {void}
|
---|
| 2090 | */
|
---|
| 2091 | validateRules(
|
---|
| 2092 | rulesConfig,
|
---|
| 2093 | source,
|
---|
| 2094 | getAdditionalRule = noop
|
---|
| 2095 | ) {
|
---|
| 2096 | if (!rulesConfig) {
|
---|
| 2097 | return;
|
---|
| 2098 | }
|
---|
| 2099 |
|
---|
| 2100 | Object.keys(rulesConfig).forEach(id => {
|
---|
| 2101 | const rule = getAdditionalRule(id) || this.builtInRules.get(id) || null;
|
---|
| 2102 |
|
---|
| 2103 | this.validateRuleOptions(rule, id, rulesConfig[id], source);
|
---|
| 2104 | });
|
---|
| 2105 | }
|
---|
| 2106 |
|
---|
| 2107 | /**
|
---|
| 2108 | * Validates a `globals` section of a config file
|
---|
| 2109 | * @param {Object} globalsConfig The `globals` section
|
---|
| 2110 | * @param {string|null} source The name of the configuration source to report in the event of an error.
|
---|
| 2111 | * @returns {void}
|
---|
| 2112 | */
|
---|
| 2113 | validateGlobals(globalsConfig, source = null) {
|
---|
| 2114 | if (!globalsConfig) {
|
---|
| 2115 | return;
|
---|
| 2116 | }
|
---|
| 2117 |
|
---|
| 2118 | Object.entries(globalsConfig)
|
---|
| 2119 | .forEach(([configuredGlobal, configuredValue]) => {
|
---|
| 2120 | try {
|
---|
| 2121 | normalizeConfigGlobal(configuredValue);
|
---|
| 2122 | } catch (err) {
|
---|
| 2123 | throw new Error(`ESLint configuration of global '${configuredGlobal}' in ${source} is invalid:\n${err.message}`);
|
---|
| 2124 | }
|
---|
| 2125 | });
|
---|
| 2126 | }
|
---|
| 2127 |
|
---|
| 2128 | /**
|
---|
| 2129 | * Validate `processor` configuration.
|
---|
| 2130 | * @param {string|undefined} processorName The processor name.
|
---|
| 2131 | * @param {string} source The name of config file.
|
---|
| 2132 | * @param {function(id:string): Processor} getProcessor The getter of defined processors.
|
---|
| 2133 | * @returns {void}
|
---|
| 2134 | */
|
---|
| 2135 | validateProcessor(processorName, source, getProcessor) {
|
---|
| 2136 | if (processorName && !getProcessor(processorName)) {
|
---|
| 2137 | throw new Error(`ESLint configuration of processor in '${source}' is invalid: '${processorName}' was not found.`);
|
---|
| 2138 | }
|
---|
| 2139 | }
|
---|
| 2140 |
|
---|
| 2141 | /**
|
---|
| 2142 | * Formats an array of schema validation errors.
|
---|
| 2143 | * @param {Array} errors An array of error messages to format.
|
---|
| 2144 | * @returns {string} Formatted error message
|
---|
| 2145 | */
|
---|
| 2146 | formatErrors(errors) {
|
---|
| 2147 | return errors.map(error => {
|
---|
| 2148 | if (error.keyword === "additionalProperties") {
|
---|
| 2149 | const formattedPropertyPath = error.dataPath.length ? `${error.dataPath.slice(1)}.${error.params.additionalProperty}` : error.params.additionalProperty;
|
---|
| 2150 |
|
---|
| 2151 | return `Unexpected top-level property "${formattedPropertyPath}"`;
|
---|
| 2152 | }
|
---|
| 2153 | if (error.keyword === "type") {
|
---|
| 2154 | const formattedField = error.dataPath.slice(1);
|
---|
| 2155 | const formattedExpectedType = Array.isArray(error.schema) ? error.schema.join("/") : error.schema;
|
---|
| 2156 | const formattedValue = JSON.stringify(error.data);
|
---|
| 2157 |
|
---|
| 2158 | return `Property "${formattedField}" is the wrong type (expected ${formattedExpectedType} but got \`${formattedValue}\`)`;
|
---|
| 2159 | }
|
---|
| 2160 |
|
---|
| 2161 | const field = error.dataPath[0] === "." ? error.dataPath.slice(1) : error.dataPath;
|
---|
| 2162 |
|
---|
| 2163 | return `"${field}" ${error.message}. Value: ${JSON.stringify(error.data)}`;
|
---|
| 2164 | }).map(message => `\t- ${message}.\n`).join("");
|
---|
| 2165 | }
|
---|
| 2166 |
|
---|
| 2167 | /**
|
---|
| 2168 | * Validates the top level properties of the config object.
|
---|
| 2169 | * @param {Object} config The config object to validate.
|
---|
| 2170 | * @param {string} source The name of the configuration source to report in any errors.
|
---|
| 2171 | * @returns {void}
|
---|
| 2172 | */
|
---|
| 2173 | validateConfigSchema(config, source = null) {
|
---|
| 2174 | validateSchema = validateSchema || ajv.compile(configSchema);
|
---|
| 2175 |
|
---|
| 2176 | if (!validateSchema(config)) {
|
---|
| 2177 | throw new Error(`ESLint configuration in ${source} is invalid:\n${this.formatErrors(validateSchema.errors)}`);
|
---|
| 2178 | }
|
---|
| 2179 |
|
---|
| 2180 | if (Object.hasOwnProperty.call(config, "ecmaFeatures")) {
|
---|
| 2181 | emitDeprecationWarning(source, "ESLINT_LEGACY_ECMAFEATURES");
|
---|
| 2182 | }
|
---|
| 2183 | }
|
---|
| 2184 |
|
---|
| 2185 | /**
|
---|
| 2186 | * Validates an entire config object.
|
---|
| 2187 | * @param {Object} config The config object to validate.
|
---|
| 2188 | * @param {string} source The name of the configuration source to report in any errors.
|
---|
| 2189 | * @param {function(ruleId:string): Object} [getAdditionalRule] A map from strings to loaded rules.
|
---|
| 2190 | * @param {function(envId:string): Object} [getAdditionalEnv] A map from strings to loaded envs.
|
---|
| 2191 | * @returns {void}
|
---|
| 2192 | */
|
---|
| 2193 | validate(config, source, getAdditionalRule, getAdditionalEnv) {
|
---|
| 2194 | this.validateConfigSchema(config, source);
|
---|
| 2195 | this.validateRules(config.rules, source, getAdditionalRule);
|
---|
| 2196 | this.validateEnvironment(config.env, source, getAdditionalEnv);
|
---|
| 2197 | this.validateGlobals(config.globals, source);
|
---|
| 2198 |
|
---|
| 2199 | for (const override of config.overrides || []) {
|
---|
| 2200 | this.validateRules(override.rules, source, getAdditionalRule);
|
---|
| 2201 | this.validateEnvironment(override.env, source, getAdditionalEnv);
|
---|
| 2202 | this.validateGlobals(config.globals, source);
|
---|
| 2203 | }
|
---|
| 2204 | }
|
---|
| 2205 |
|
---|
| 2206 | /**
|
---|
| 2207 | * Validate config array object.
|
---|
| 2208 | * @param {ConfigArray} configArray The config array to validate.
|
---|
| 2209 | * @returns {void}
|
---|
| 2210 | */
|
---|
| 2211 | validateConfigArray(configArray) {
|
---|
| 2212 | const getPluginEnv = Map.prototype.get.bind(configArray.pluginEnvironments);
|
---|
| 2213 | const getPluginProcessor = Map.prototype.get.bind(configArray.pluginProcessors);
|
---|
| 2214 | const getPluginRule = Map.prototype.get.bind(configArray.pluginRules);
|
---|
| 2215 |
|
---|
| 2216 | // Validate.
|
---|
| 2217 | for (const element of configArray) {
|
---|
| 2218 | if (validated.has(element)) {
|
---|
| 2219 | continue;
|
---|
| 2220 | }
|
---|
| 2221 | validated.add(element);
|
---|
| 2222 |
|
---|
| 2223 | this.validateEnvironment(element.env, element.name, getPluginEnv);
|
---|
| 2224 | this.validateGlobals(element.globals, element.name);
|
---|
| 2225 | this.validateProcessor(element.processor, element.name, getPluginProcessor);
|
---|
| 2226 | this.validateRules(element.rules, element.name, getPluginRule);
|
---|
| 2227 | }
|
---|
| 2228 | }
|
---|
| 2229 |
|
---|
| 2230 | }
|
---|
| 2231 |
|
---|
| 2232 | /**
|
---|
| 2233 | * @fileoverview Common helpers for naming of plugins, formatters and configs
|
---|
| 2234 | */
|
---|
| 2235 |
|
---|
| 2236 | const NAMESPACE_REGEX = /^@.*\//iu;
|
---|
| 2237 |
|
---|
| 2238 | /**
|
---|
| 2239 | * Brings package name to correct format based on prefix
|
---|
| 2240 | * @param {string} name The name of the package.
|
---|
| 2241 | * @param {string} prefix Can be either "eslint-plugin", "eslint-config" or "eslint-formatter"
|
---|
| 2242 | * @returns {string} Normalized name of the package
|
---|
| 2243 | * @private
|
---|
| 2244 | */
|
---|
| 2245 | function normalizePackageName(name, prefix) {
|
---|
| 2246 | let normalizedName = name;
|
---|
| 2247 |
|
---|
| 2248 | /**
|
---|
| 2249 | * On Windows, name can come in with Windows slashes instead of Unix slashes.
|
---|
| 2250 | * Normalize to Unix first to avoid errors later on.
|
---|
| 2251 | * https://github.com/eslint/eslint/issues/5644
|
---|
| 2252 | */
|
---|
| 2253 | if (normalizedName.includes("\\")) {
|
---|
| 2254 | normalizedName = normalizedName.replace(/\\/gu, "/");
|
---|
| 2255 | }
|
---|
| 2256 |
|
---|
| 2257 | if (normalizedName.charAt(0) === "@") {
|
---|
| 2258 |
|
---|
| 2259 | /**
|
---|
| 2260 | * it's a scoped package
|
---|
| 2261 | * package name is the prefix, or just a username
|
---|
| 2262 | */
|
---|
| 2263 | const scopedPackageShortcutRegex = new RegExp(`^(@[^/]+)(?:/(?:${prefix})?)?$`, "u"),
|
---|
| 2264 | scopedPackageNameRegex = new RegExp(`^${prefix}(-|$)`, "u");
|
---|
| 2265 |
|
---|
| 2266 | if (scopedPackageShortcutRegex.test(normalizedName)) {
|
---|
| 2267 | normalizedName = normalizedName.replace(scopedPackageShortcutRegex, `$1/${prefix}`);
|
---|
| 2268 | } else if (!scopedPackageNameRegex.test(normalizedName.split("/")[1])) {
|
---|
| 2269 |
|
---|
| 2270 | /**
|
---|
| 2271 | * for scoped packages, insert the prefix after the first / unless
|
---|
| 2272 | * the path is already @scope/eslint or @scope/eslint-xxx-yyy
|
---|
| 2273 | */
|
---|
| 2274 | normalizedName = normalizedName.replace(/^@([^/]+)\/(.*)$/u, `@$1/${prefix}-$2`);
|
---|
| 2275 | }
|
---|
| 2276 | } else if (!normalizedName.startsWith(`${prefix}-`)) {
|
---|
| 2277 | normalizedName = `${prefix}-${normalizedName}`;
|
---|
| 2278 | }
|
---|
| 2279 |
|
---|
| 2280 | return normalizedName;
|
---|
| 2281 | }
|
---|
| 2282 |
|
---|
| 2283 | /**
|
---|
| 2284 | * Removes the prefix from a fullname.
|
---|
| 2285 | * @param {string} fullname The term which may have the prefix.
|
---|
| 2286 | * @param {string} prefix The prefix to remove.
|
---|
| 2287 | * @returns {string} The term without prefix.
|
---|
| 2288 | */
|
---|
| 2289 | function getShorthandName(fullname, prefix) {
|
---|
| 2290 | if (fullname[0] === "@") {
|
---|
| 2291 | let matchResult = new RegExp(`^(@[^/]+)/${prefix}$`, "u").exec(fullname);
|
---|
| 2292 |
|
---|
| 2293 | if (matchResult) {
|
---|
| 2294 | return matchResult[1];
|
---|
| 2295 | }
|
---|
| 2296 |
|
---|
| 2297 | matchResult = new RegExp(`^(@[^/]+)/${prefix}-(.+)$`, "u").exec(fullname);
|
---|
| 2298 | if (matchResult) {
|
---|
| 2299 | return `${matchResult[1]}/${matchResult[2]}`;
|
---|
| 2300 | }
|
---|
| 2301 | } else if (fullname.startsWith(`${prefix}-`)) {
|
---|
| 2302 | return fullname.slice(prefix.length + 1);
|
---|
| 2303 | }
|
---|
| 2304 |
|
---|
| 2305 | return fullname;
|
---|
| 2306 | }
|
---|
| 2307 |
|
---|
| 2308 | /**
|
---|
| 2309 | * Gets the scope (namespace) of a term.
|
---|
| 2310 | * @param {string} term The term which may have the namespace.
|
---|
| 2311 | * @returns {string} The namespace of the term if it has one.
|
---|
| 2312 | */
|
---|
| 2313 | function getNamespaceFromTerm(term) {
|
---|
| 2314 | const match = term.match(NAMESPACE_REGEX);
|
---|
| 2315 |
|
---|
| 2316 | return match ? match[0] : "";
|
---|
| 2317 | }
|
---|
| 2318 |
|
---|
| 2319 | var naming = {
|
---|
| 2320 | __proto__: null,
|
---|
| 2321 | normalizePackageName: normalizePackageName,
|
---|
| 2322 | getShorthandName: getShorthandName,
|
---|
| 2323 | getNamespaceFromTerm: getNamespaceFromTerm
|
---|
| 2324 | };
|
---|
| 2325 |
|
---|
| 2326 | /**
|
---|
| 2327 | * Utility for resolving a module relative to another module
|
---|
| 2328 | * @author Teddy Katz
|
---|
| 2329 | */
|
---|
| 2330 |
|
---|
| 2331 | /*
|
---|
| 2332 | * `Module.createRequire` is added in v12.2.0. It supports URL as well.
|
---|
| 2333 | * We only support the case where the argument is a filepath, not a URL.
|
---|
| 2334 | */
|
---|
| 2335 | const createRequire = Module__default["default"].createRequire;
|
---|
| 2336 |
|
---|
| 2337 | /**
|
---|
| 2338 | * Resolves a Node module relative to another module
|
---|
| 2339 | * @param {string} moduleName The name of a Node module, or a path to a Node module.
|
---|
| 2340 | * @param {string} relativeToPath An absolute path indicating the module that `moduleName` should be resolved relative to. This must be
|
---|
| 2341 | * a file rather than a directory, but the file need not actually exist.
|
---|
| 2342 | * @returns {string} The absolute path that would result from calling `require.resolve(moduleName)` in a file located at `relativeToPath`
|
---|
| 2343 | */
|
---|
| 2344 | function resolve(moduleName, relativeToPath) {
|
---|
| 2345 | try {
|
---|
| 2346 | return createRequire(relativeToPath).resolve(moduleName);
|
---|
| 2347 | } catch (error) {
|
---|
| 2348 |
|
---|
| 2349 | // This `if` block is for older Node.js than 12.0.0. We can remove this block in the future.
|
---|
| 2350 | if (
|
---|
| 2351 | typeof error === "object" &&
|
---|
| 2352 | error !== null &&
|
---|
| 2353 | error.code === "MODULE_NOT_FOUND" &&
|
---|
| 2354 | !error.requireStack &&
|
---|
| 2355 | error.message.includes(moduleName)
|
---|
| 2356 | ) {
|
---|
| 2357 | error.message += `\nRequire stack:\n- ${relativeToPath}`;
|
---|
| 2358 | }
|
---|
| 2359 | throw error;
|
---|
| 2360 | }
|
---|
| 2361 | }
|
---|
| 2362 |
|
---|
| 2363 | var ModuleResolver = {
|
---|
| 2364 | __proto__: null,
|
---|
| 2365 | resolve: resolve
|
---|
| 2366 | };
|
---|
| 2367 |
|
---|
| 2368 | /**
|
---|
| 2369 | * @fileoverview The factory of `ConfigArray` objects.
|
---|
| 2370 | *
|
---|
| 2371 | * This class provides methods to create `ConfigArray` instance.
|
---|
| 2372 | *
|
---|
| 2373 | * - `create(configData, options)`
|
---|
| 2374 | * Create a `ConfigArray` instance from a config data. This is to handle CLI
|
---|
| 2375 | * options except `--config`.
|
---|
| 2376 | * - `loadFile(filePath, options)`
|
---|
| 2377 | * Create a `ConfigArray` instance from a config file. This is to handle
|
---|
| 2378 | * `--config` option. If the file was not found, throws the following error:
|
---|
| 2379 | * - If the filename was `*.js`, a `MODULE_NOT_FOUND` error.
|
---|
| 2380 | * - If the filename was `package.json`, an IO error or an
|
---|
| 2381 | * `ESLINT_CONFIG_FIELD_NOT_FOUND` error.
|
---|
| 2382 | * - Otherwise, an IO error such as `ENOENT`.
|
---|
| 2383 | * - `loadInDirectory(directoryPath, options)`
|
---|
| 2384 | * Create a `ConfigArray` instance from a config file which is on a given
|
---|
| 2385 | * directory. This tries to load `.eslintrc.*` or `package.json`. If not
|
---|
| 2386 | * found, returns an empty `ConfigArray`.
|
---|
| 2387 | * - `loadESLintIgnore(filePath)`
|
---|
| 2388 | * Create a `ConfigArray` instance from a config file that is `.eslintignore`
|
---|
| 2389 | * format. This is to handle `--ignore-path` option.
|
---|
| 2390 | * - `loadDefaultESLintIgnore()`
|
---|
| 2391 | * Create a `ConfigArray` instance from `.eslintignore` or `package.json` in
|
---|
| 2392 | * the current working directory.
|
---|
| 2393 | *
|
---|
| 2394 | * `ConfigArrayFactory` class has the responsibility that loads configuration
|
---|
| 2395 | * files, including loading `extends`, `parser`, and `plugins`. The created
|
---|
| 2396 | * `ConfigArray` instance has the loaded `extends`, `parser`, and `plugins`.
|
---|
| 2397 | *
|
---|
| 2398 | * But this class doesn't handle cascading. `CascadingConfigArrayFactory` class
|
---|
| 2399 | * handles cascading and hierarchy.
|
---|
| 2400 | *
|
---|
| 2401 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 2402 | */
|
---|
| 2403 |
|
---|
| 2404 | const require$1 = Module.createRequire(require('url').pathToFileURL(__filename).toString());
|
---|
| 2405 |
|
---|
| 2406 | const debug$2 = debugOrig__default["default"]("eslintrc:config-array-factory");
|
---|
| 2407 |
|
---|
| 2408 | //------------------------------------------------------------------------------
|
---|
| 2409 | // Helpers
|
---|
| 2410 | //------------------------------------------------------------------------------
|
---|
| 2411 |
|
---|
| 2412 | const configFilenames = [
|
---|
| 2413 | ".eslintrc.js",
|
---|
| 2414 | ".eslintrc.cjs",
|
---|
| 2415 | ".eslintrc.yaml",
|
---|
| 2416 | ".eslintrc.yml",
|
---|
| 2417 | ".eslintrc.json",
|
---|
| 2418 | ".eslintrc",
|
---|
| 2419 | "package.json"
|
---|
| 2420 | ];
|
---|
| 2421 |
|
---|
| 2422 | // Define types for VSCode IntelliSense.
|
---|
| 2423 | /** @typedef {import("./shared/types").ConfigData} ConfigData */
|
---|
| 2424 | /** @typedef {import("./shared/types").OverrideConfigData} OverrideConfigData */
|
---|
| 2425 | /** @typedef {import("./shared/types").Parser} Parser */
|
---|
| 2426 | /** @typedef {import("./shared/types").Plugin} Plugin */
|
---|
| 2427 | /** @typedef {import("./shared/types").Rule} Rule */
|
---|
| 2428 | /** @typedef {import("./config-array/config-dependency").DependentParser} DependentParser */
|
---|
| 2429 | /** @typedef {import("./config-array/config-dependency").DependentPlugin} DependentPlugin */
|
---|
| 2430 | /** @typedef {ConfigArray[0]} ConfigArrayElement */
|
---|
| 2431 |
|
---|
| 2432 | /**
|
---|
| 2433 | * @typedef {Object} ConfigArrayFactoryOptions
|
---|
| 2434 | * @property {Map<string,Plugin>} [additionalPluginPool] The map for additional plugins.
|
---|
| 2435 | * @property {string} [cwd] The path to the current working directory.
|
---|
| 2436 | * @property {string} [resolvePluginsRelativeTo] A path to the directory that plugins should be resolved from. Defaults to `cwd`.
|
---|
| 2437 | * @property {Map<string,Rule>} builtInRules The rules that are built in to ESLint.
|
---|
| 2438 | * @property {Object} [resolver=ModuleResolver] The module resolver object.
|
---|
| 2439 | * @property {string} eslintAllPath The path to the definitions for eslint:all.
|
---|
| 2440 | * @property {Function} getEslintAllConfig Returns the config data for eslint:all.
|
---|
| 2441 | * @property {string} eslintRecommendedPath The path to the definitions for eslint:recommended.
|
---|
| 2442 | * @property {Function} getEslintRecommendedConfig Returns the config data for eslint:recommended.
|
---|
| 2443 | */
|
---|
| 2444 |
|
---|
| 2445 | /**
|
---|
| 2446 | * @typedef {Object} ConfigArrayFactoryInternalSlots
|
---|
| 2447 | * @property {Map<string,Plugin>} additionalPluginPool The map for additional plugins.
|
---|
| 2448 | * @property {string} cwd The path to the current working directory.
|
---|
| 2449 | * @property {string | undefined} resolvePluginsRelativeTo An absolute path the the directory that plugins should be resolved from.
|
---|
| 2450 | * @property {Map<string,Rule>} builtInRules The rules that are built in to ESLint.
|
---|
| 2451 | * @property {Object} [resolver=ModuleResolver] The module resolver object.
|
---|
| 2452 | * @property {string} eslintAllPath The path to the definitions for eslint:all.
|
---|
| 2453 | * @property {Function} getEslintAllConfig Returns the config data for eslint:all.
|
---|
| 2454 | * @property {string} eslintRecommendedPath The path to the definitions for eslint:recommended.
|
---|
| 2455 | * @property {Function} getEslintRecommendedConfig Returns the config data for eslint:recommended.
|
---|
| 2456 | */
|
---|
| 2457 |
|
---|
| 2458 | /**
|
---|
| 2459 | * @typedef {Object} ConfigArrayFactoryLoadingContext
|
---|
| 2460 | * @property {string} filePath The path to the current configuration.
|
---|
| 2461 | * @property {string} matchBasePath The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`.
|
---|
| 2462 | * @property {string} name The name of the current configuration.
|
---|
| 2463 | * @property {string} pluginBasePath The base path to resolve plugins.
|
---|
| 2464 | * @property {"config" | "ignore" | "implicit-processor"} type The type of the current configuration. This is `"config"` in normal. This is `"ignore"` if it came from `.eslintignore`. This is `"implicit-processor"` if it came from legacy file-extension processors.
|
---|
| 2465 | */
|
---|
| 2466 |
|
---|
| 2467 | /**
|
---|
| 2468 | * @typedef {Object} ConfigArrayFactoryLoadingContext
|
---|
| 2469 | * @property {string} filePath The path to the current configuration.
|
---|
| 2470 | * @property {string} matchBasePath The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`.
|
---|
| 2471 | * @property {string} name The name of the current configuration.
|
---|
| 2472 | * @property {"config" | "ignore" | "implicit-processor"} type The type of the current configuration. This is `"config"` in normal. This is `"ignore"` if it came from `.eslintignore`. This is `"implicit-processor"` if it came from legacy file-extension processors.
|
---|
| 2473 | */
|
---|
| 2474 |
|
---|
| 2475 | /** @type {WeakMap<ConfigArrayFactory, ConfigArrayFactoryInternalSlots>} */
|
---|
| 2476 | const internalSlotsMap$1 = new WeakMap();
|
---|
| 2477 |
|
---|
| 2478 | /** @type {WeakMap<object, Plugin>} */
|
---|
| 2479 | const normalizedPlugins = new WeakMap();
|
---|
| 2480 |
|
---|
| 2481 | /**
|
---|
| 2482 | * Check if a given string is a file path.
|
---|
| 2483 | * @param {string} nameOrPath A module name or file path.
|
---|
| 2484 | * @returns {boolean} `true` if the `nameOrPath` is a file path.
|
---|
| 2485 | */
|
---|
| 2486 | function isFilePath(nameOrPath) {
|
---|
| 2487 | return (
|
---|
| 2488 | /^\.{1,2}[/\\]/u.test(nameOrPath) ||
|
---|
| 2489 | path__default["default"].isAbsolute(nameOrPath)
|
---|
| 2490 | );
|
---|
| 2491 | }
|
---|
| 2492 |
|
---|
| 2493 | /**
|
---|
| 2494 | * Convenience wrapper for synchronously reading file contents.
|
---|
| 2495 | * @param {string} filePath The filename to read.
|
---|
| 2496 | * @returns {string} The file contents, with the BOM removed.
|
---|
| 2497 | * @private
|
---|
| 2498 | */
|
---|
| 2499 | function readFile(filePath) {
|
---|
| 2500 | return fs__default["default"].readFileSync(filePath, "utf8").replace(/^\ufeff/u, "");
|
---|
| 2501 | }
|
---|
| 2502 |
|
---|
| 2503 | /**
|
---|
| 2504 | * Loads a YAML configuration from a file.
|
---|
| 2505 | * @param {string} filePath The filename to load.
|
---|
| 2506 | * @returns {ConfigData} The configuration object from the file.
|
---|
| 2507 | * @throws {Error} If the file cannot be read.
|
---|
| 2508 | * @private
|
---|
| 2509 | */
|
---|
| 2510 | function loadYAMLConfigFile(filePath) {
|
---|
| 2511 | debug$2(`Loading YAML config file: ${filePath}`);
|
---|
| 2512 |
|
---|
| 2513 | // lazy load YAML to improve performance when not used
|
---|
| 2514 | const yaml = require$1("js-yaml");
|
---|
| 2515 |
|
---|
| 2516 | try {
|
---|
| 2517 |
|
---|
| 2518 | // empty YAML file can be null, so always use
|
---|
| 2519 | return yaml.load(readFile(filePath)) || {};
|
---|
| 2520 | } catch (e) {
|
---|
| 2521 | debug$2(`Error reading YAML file: ${filePath}`);
|
---|
| 2522 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
---|
| 2523 | throw e;
|
---|
| 2524 | }
|
---|
| 2525 | }
|
---|
| 2526 |
|
---|
| 2527 | /**
|
---|
| 2528 | * Loads a JSON configuration from a file.
|
---|
| 2529 | * @param {string} filePath The filename to load.
|
---|
| 2530 | * @returns {ConfigData} The configuration object from the file.
|
---|
| 2531 | * @throws {Error} If the file cannot be read.
|
---|
| 2532 | * @private
|
---|
| 2533 | */
|
---|
| 2534 | function loadJSONConfigFile(filePath) {
|
---|
| 2535 | debug$2(`Loading JSON config file: ${filePath}`);
|
---|
| 2536 |
|
---|
| 2537 | try {
|
---|
| 2538 | return JSON.parse(stripComments__default["default"](readFile(filePath)));
|
---|
| 2539 | } catch (e) {
|
---|
| 2540 | debug$2(`Error reading JSON file: ${filePath}`);
|
---|
| 2541 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
---|
| 2542 | e.messageTemplate = "failed-to-read-json";
|
---|
| 2543 | e.messageData = {
|
---|
| 2544 | path: filePath,
|
---|
| 2545 | message: e.message
|
---|
| 2546 | };
|
---|
| 2547 | throw e;
|
---|
| 2548 | }
|
---|
| 2549 | }
|
---|
| 2550 |
|
---|
| 2551 | /**
|
---|
| 2552 | * Loads a legacy (.eslintrc) configuration from a file.
|
---|
| 2553 | * @param {string} filePath The filename to load.
|
---|
| 2554 | * @returns {ConfigData} The configuration object from the file.
|
---|
| 2555 | * @throws {Error} If the file cannot be read.
|
---|
| 2556 | * @private
|
---|
| 2557 | */
|
---|
| 2558 | function loadLegacyConfigFile(filePath) {
|
---|
| 2559 | debug$2(`Loading legacy config file: ${filePath}`);
|
---|
| 2560 |
|
---|
| 2561 | // lazy load YAML to improve performance when not used
|
---|
| 2562 | const yaml = require$1("js-yaml");
|
---|
| 2563 |
|
---|
| 2564 | try {
|
---|
| 2565 | return yaml.load(stripComments__default["default"](readFile(filePath))) || /* istanbul ignore next */ {};
|
---|
| 2566 | } catch (e) {
|
---|
| 2567 | debug$2("Error reading YAML file: %s\n%o", filePath, e);
|
---|
| 2568 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
---|
| 2569 | throw e;
|
---|
| 2570 | }
|
---|
| 2571 | }
|
---|
| 2572 |
|
---|
| 2573 | /**
|
---|
| 2574 | * Loads a JavaScript configuration from a file.
|
---|
| 2575 | * @param {string} filePath The filename to load.
|
---|
| 2576 | * @returns {ConfigData} The configuration object from the file.
|
---|
| 2577 | * @throws {Error} If the file cannot be read.
|
---|
| 2578 | * @private
|
---|
| 2579 | */
|
---|
| 2580 | function loadJSConfigFile(filePath) {
|
---|
| 2581 | debug$2(`Loading JS config file: ${filePath}`);
|
---|
| 2582 | try {
|
---|
| 2583 | return importFresh__default["default"](filePath);
|
---|
| 2584 | } catch (e) {
|
---|
| 2585 | debug$2(`Error reading JavaScript file: ${filePath}`);
|
---|
| 2586 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
---|
| 2587 | throw e;
|
---|
| 2588 | }
|
---|
| 2589 | }
|
---|
| 2590 |
|
---|
| 2591 | /**
|
---|
| 2592 | * Loads a configuration from a package.json file.
|
---|
| 2593 | * @param {string} filePath The filename to load.
|
---|
| 2594 | * @returns {ConfigData} The configuration object from the file.
|
---|
| 2595 | * @throws {Error} If the file cannot be read.
|
---|
| 2596 | * @private
|
---|
| 2597 | */
|
---|
| 2598 | function loadPackageJSONConfigFile(filePath) {
|
---|
| 2599 | debug$2(`Loading package.json config file: ${filePath}`);
|
---|
| 2600 | try {
|
---|
| 2601 | const packageData = loadJSONConfigFile(filePath);
|
---|
| 2602 |
|
---|
| 2603 | if (!Object.hasOwnProperty.call(packageData, "eslintConfig")) {
|
---|
| 2604 | throw Object.assign(
|
---|
| 2605 | new Error("package.json file doesn't have 'eslintConfig' field."),
|
---|
| 2606 | { code: "ESLINT_CONFIG_FIELD_NOT_FOUND" }
|
---|
| 2607 | );
|
---|
| 2608 | }
|
---|
| 2609 |
|
---|
| 2610 | return packageData.eslintConfig;
|
---|
| 2611 | } catch (e) {
|
---|
| 2612 | debug$2(`Error reading package.json file: ${filePath}`);
|
---|
| 2613 | e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`;
|
---|
| 2614 | throw e;
|
---|
| 2615 | }
|
---|
| 2616 | }
|
---|
| 2617 |
|
---|
| 2618 | /**
|
---|
| 2619 | * Loads a `.eslintignore` from a file.
|
---|
| 2620 | * @param {string} filePath The filename to load.
|
---|
| 2621 | * @returns {string[]} The ignore patterns from the file.
|
---|
| 2622 | * @private
|
---|
| 2623 | */
|
---|
| 2624 | function loadESLintIgnoreFile(filePath) {
|
---|
| 2625 | debug$2(`Loading .eslintignore file: ${filePath}`);
|
---|
| 2626 |
|
---|
| 2627 | try {
|
---|
| 2628 | return readFile(filePath)
|
---|
| 2629 | .split(/\r?\n/gu)
|
---|
| 2630 | .filter(line => line.trim() !== "" && !line.startsWith("#"));
|
---|
| 2631 | } catch (e) {
|
---|
| 2632 | debug$2(`Error reading .eslintignore file: ${filePath}`);
|
---|
| 2633 | e.message = `Cannot read .eslintignore file: ${filePath}\nError: ${e.message}`;
|
---|
| 2634 | throw e;
|
---|
| 2635 | }
|
---|
| 2636 | }
|
---|
| 2637 |
|
---|
| 2638 | /**
|
---|
| 2639 | * Creates an error to notify about a missing config to extend from.
|
---|
| 2640 | * @param {string} configName The name of the missing config.
|
---|
| 2641 | * @param {string} importerName The name of the config that imported the missing config
|
---|
| 2642 | * @param {string} messageTemplate The text template to source error strings from.
|
---|
| 2643 | * @returns {Error} The error object to throw
|
---|
| 2644 | * @private
|
---|
| 2645 | */
|
---|
| 2646 | function configInvalidError(configName, importerName, messageTemplate) {
|
---|
| 2647 | return Object.assign(
|
---|
| 2648 | new Error(`Failed to load config "${configName}" to extend from.`),
|
---|
| 2649 | {
|
---|
| 2650 | messageTemplate,
|
---|
| 2651 | messageData: { configName, importerName }
|
---|
| 2652 | }
|
---|
| 2653 | );
|
---|
| 2654 | }
|
---|
| 2655 |
|
---|
| 2656 | /**
|
---|
| 2657 | * Loads a configuration file regardless of the source. Inspects the file path
|
---|
| 2658 | * to determine the correctly way to load the config file.
|
---|
| 2659 | * @param {string} filePath The path to the configuration.
|
---|
| 2660 | * @returns {ConfigData|null} The configuration information.
|
---|
| 2661 | * @private
|
---|
| 2662 | */
|
---|
| 2663 | function loadConfigFile(filePath) {
|
---|
| 2664 | switch (path__default["default"].extname(filePath)) {
|
---|
| 2665 | case ".js":
|
---|
| 2666 | case ".cjs":
|
---|
| 2667 | return loadJSConfigFile(filePath);
|
---|
| 2668 |
|
---|
| 2669 | case ".json":
|
---|
| 2670 | if (path__default["default"].basename(filePath) === "package.json") {
|
---|
| 2671 | return loadPackageJSONConfigFile(filePath);
|
---|
| 2672 | }
|
---|
| 2673 | return loadJSONConfigFile(filePath);
|
---|
| 2674 |
|
---|
| 2675 | case ".yaml":
|
---|
| 2676 | case ".yml":
|
---|
| 2677 | return loadYAMLConfigFile(filePath);
|
---|
| 2678 |
|
---|
| 2679 | default:
|
---|
| 2680 | return loadLegacyConfigFile(filePath);
|
---|
| 2681 | }
|
---|
| 2682 | }
|
---|
| 2683 |
|
---|
| 2684 | /**
|
---|
| 2685 | * Write debug log.
|
---|
| 2686 | * @param {string} request The requested module name.
|
---|
| 2687 | * @param {string} relativeTo The file path to resolve the request relative to.
|
---|
| 2688 | * @param {string} filePath The resolved file path.
|
---|
| 2689 | * @returns {void}
|
---|
| 2690 | */
|
---|
| 2691 | function writeDebugLogForLoading(request, relativeTo, filePath) {
|
---|
| 2692 | /* istanbul ignore next */
|
---|
| 2693 | if (debug$2.enabled) {
|
---|
| 2694 | let nameAndVersion = null;
|
---|
| 2695 |
|
---|
| 2696 | try {
|
---|
| 2697 | const packageJsonPath = resolve(
|
---|
| 2698 | `${request}/package.json`,
|
---|
| 2699 | relativeTo
|
---|
| 2700 | );
|
---|
| 2701 | const { version = "unknown" } = require$1(packageJsonPath);
|
---|
| 2702 |
|
---|
| 2703 | nameAndVersion = `${request}@${version}`;
|
---|
| 2704 | } catch (error) {
|
---|
| 2705 | debug$2("package.json was not found:", error.message);
|
---|
| 2706 | nameAndVersion = request;
|
---|
| 2707 | }
|
---|
| 2708 |
|
---|
| 2709 | debug$2("Loaded: %s (%s)", nameAndVersion, filePath);
|
---|
| 2710 | }
|
---|
| 2711 | }
|
---|
| 2712 |
|
---|
| 2713 | /**
|
---|
| 2714 | * Create a new context with default values.
|
---|
| 2715 | * @param {ConfigArrayFactoryInternalSlots} slots The internal slots.
|
---|
| 2716 | * @param {"config" | "ignore" | "implicit-processor" | undefined} providedType The type of the current configuration. Default is `"config"`.
|
---|
| 2717 | * @param {string | undefined} providedName The name of the current configuration. Default is the relative path from `cwd` to `filePath`.
|
---|
| 2718 | * @param {string | undefined} providedFilePath The path to the current configuration. Default is empty string.
|
---|
| 2719 | * @param {string | undefined} providedMatchBasePath The type of the current configuration. Default is the directory of `filePath` or `cwd`.
|
---|
| 2720 | * @returns {ConfigArrayFactoryLoadingContext} The created context.
|
---|
| 2721 | */
|
---|
| 2722 | function createContext(
|
---|
| 2723 | { cwd, resolvePluginsRelativeTo },
|
---|
| 2724 | providedType,
|
---|
| 2725 | providedName,
|
---|
| 2726 | providedFilePath,
|
---|
| 2727 | providedMatchBasePath
|
---|
| 2728 | ) {
|
---|
| 2729 | const filePath = providedFilePath
|
---|
| 2730 | ? path__default["default"].resolve(cwd, providedFilePath)
|
---|
| 2731 | : "";
|
---|
| 2732 | const matchBasePath =
|
---|
| 2733 | (providedMatchBasePath && path__default["default"].resolve(cwd, providedMatchBasePath)) ||
|
---|
| 2734 | (filePath && path__default["default"].dirname(filePath)) ||
|
---|
| 2735 | cwd;
|
---|
| 2736 | const name =
|
---|
| 2737 | providedName ||
|
---|
| 2738 | (filePath && path__default["default"].relative(cwd, filePath)) ||
|
---|
| 2739 | "";
|
---|
| 2740 | const pluginBasePath =
|
---|
| 2741 | resolvePluginsRelativeTo ||
|
---|
| 2742 | (filePath && path__default["default"].dirname(filePath)) ||
|
---|
| 2743 | cwd;
|
---|
| 2744 | const type = providedType || "config";
|
---|
| 2745 |
|
---|
| 2746 | return { filePath, matchBasePath, name, pluginBasePath, type };
|
---|
| 2747 | }
|
---|
| 2748 |
|
---|
| 2749 | /**
|
---|
| 2750 | * Normalize a given plugin.
|
---|
| 2751 | * - Ensure the object to have four properties: configs, environments, processors, and rules.
|
---|
| 2752 | * - Ensure the object to not have other properties.
|
---|
| 2753 | * @param {Plugin} plugin The plugin to normalize.
|
---|
| 2754 | * @returns {Plugin} The normalized plugin.
|
---|
| 2755 | */
|
---|
| 2756 | function normalizePlugin(plugin) {
|
---|
| 2757 |
|
---|
| 2758 | // first check the cache
|
---|
| 2759 | let normalizedPlugin = normalizedPlugins.get(plugin);
|
---|
| 2760 |
|
---|
| 2761 | if (normalizedPlugin) {
|
---|
| 2762 | return normalizedPlugin;
|
---|
| 2763 | }
|
---|
| 2764 |
|
---|
| 2765 | normalizedPlugin = {
|
---|
| 2766 | configs: plugin.configs || {},
|
---|
| 2767 | environments: plugin.environments || {},
|
---|
| 2768 | processors: plugin.processors || {},
|
---|
| 2769 | rules: plugin.rules || {}
|
---|
| 2770 | };
|
---|
| 2771 |
|
---|
| 2772 | // save the reference for later
|
---|
| 2773 | normalizedPlugins.set(plugin, normalizedPlugin);
|
---|
| 2774 |
|
---|
| 2775 | return normalizedPlugin;
|
---|
| 2776 | }
|
---|
| 2777 |
|
---|
| 2778 | //------------------------------------------------------------------------------
|
---|
| 2779 | // Public Interface
|
---|
| 2780 | //------------------------------------------------------------------------------
|
---|
| 2781 |
|
---|
| 2782 | /**
|
---|
| 2783 | * The factory of `ConfigArray` objects.
|
---|
| 2784 | */
|
---|
| 2785 | class ConfigArrayFactory {
|
---|
| 2786 |
|
---|
| 2787 | /**
|
---|
| 2788 | * Initialize this instance.
|
---|
| 2789 | * @param {ConfigArrayFactoryOptions} [options] The map for additional plugins.
|
---|
| 2790 | */
|
---|
| 2791 | constructor({
|
---|
| 2792 | additionalPluginPool = new Map(),
|
---|
| 2793 | cwd = process.cwd(),
|
---|
| 2794 | resolvePluginsRelativeTo,
|
---|
| 2795 | builtInRules,
|
---|
| 2796 | resolver = ModuleResolver,
|
---|
| 2797 | eslintAllPath,
|
---|
| 2798 | getEslintAllConfig,
|
---|
| 2799 | eslintRecommendedPath,
|
---|
| 2800 | getEslintRecommendedConfig
|
---|
| 2801 | } = {}) {
|
---|
| 2802 | internalSlotsMap$1.set(this, {
|
---|
| 2803 | additionalPluginPool,
|
---|
| 2804 | cwd,
|
---|
| 2805 | resolvePluginsRelativeTo:
|
---|
| 2806 | resolvePluginsRelativeTo &&
|
---|
| 2807 | path__default["default"].resolve(cwd, resolvePluginsRelativeTo),
|
---|
| 2808 | builtInRules,
|
---|
| 2809 | resolver,
|
---|
| 2810 | eslintAllPath,
|
---|
| 2811 | getEslintAllConfig,
|
---|
| 2812 | eslintRecommendedPath,
|
---|
| 2813 | getEslintRecommendedConfig
|
---|
| 2814 | });
|
---|
| 2815 | }
|
---|
| 2816 |
|
---|
| 2817 | /**
|
---|
| 2818 | * Create `ConfigArray` instance from a config data.
|
---|
| 2819 | * @param {ConfigData|null} configData The config data to create.
|
---|
| 2820 | * @param {Object} [options] The options.
|
---|
| 2821 | * @param {string} [options.basePath] The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`.
|
---|
| 2822 | * @param {string} [options.filePath] The path to this config data.
|
---|
| 2823 | * @param {string} [options.name] The config name.
|
---|
| 2824 | * @returns {ConfigArray} Loaded config.
|
---|
| 2825 | */
|
---|
| 2826 | create(configData, { basePath, filePath, name } = {}) {
|
---|
| 2827 | if (!configData) {
|
---|
| 2828 | return new ConfigArray();
|
---|
| 2829 | }
|
---|
| 2830 |
|
---|
| 2831 | const slots = internalSlotsMap$1.get(this);
|
---|
| 2832 | const ctx = createContext(slots, "config", name, filePath, basePath);
|
---|
| 2833 | const elements = this._normalizeConfigData(configData, ctx);
|
---|
| 2834 |
|
---|
| 2835 | return new ConfigArray(...elements);
|
---|
| 2836 | }
|
---|
| 2837 |
|
---|
| 2838 | /**
|
---|
| 2839 | * Load a config file.
|
---|
| 2840 | * @param {string} filePath The path to a config file.
|
---|
| 2841 | * @param {Object} [options] The options.
|
---|
| 2842 | * @param {string} [options.basePath] The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`.
|
---|
| 2843 | * @param {string} [options.name] The config name.
|
---|
| 2844 | * @returns {ConfigArray} Loaded config.
|
---|
| 2845 | */
|
---|
| 2846 | loadFile(filePath, { basePath, name } = {}) {
|
---|
| 2847 | const slots = internalSlotsMap$1.get(this);
|
---|
| 2848 | const ctx = createContext(slots, "config", name, filePath, basePath);
|
---|
| 2849 |
|
---|
| 2850 | return new ConfigArray(...this._loadConfigData(ctx));
|
---|
| 2851 | }
|
---|
| 2852 |
|
---|
| 2853 | /**
|
---|
| 2854 | * Load the config file on a given directory if exists.
|
---|
| 2855 | * @param {string} directoryPath The path to a directory.
|
---|
| 2856 | * @param {Object} [options] The options.
|
---|
| 2857 | * @param {string} [options.basePath] The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`.
|
---|
| 2858 | * @param {string} [options.name] The config name.
|
---|
| 2859 | * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist.
|
---|
| 2860 | */
|
---|
| 2861 | loadInDirectory(directoryPath, { basePath, name } = {}) {
|
---|
| 2862 | const slots = internalSlotsMap$1.get(this);
|
---|
| 2863 |
|
---|
| 2864 | for (const filename of configFilenames) {
|
---|
| 2865 | const ctx = createContext(
|
---|
| 2866 | slots,
|
---|
| 2867 | "config",
|
---|
| 2868 | name,
|
---|
| 2869 | path__default["default"].join(directoryPath, filename),
|
---|
| 2870 | basePath
|
---|
| 2871 | );
|
---|
| 2872 |
|
---|
| 2873 | if (fs__default["default"].existsSync(ctx.filePath) && fs__default["default"].statSync(ctx.filePath).isFile()) {
|
---|
| 2874 | let configData;
|
---|
| 2875 |
|
---|
| 2876 | try {
|
---|
| 2877 | configData = loadConfigFile(ctx.filePath);
|
---|
| 2878 | } catch (error) {
|
---|
| 2879 | if (!error || error.code !== "ESLINT_CONFIG_FIELD_NOT_FOUND") {
|
---|
| 2880 | throw error;
|
---|
| 2881 | }
|
---|
| 2882 | }
|
---|
| 2883 |
|
---|
| 2884 | if (configData) {
|
---|
| 2885 | debug$2(`Config file found: ${ctx.filePath}`);
|
---|
| 2886 | return new ConfigArray(
|
---|
| 2887 | ...this._normalizeConfigData(configData, ctx)
|
---|
| 2888 | );
|
---|
| 2889 | }
|
---|
| 2890 | }
|
---|
| 2891 | }
|
---|
| 2892 |
|
---|
| 2893 | debug$2(`Config file not found on ${directoryPath}`);
|
---|
| 2894 | return new ConfigArray();
|
---|
| 2895 | }
|
---|
| 2896 |
|
---|
| 2897 | /**
|
---|
| 2898 | * Check if a config file on a given directory exists or not.
|
---|
| 2899 | * @param {string} directoryPath The path to a directory.
|
---|
| 2900 | * @returns {string | null} The path to the found config file. If not found then null.
|
---|
| 2901 | */
|
---|
| 2902 | static getPathToConfigFileInDirectory(directoryPath) {
|
---|
| 2903 | for (const filename of configFilenames) {
|
---|
| 2904 | const filePath = path__default["default"].join(directoryPath, filename);
|
---|
| 2905 |
|
---|
| 2906 | if (fs__default["default"].existsSync(filePath)) {
|
---|
| 2907 | if (filename === "package.json") {
|
---|
| 2908 | try {
|
---|
| 2909 | loadPackageJSONConfigFile(filePath);
|
---|
| 2910 | return filePath;
|
---|
| 2911 | } catch { /* ignore */ }
|
---|
| 2912 | } else {
|
---|
| 2913 | return filePath;
|
---|
| 2914 | }
|
---|
| 2915 | }
|
---|
| 2916 | }
|
---|
| 2917 | return null;
|
---|
| 2918 | }
|
---|
| 2919 |
|
---|
| 2920 | /**
|
---|
| 2921 | * Load `.eslintignore` file.
|
---|
| 2922 | * @param {string} filePath The path to a `.eslintignore` file to load.
|
---|
| 2923 | * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist.
|
---|
| 2924 | */
|
---|
| 2925 | loadESLintIgnore(filePath) {
|
---|
| 2926 | const slots = internalSlotsMap$1.get(this);
|
---|
| 2927 | const ctx = createContext(
|
---|
| 2928 | slots,
|
---|
| 2929 | "ignore",
|
---|
| 2930 | void 0,
|
---|
| 2931 | filePath,
|
---|
| 2932 | slots.cwd
|
---|
| 2933 | );
|
---|
| 2934 | const ignorePatterns = loadESLintIgnoreFile(ctx.filePath);
|
---|
| 2935 |
|
---|
| 2936 | return new ConfigArray(
|
---|
| 2937 | ...this._normalizeESLintIgnoreData(ignorePatterns, ctx)
|
---|
| 2938 | );
|
---|
| 2939 | }
|
---|
| 2940 |
|
---|
| 2941 | /**
|
---|
| 2942 | * Load `.eslintignore` file in the current working directory.
|
---|
| 2943 | * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist.
|
---|
| 2944 | */
|
---|
| 2945 | loadDefaultESLintIgnore() {
|
---|
| 2946 | const slots = internalSlotsMap$1.get(this);
|
---|
| 2947 | const eslintIgnorePath = path__default["default"].resolve(slots.cwd, ".eslintignore");
|
---|
| 2948 | const packageJsonPath = path__default["default"].resolve(slots.cwd, "package.json");
|
---|
| 2949 |
|
---|
| 2950 | if (fs__default["default"].existsSync(eslintIgnorePath)) {
|
---|
| 2951 | return this.loadESLintIgnore(eslintIgnorePath);
|
---|
| 2952 | }
|
---|
| 2953 | if (fs__default["default"].existsSync(packageJsonPath)) {
|
---|
| 2954 | const data = loadJSONConfigFile(packageJsonPath);
|
---|
| 2955 |
|
---|
| 2956 | if (Object.hasOwnProperty.call(data, "eslintIgnore")) {
|
---|
| 2957 | if (!Array.isArray(data.eslintIgnore)) {
|
---|
| 2958 | throw new Error("Package.json eslintIgnore property requires an array of paths");
|
---|
| 2959 | }
|
---|
| 2960 | const ctx = createContext(
|
---|
| 2961 | slots,
|
---|
| 2962 | "ignore",
|
---|
| 2963 | "eslintIgnore in package.json",
|
---|
| 2964 | packageJsonPath,
|
---|
| 2965 | slots.cwd
|
---|
| 2966 | );
|
---|
| 2967 |
|
---|
| 2968 | return new ConfigArray(
|
---|
| 2969 | ...this._normalizeESLintIgnoreData(data.eslintIgnore, ctx)
|
---|
| 2970 | );
|
---|
| 2971 | }
|
---|
| 2972 | }
|
---|
| 2973 |
|
---|
| 2974 | return new ConfigArray();
|
---|
| 2975 | }
|
---|
| 2976 |
|
---|
| 2977 | /**
|
---|
| 2978 | * Load a given config file.
|
---|
| 2979 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 2980 | * @returns {IterableIterator<ConfigArrayElement>} Loaded config.
|
---|
| 2981 | * @private
|
---|
| 2982 | */
|
---|
| 2983 | _loadConfigData(ctx) {
|
---|
| 2984 | return this._normalizeConfigData(loadConfigFile(ctx.filePath), ctx);
|
---|
| 2985 | }
|
---|
| 2986 |
|
---|
| 2987 | /**
|
---|
| 2988 | * Normalize a given `.eslintignore` data to config array elements.
|
---|
| 2989 | * @param {string[]} ignorePatterns The patterns to ignore files.
|
---|
| 2990 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 2991 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 2992 | * @private
|
---|
| 2993 | */
|
---|
| 2994 | *_normalizeESLintIgnoreData(ignorePatterns, ctx) {
|
---|
| 2995 | const elements = this._normalizeObjectConfigData(
|
---|
| 2996 | { ignorePatterns },
|
---|
| 2997 | ctx
|
---|
| 2998 | );
|
---|
| 2999 |
|
---|
| 3000 | // Set `ignorePattern.loose` flag for backward compatibility.
|
---|
| 3001 | for (const element of elements) {
|
---|
| 3002 | if (element.ignorePattern) {
|
---|
| 3003 | element.ignorePattern.loose = true;
|
---|
| 3004 | }
|
---|
| 3005 | yield element;
|
---|
| 3006 | }
|
---|
| 3007 | }
|
---|
| 3008 |
|
---|
| 3009 | /**
|
---|
| 3010 | * Normalize a given config to an array.
|
---|
| 3011 | * @param {ConfigData} configData The config data to normalize.
|
---|
| 3012 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3013 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 3014 | * @private
|
---|
| 3015 | */
|
---|
| 3016 | _normalizeConfigData(configData, ctx) {
|
---|
| 3017 | const validator = new ConfigValidator();
|
---|
| 3018 |
|
---|
| 3019 | validator.validateConfigSchema(configData, ctx.name || ctx.filePath);
|
---|
| 3020 | return this._normalizeObjectConfigData(configData, ctx);
|
---|
| 3021 | }
|
---|
| 3022 |
|
---|
| 3023 | /**
|
---|
| 3024 | * Normalize a given config to an array.
|
---|
| 3025 | * @param {ConfigData|OverrideConfigData} configData The config data to normalize.
|
---|
| 3026 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3027 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 3028 | * @private
|
---|
| 3029 | */
|
---|
| 3030 | *_normalizeObjectConfigData(configData, ctx) {
|
---|
| 3031 | const { files, excludedFiles, ...configBody } = configData;
|
---|
| 3032 | const criteria = OverrideTester.create(
|
---|
| 3033 | files,
|
---|
| 3034 | excludedFiles,
|
---|
| 3035 | ctx.matchBasePath
|
---|
| 3036 | );
|
---|
| 3037 | const elements = this._normalizeObjectConfigDataBody(configBody, ctx);
|
---|
| 3038 |
|
---|
| 3039 | // Apply the criteria to every element.
|
---|
| 3040 | for (const element of elements) {
|
---|
| 3041 |
|
---|
| 3042 | /*
|
---|
| 3043 | * Merge the criteria.
|
---|
| 3044 | * This is for the `overrides` entries that came from the
|
---|
| 3045 | * configurations of `overrides[].extends`.
|
---|
| 3046 | */
|
---|
| 3047 | element.criteria = OverrideTester.and(criteria, element.criteria);
|
---|
| 3048 |
|
---|
| 3049 | /*
|
---|
| 3050 | * Remove `root` property to ignore `root` settings which came from
|
---|
| 3051 | * `extends` in `overrides`.
|
---|
| 3052 | */
|
---|
| 3053 | if (element.criteria) {
|
---|
| 3054 | element.root = void 0;
|
---|
| 3055 | }
|
---|
| 3056 |
|
---|
| 3057 | yield element;
|
---|
| 3058 | }
|
---|
| 3059 | }
|
---|
| 3060 |
|
---|
| 3061 | /**
|
---|
| 3062 | * Normalize a given config to an array.
|
---|
| 3063 | * @param {ConfigData} configData The config data to normalize.
|
---|
| 3064 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3065 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 3066 | * @private
|
---|
| 3067 | */
|
---|
| 3068 | *_normalizeObjectConfigDataBody(
|
---|
| 3069 | {
|
---|
| 3070 | env,
|
---|
| 3071 | extends: extend,
|
---|
| 3072 | globals,
|
---|
| 3073 | ignorePatterns,
|
---|
| 3074 | noInlineConfig,
|
---|
| 3075 | parser: parserName,
|
---|
| 3076 | parserOptions,
|
---|
| 3077 | plugins: pluginList,
|
---|
| 3078 | processor,
|
---|
| 3079 | reportUnusedDisableDirectives,
|
---|
| 3080 | root,
|
---|
| 3081 | rules,
|
---|
| 3082 | settings,
|
---|
| 3083 | overrides: overrideList = []
|
---|
| 3084 | },
|
---|
| 3085 | ctx
|
---|
| 3086 | ) {
|
---|
| 3087 | const extendList = Array.isArray(extend) ? extend : [extend];
|
---|
| 3088 | const ignorePattern = ignorePatterns && new IgnorePattern(
|
---|
| 3089 | Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns],
|
---|
| 3090 | ctx.matchBasePath
|
---|
| 3091 | );
|
---|
| 3092 |
|
---|
| 3093 | // Flatten `extends`.
|
---|
| 3094 | for (const extendName of extendList.filter(Boolean)) {
|
---|
| 3095 | yield* this._loadExtends(extendName, ctx);
|
---|
| 3096 | }
|
---|
| 3097 |
|
---|
| 3098 | // Load parser & plugins.
|
---|
| 3099 | const parser = parserName && this._loadParser(parserName, ctx);
|
---|
| 3100 | const plugins = pluginList && this._loadPlugins(pluginList, ctx);
|
---|
| 3101 |
|
---|
| 3102 | // Yield pseudo config data for file extension processors.
|
---|
| 3103 | if (plugins) {
|
---|
| 3104 | yield* this._takeFileExtensionProcessors(plugins, ctx);
|
---|
| 3105 | }
|
---|
| 3106 |
|
---|
| 3107 | // Yield the config data except `extends` and `overrides`.
|
---|
| 3108 | yield {
|
---|
| 3109 |
|
---|
| 3110 | // Debug information.
|
---|
| 3111 | type: ctx.type,
|
---|
| 3112 | name: ctx.name,
|
---|
| 3113 | filePath: ctx.filePath,
|
---|
| 3114 |
|
---|
| 3115 | // Config data.
|
---|
| 3116 | criteria: null,
|
---|
| 3117 | env,
|
---|
| 3118 | globals,
|
---|
| 3119 | ignorePattern,
|
---|
| 3120 | noInlineConfig,
|
---|
| 3121 | parser,
|
---|
| 3122 | parserOptions,
|
---|
| 3123 | plugins,
|
---|
| 3124 | processor,
|
---|
| 3125 | reportUnusedDisableDirectives,
|
---|
| 3126 | root,
|
---|
| 3127 | rules,
|
---|
| 3128 | settings
|
---|
| 3129 | };
|
---|
| 3130 |
|
---|
| 3131 | // Flatten `overries`.
|
---|
| 3132 | for (let i = 0; i < overrideList.length; ++i) {
|
---|
| 3133 | yield* this._normalizeObjectConfigData(
|
---|
| 3134 | overrideList[i],
|
---|
| 3135 | { ...ctx, name: `${ctx.name}#overrides[${i}]` }
|
---|
| 3136 | );
|
---|
| 3137 | }
|
---|
| 3138 | }
|
---|
| 3139 |
|
---|
| 3140 | /**
|
---|
| 3141 | * Load configs of an element in `extends`.
|
---|
| 3142 | * @param {string} extendName The name of a base config.
|
---|
| 3143 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3144 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 3145 | * @private
|
---|
| 3146 | */
|
---|
| 3147 | _loadExtends(extendName, ctx) {
|
---|
| 3148 | debug$2("Loading {extends:%j} relative to %s", extendName, ctx.filePath);
|
---|
| 3149 | try {
|
---|
| 3150 | if (extendName.startsWith("eslint:")) {
|
---|
| 3151 | return this._loadExtendedBuiltInConfig(extendName, ctx);
|
---|
| 3152 | }
|
---|
| 3153 | if (extendName.startsWith("plugin:")) {
|
---|
| 3154 | return this._loadExtendedPluginConfig(extendName, ctx);
|
---|
| 3155 | }
|
---|
| 3156 | return this._loadExtendedShareableConfig(extendName, ctx);
|
---|
| 3157 | } catch (error) {
|
---|
| 3158 | error.message += `\nReferenced from: ${ctx.filePath || ctx.name}`;
|
---|
| 3159 | throw error;
|
---|
| 3160 | }
|
---|
| 3161 | }
|
---|
| 3162 |
|
---|
| 3163 | /**
|
---|
| 3164 | * Load configs of an element in `extends`.
|
---|
| 3165 | * @param {string} extendName The name of a base config.
|
---|
| 3166 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3167 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 3168 | * @private
|
---|
| 3169 | */
|
---|
| 3170 | _loadExtendedBuiltInConfig(extendName, ctx) {
|
---|
| 3171 | const {
|
---|
| 3172 | eslintAllPath,
|
---|
| 3173 | getEslintAllConfig,
|
---|
| 3174 | eslintRecommendedPath,
|
---|
| 3175 | getEslintRecommendedConfig
|
---|
| 3176 | } = internalSlotsMap$1.get(this);
|
---|
| 3177 |
|
---|
| 3178 | if (extendName === "eslint:recommended") {
|
---|
| 3179 | const name = `${ctx.name} » ${extendName}`;
|
---|
| 3180 |
|
---|
| 3181 | if (getEslintRecommendedConfig) {
|
---|
| 3182 | if (typeof getEslintRecommendedConfig !== "function") {
|
---|
| 3183 | throw new Error(`getEslintRecommendedConfig must be a function instead of '${getEslintRecommendedConfig}'`);
|
---|
| 3184 | }
|
---|
| 3185 | return this._normalizeConfigData(getEslintRecommendedConfig(), { ...ctx, name, filePath: "" });
|
---|
| 3186 | }
|
---|
| 3187 | return this._loadConfigData({
|
---|
| 3188 | ...ctx,
|
---|
| 3189 | name,
|
---|
| 3190 | filePath: eslintRecommendedPath
|
---|
| 3191 | });
|
---|
| 3192 | }
|
---|
| 3193 | if (extendName === "eslint:all") {
|
---|
| 3194 | const name = `${ctx.name} » ${extendName}`;
|
---|
| 3195 |
|
---|
| 3196 | if (getEslintAllConfig) {
|
---|
| 3197 | if (typeof getEslintAllConfig !== "function") {
|
---|
| 3198 | throw new Error(`getEslintAllConfig must be a function instead of '${getEslintAllConfig}'`);
|
---|
| 3199 | }
|
---|
| 3200 | return this._normalizeConfigData(getEslintAllConfig(), { ...ctx, name, filePath: "" });
|
---|
| 3201 | }
|
---|
| 3202 | return this._loadConfigData({
|
---|
| 3203 | ...ctx,
|
---|
| 3204 | name,
|
---|
| 3205 | filePath: eslintAllPath
|
---|
| 3206 | });
|
---|
| 3207 | }
|
---|
| 3208 |
|
---|
| 3209 | throw configInvalidError(extendName, ctx.name, "extend-config-missing");
|
---|
| 3210 | }
|
---|
| 3211 |
|
---|
| 3212 | /**
|
---|
| 3213 | * Load configs of an element in `extends`.
|
---|
| 3214 | * @param {string} extendName The name of a base config.
|
---|
| 3215 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3216 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 3217 | * @private
|
---|
| 3218 | */
|
---|
| 3219 | _loadExtendedPluginConfig(extendName, ctx) {
|
---|
| 3220 | const slashIndex = extendName.lastIndexOf("/");
|
---|
| 3221 |
|
---|
| 3222 | if (slashIndex === -1) {
|
---|
| 3223 | throw configInvalidError(extendName, ctx.filePath, "plugin-invalid");
|
---|
| 3224 | }
|
---|
| 3225 |
|
---|
| 3226 | const pluginName = extendName.slice("plugin:".length, slashIndex);
|
---|
| 3227 | const configName = extendName.slice(slashIndex + 1);
|
---|
| 3228 |
|
---|
| 3229 | if (isFilePath(pluginName)) {
|
---|
| 3230 | throw new Error("'extends' cannot use a file path for plugins.");
|
---|
| 3231 | }
|
---|
| 3232 |
|
---|
| 3233 | const plugin = this._loadPlugin(pluginName, ctx);
|
---|
| 3234 | const configData =
|
---|
| 3235 | plugin.definition &&
|
---|
| 3236 | plugin.definition.configs[configName];
|
---|
| 3237 |
|
---|
| 3238 | if (configData) {
|
---|
| 3239 | return this._normalizeConfigData(configData, {
|
---|
| 3240 | ...ctx,
|
---|
| 3241 | filePath: plugin.filePath || ctx.filePath,
|
---|
| 3242 | name: `${ctx.name} » plugin:${plugin.id}/${configName}`
|
---|
| 3243 | });
|
---|
| 3244 | }
|
---|
| 3245 |
|
---|
| 3246 | throw plugin.error || configInvalidError(extendName, ctx.filePath, "extend-config-missing");
|
---|
| 3247 | }
|
---|
| 3248 |
|
---|
| 3249 | /**
|
---|
| 3250 | * Load configs of an element in `extends`.
|
---|
| 3251 | * @param {string} extendName The name of a base config.
|
---|
| 3252 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3253 | * @returns {IterableIterator<ConfigArrayElement>} The normalized config.
|
---|
| 3254 | * @private
|
---|
| 3255 | */
|
---|
| 3256 | _loadExtendedShareableConfig(extendName, ctx) {
|
---|
| 3257 | const { cwd, resolver } = internalSlotsMap$1.get(this);
|
---|
| 3258 | const relativeTo = ctx.filePath || path__default["default"].join(cwd, "__placeholder__.js");
|
---|
| 3259 | let request;
|
---|
| 3260 |
|
---|
| 3261 | if (isFilePath(extendName)) {
|
---|
| 3262 | request = extendName;
|
---|
| 3263 | } else if (extendName.startsWith(".")) {
|
---|
| 3264 | request = `./${extendName}`; // For backward compatibility. A ton of tests depended on this behavior.
|
---|
| 3265 | } else {
|
---|
| 3266 | request = normalizePackageName(
|
---|
| 3267 | extendName,
|
---|
| 3268 | "eslint-config"
|
---|
| 3269 | );
|
---|
| 3270 | }
|
---|
| 3271 |
|
---|
| 3272 | let filePath;
|
---|
| 3273 |
|
---|
| 3274 | try {
|
---|
| 3275 | filePath = resolver.resolve(request, relativeTo);
|
---|
| 3276 | } catch (error) {
|
---|
| 3277 | /* istanbul ignore else */
|
---|
| 3278 | if (error && error.code === "MODULE_NOT_FOUND") {
|
---|
| 3279 | throw configInvalidError(extendName, ctx.filePath, "extend-config-missing");
|
---|
| 3280 | }
|
---|
| 3281 | throw error;
|
---|
| 3282 | }
|
---|
| 3283 |
|
---|
| 3284 | writeDebugLogForLoading(request, relativeTo, filePath);
|
---|
| 3285 | return this._loadConfigData({
|
---|
| 3286 | ...ctx,
|
---|
| 3287 | filePath,
|
---|
| 3288 | name: `${ctx.name} » ${request}`
|
---|
| 3289 | });
|
---|
| 3290 | }
|
---|
| 3291 |
|
---|
| 3292 | /**
|
---|
| 3293 | * Load given plugins.
|
---|
| 3294 | * @param {string[]} names The plugin names to load.
|
---|
| 3295 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3296 | * @returns {Record<string,DependentPlugin>} The loaded parser.
|
---|
| 3297 | * @private
|
---|
| 3298 | */
|
---|
| 3299 | _loadPlugins(names, ctx) {
|
---|
| 3300 | return names.reduce((map, name) => {
|
---|
| 3301 | if (isFilePath(name)) {
|
---|
| 3302 | throw new Error("Plugins array cannot includes file paths.");
|
---|
| 3303 | }
|
---|
| 3304 | const plugin = this._loadPlugin(name, ctx);
|
---|
| 3305 |
|
---|
| 3306 | map[plugin.id] = plugin;
|
---|
| 3307 |
|
---|
| 3308 | return map;
|
---|
| 3309 | }, {});
|
---|
| 3310 | }
|
---|
| 3311 |
|
---|
| 3312 | /**
|
---|
| 3313 | * Load a given parser.
|
---|
| 3314 | * @param {string} nameOrPath The package name or the path to a parser file.
|
---|
| 3315 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3316 | * @returns {DependentParser} The loaded parser.
|
---|
| 3317 | */
|
---|
| 3318 | _loadParser(nameOrPath, ctx) {
|
---|
| 3319 | debug$2("Loading parser %j from %s", nameOrPath, ctx.filePath);
|
---|
| 3320 |
|
---|
| 3321 | const { cwd, resolver } = internalSlotsMap$1.get(this);
|
---|
| 3322 | const relativeTo = ctx.filePath || path__default["default"].join(cwd, "__placeholder__.js");
|
---|
| 3323 |
|
---|
| 3324 | try {
|
---|
| 3325 | const filePath = resolver.resolve(nameOrPath, relativeTo);
|
---|
| 3326 |
|
---|
| 3327 | writeDebugLogForLoading(nameOrPath, relativeTo, filePath);
|
---|
| 3328 |
|
---|
| 3329 | return new ConfigDependency({
|
---|
| 3330 | definition: require$1(filePath),
|
---|
| 3331 | filePath,
|
---|
| 3332 | id: nameOrPath,
|
---|
| 3333 | importerName: ctx.name,
|
---|
| 3334 | importerPath: ctx.filePath
|
---|
| 3335 | });
|
---|
| 3336 | } catch (error) {
|
---|
| 3337 |
|
---|
| 3338 | // If the parser name is "espree", load the espree of ESLint.
|
---|
| 3339 | if (nameOrPath === "espree") {
|
---|
| 3340 | debug$2("Fallback espree.");
|
---|
| 3341 | return new ConfigDependency({
|
---|
| 3342 | definition: require$1("espree"),
|
---|
| 3343 | filePath: require$1.resolve("espree"),
|
---|
| 3344 | id: nameOrPath,
|
---|
| 3345 | importerName: ctx.name,
|
---|
| 3346 | importerPath: ctx.filePath
|
---|
| 3347 | });
|
---|
| 3348 | }
|
---|
| 3349 |
|
---|
| 3350 | debug$2("Failed to load parser '%s' declared in '%s'.", nameOrPath, ctx.name);
|
---|
| 3351 | error.message = `Failed to load parser '${nameOrPath}' declared in '${ctx.name}': ${error.message}`;
|
---|
| 3352 |
|
---|
| 3353 | return new ConfigDependency({
|
---|
| 3354 | error,
|
---|
| 3355 | id: nameOrPath,
|
---|
| 3356 | importerName: ctx.name,
|
---|
| 3357 | importerPath: ctx.filePath
|
---|
| 3358 | });
|
---|
| 3359 | }
|
---|
| 3360 | }
|
---|
| 3361 |
|
---|
| 3362 | /**
|
---|
| 3363 | * Load a given plugin.
|
---|
| 3364 | * @param {string} name The plugin name to load.
|
---|
| 3365 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3366 | * @returns {DependentPlugin} The loaded plugin.
|
---|
| 3367 | * @private
|
---|
| 3368 | */
|
---|
| 3369 | _loadPlugin(name, ctx) {
|
---|
| 3370 | debug$2("Loading plugin %j from %s", name, ctx.filePath);
|
---|
| 3371 |
|
---|
| 3372 | const { additionalPluginPool, resolver } = internalSlotsMap$1.get(this);
|
---|
| 3373 | const request = normalizePackageName(name, "eslint-plugin");
|
---|
| 3374 | const id = getShorthandName(request, "eslint-plugin");
|
---|
| 3375 | const relativeTo = path__default["default"].join(ctx.pluginBasePath, "__placeholder__.js");
|
---|
| 3376 |
|
---|
| 3377 | if (name.match(/\s+/u)) {
|
---|
| 3378 | const error = Object.assign(
|
---|
| 3379 | new Error(`Whitespace found in plugin name '${name}'`),
|
---|
| 3380 | {
|
---|
| 3381 | messageTemplate: "whitespace-found",
|
---|
| 3382 | messageData: { pluginName: request }
|
---|
| 3383 | }
|
---|
| 3384 | );
|
---|
| 3385 |
|
---|
| 3386 | return new ConfigDependency({
|
---|
| 3387 | error,
|
---|
| 3388 | id,
|
---|
| 3389 | importerName: ctx.name,
|
---|
| 3390 | importerPath: ctx.filePath
|
---|
| 3391 | });
|
---|
| 3392 | }
|
---|
| 3393 |
|
---|
| 3394 | // Check for additional pool.
|
---|
| 3395 | const plugin =
|
---|
| 3396 | additionalPluginPool.get(request) ||
|
---|
| 3397 | additionalPluginPool.get(id);
|
---|
| 3398 |
|
---|
| 3399 | if (plugin) {
|
---|
| 3400 | return new ConfigDependency({
|
---|
| 3401 | definition: normalizePlugin(plugin),
|
---|
| 3402 | original: plugin,
|
---|
| 3403 | filePath: "", // It's unknown where the plugin came from.
|
---|
| 3404 | id,
|
---|
| 3405 | importerName: ctx.name,
|
---|
| 3406 | importerPath: ctx.filePath
|
---|
| 3407 | });
|
---|
| 3408 | }
|
---|
| 3409 |
|
---|
| 3410 | let filePath;
|
---|
| 3411 | let error;
|
---|
| 3412 |
|
---|
| 3413 | try {
|
---|
| 3414 | filePath = resolver.resolve(request, relativeTo);
|
---|
| 3415 | } catch (resolveError) {
|
---|
| 3416 | error = resolveError;
|
---|
| 3417 | /* istanbul ignore else */
|
---|
| 3418 | if (error && error.code === "MODULE_NOT_FOUND") {
|
---|
| 3419 | error.messageTemplate = "plugin-missing";
|
---|
| 3420 | error.messageData = {
|
---|
| 3421 | pluginName: request,
|
---|
| 3422 | resolvePluginsRelativeTo: ctx.pluginBasePath,
|
---|
| 3423 | importerName: ctx.name
|
---|
| 3424 | };
|
---|
| 3425 | }
|
---|
| 3426 | }
|
---|
| 3427 |
|
---|
| 3428 | if (filePath) {
|
---|
| 3429 | try {
|
---|
| 3430 | writeDebugLogForLoading(request, relativeTo, filePath);
|
---|
| 3431 |
|
---|
| 3432 | const startTime = Date.now();
|
---|
| 3433 | const pluginDefinition = require$1(filePath);
|
---|
| 3434 |
|
---|
| 3435 | debug$2(`Plugin ${filePath} loaded in: ${Date.now() - startTime}ms`);
|
---|
| 3436 |
|
---|
| 3437 | return new ConfigDependency({
|
---|
| 3438 | definition: normalizePlugin(pluginDefinition),
|
---|
| 3439 | original: pluginDefinition,
|
---|
| 3440 | filePath,
|
---|
| 3441 | id,
|
---|
| 3442 | importerName: ctx.name,
|
---|
| 3443 | importerPath: ctx.filePath
|
---|
| 3444 | });
|
---|
| 3445 | } catch (loadError) {
|
---|
| 3446 | error = loadError;
|
---|
| 3447 | }
|
---|
| 3448 | }
|
---|
| 3449 |
|
---|
| 3450 | debug$2("Failed to load plugin '%s' declared in '%s'.", name, ctx.name);
|
---|
| 3451 | error.message = `Failed to load plugin '${name}' declared in '${ctx.name}': ${error.message}`;
|
---|
| 3452 | return new ConfigDependency({
|
---|
| 3453 | error,
|
---|
| 3454 | id,
|
---|
| 3455 | importerName: ctx.name,
|
---|
| 3456 | importerPath: ctx.filePath
|
---|
| 3457 | });
|
---|
| 3458 | }
|
---|
| 3459 |
|
---|
| 3460 | /**
|
---|
| 3461 | * Take file expression processors as config array elements.
|
---|
| 3462 | * @param {Record<string,DependentPlugin>} plugins The plugin definitions.
|
---|
| 3463 | * @param {ConfigArrayFactoryLoadingContext} ctx The loading context.
|
---|
| 3464 | * @returns {IterableIterator<ConfigArrayElement>} The config array elements of file expression processors.
|
---|
| 3465 | * @private
|
---|
| 3466 | */
|
---|
| 3467 | *_takeFileExtensionProcessors(plugins, ctx) {
|
---|
| 3468 | for (const pluginId of Object.keys(plugins)) {
|
---|
| 3469 | const processors =
|
---|
| 3470 | plugins[pluginId] &&
|
---|
| 3471 | plugins[pluginId].definition &&
|
---|
| 3472 | plugins[pluginId].definition.processors;
|
---|
| 3473 |
|
---|
| 3474 | if (!processors) {
|
---|
| 3475 | continue;
|
---|
| 3476 | }
|
---|
| 3477 |
|
---|
| 3478 | for (const processorId of Object.keys(processors)) {
|
---|
| 3479 | if (processorId.startsWith(".")) {
|
---|
| 3480 | yield* this._normalizeObjectConfigData(
|
---|
| 3481 | {
|
---|
| 3482 | files: [`*${processorId}`],
|
---|
| 3483 | processor: `${pluginId}/${processorId}`
|
---|
| 3484 | },
|
---|
| 3485 | {
|
---|
| 3486 | ...ctx,
|
---|
| 3487 | type: "implicit-processor",
|
---|
| 3488 | name: `${ctx.name}#processors["${pluginId}/${processorId}"]`
|
---|
| 3489 | }
|
---|
| 3490 | );
|
---|
| 3491 | }
|
---|
| 3492 | }
|
---|
| 3493 | }
|
---|
| 3494 | }
|
---|
| 3495 | }
|
---|
| 3496 |
|
---|
| 3497 | /**
|
---|
| 3498 | * @fileoverview `CascadingConfigArrayFactory` class.
|
---|
| 3499 | *
|
---|
| 3500 | * `CascadingConfigArrayFactory` class has a responsibility:
|
---|
| 3501 | *
|
---|
| 3502 | * 1. Handles cascading of config files.
|
---|
| 3503 | *
|
---|
| 3504 | * It provides two methods:
|
---|
| 3505 | *
|
---|
| 3506 | * - `getConfigArrayForFile(filePath)`
|
---|
| 3507 | * Get the corresponded configuration of a given file. This method doesn't
|
---|
| 3508 | * throw even if the given file didn't exist.
|
---|
| 3509 | * - `clearCache()`
|
---|
| 3510 | * Clear the internal cache. You have to call this method when
|
---|
| 3511 | * `additionalPluginPool` was updated if `baseConfig` or `cliConfig` depends
|
---|
| 3512 | * on the additional plugins. (`CLIEngine#addPlugin()` method calls this.)
|
---|
| 3513 | *
|
---|
| 3514 | * @author Toru Nagashima <https://github.com/mysticatea>
|
---|
| 3515 | */
|
---|
| 3516 |
|
---|
| 3517 | const debug$1 = debugOrig__default["default"]("eslintrc:cascading-config-array-factory");
|
---|
| 3518 |
|
---|
| 3519 | //------------------------------------------------------------------------------
|
---|
| 3520 | // Helpers
|
---|
| 3521 | //------------------------------------------------------------------------------
|
---|
| 3522 |
|
---|
| 3523 | // Define types for VSCode IntelliSense.
|
---|
| 3524 | /** @typedef {import("./shared/types").ConfigData} ConfigData */
|
---|
| 3525 | /** @typedef {import("./shared/types").Parser} Parser */
|
---|
| 3526 | /** @typedef {import("./shared/types").Plugin} Plugin */
|
---|
| 3527 | /** @typedef {import("./shared/types").Rule} Rule */
|
---|
| 3528 | /** @typedef {ReturnType<ConfigArrayFactory["create"]>} ConfigArray */
|
---|
| 3529 |
|
---|
| 3530 | /**
|
---|
| 3531 | * @typedef {Object} CascadingConfigArrayFactoryOptions
|
---|
| 3532 | * @property {Map<string,Plugin>} [additionalPluginPool] The map for additional plugins.
|
---|
| 3533 | * @property {ConfigData} [baseConfig] The config by `baseConfig` option.
|
---|
| 3534 | * @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--ignore-pattern`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files.
|
---|
| 3535 | * @property {string} [cwd] The base directory to start lookup.
|
---|
| 3536 | * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`.
|
---|
| 3537 | * @property {string[]} [rulePaths] The value of `--rulesdir` option.
|
---|
| 3538 | * @property {string} [specificConfigPath] The value of `--config` option.
|
---|
| 3539 | * @property {boolean} [useEslintrc] if `false` then it doesn't load config files.
|
---|
| 3540 | * @property {Function} loadRules The function to use to load rules.
|
---|
| 3541 | * @property {Map<string,Rule>} builtInRules The rules that are built in to ESLint.
|
---|
| 3542 | * @property {Object} [resolver=ModuleResolver] The module resolver object.
|
---|
| 3543 | * @property {string} eslintAllPath The path to the definitions for eslint:all.
|
---|
| 3544 | * @property {Function} getEslintAllConfig Returns the config data for eslint:all.
|
---|
| 3545 | * @property {string} eslintRecommendedPath The path to the definitions for eslint:recommended.
|
---|
| 3546 | * @property {Function} getEslintRecommendedConfig Returns the config data for eslint:recommended.
|
---|
| 3547 | */
|
---|
| 3548 |
|
---|
| 3549 | /**
|
---|
| 3550 | * @typedef {Object} CascadingConfigArrayFactoryInternalSlots
|
---|
| 3551 | * @property {ConfigArray} baseConfigArray The config array of `baseConfig` option.
|
---|
| 3552 | * @property {ConfigData} baseConfigData The config data of `baseConfig` option. This is used to reset `baseConfigArray`.
|
---|
| 3553 | * @property {ConfigArray} cliConfigArray The config array of CLI options.
|
---|
| 3554 | * @property {ConfigData} cliConfigData The config data of CLI options. This is used to reset `cliConfigArray`.
|
---|
| 3555 | * @property {ConfigArrayFactory} configArrayFactory The factory for config arrays.
|
---|
| 3556 | * @property {Map<string, ConfigArray>} configCache The cache from directory paths to config arrays.
|
---|
| 3557 | * @property {string} cwd The base directory to start lookup.
|
---|
| 3558 | * @property {WeakMap<ConfigArray, ConfigArray>} finalizeCache The cache from config arrays to finalized config arrays.
|
---|
| 3559 | * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`.
|
---|
| 3560 | * @property {string[]|null} rulePaths The value of `--rulesdir` option. This is used to reset `baseConfigArray`.
|
---|
| 3561 | * @property {string|null} specificConfigPath The value of `--config` option. This is used to reset `cliConfigArray`.
|
---|
| 3562 | * @property {boolean} useEslintrc if `false` then it doesn't load config files.
|
---|
| 3563 | * @property {Function} loadRules The function to use to load rules.
|
---|
| 3564 | * @property {Map<string,Rule>} builtInRules The rules that are built in to ESLint.
|
---|
| 3565 | * @property {Object} [resolver=ModuleResolver] The module resolver object.
|
---|
| 3566 | * @property {string} eslintAllPath The path to the definitions for eslint:all.
|
---|
| 3567 | * @property {Function} getEslintAllConfig Returns the config data for eslint:all.
|
---|
| 3568 | * @property {string} eslintRecommendedPath The path to the definitions for eslint:recommended.
|
---|
| 3569 | * @property {Function} getEslintRecommendedConfig Returns the config data for eslint:recommended.
|
---|
| 3570 | */
|
---|
| 3571 |
|
---|
| 3572 | /** @type {WeakMap<CascadingConfigArrayFactory, CascadingConfigArrayFactoryInternalSlots>} */
|
---|
| 3573 | const internalSlotsMap = new WeakMap();
|
---|
| 3574 |
|
---|
| 3575 | /**
|
---|
| 3576 | * Create the config array from `baseConfig` and `rulePaths`.
|
---|
| 3577 | * @param {CascadingConfigArrayFactoryInternalSlots} slots The slots.
|
---|
| 3578 | * @returns {ConfigArray} The config array of the base configs.
|
---|
| 3579 | */
|
---|
| 3580 | function createBaseConfigArray({
|
---|
| 3581 | configArrayFactory,
|
---|
| 3582 | baseConfigData,
|
---|
| 3583 | rulePaths,
|
---|
| 3584 | cwd,
|
---|
| 3585 | loadRules
|
---|
| 3586 | }) {
|
---|
| 3587 | const baseConfigArray = configArrayFactory.create(
|
---|
| 3588 | baseConfigData,
|
---|
| 3589 | { name: "BaseConfig" }
|
---|
| 3590 | );
|
---|
| 3591 |
|
---|
| 3592 | /*
|
---|
| 3593 | * Create the config array element for the default ignore patterns.
|
---|
| 3594 | * This element has `ignorePattern` property that ignores the default
|
---|
| 3595 | * patterns in the current working directory.
|
---|
| 3596 | */
|
---|
| 3597 | baseConfigArray.unshift(configArrayFactory.create(
|
---|
| 3598 | { ignorePatterns: IgnorePattern.DefaultPatterns },
|
---|
| 3599 | { name: "DefaultIgnorePattern" }
|
---|
| 3600 | )[0]);
|
---|
| 3601 |
|
---|
| 3602 | /*
|
---|
| 3603 | * Load rules `--rulesdir` option as a pseudo plugin.
|
---|
| 3604 | * Use a pseudo plugin to define rules of `--rulesdir`, so we can validate
|
---|
| 3605 | * the rule's options with only information in the config array.
|
---|
| 3606 | */
|
---|
| 3607 | if (rulePaths && rulePaths.length > 0) {
|
---|
| 3608 | baseConfigArray.push({
|
---|
| 3609 | type: "config",
|
---|
| 3610 | name: "--rulesdir",
|
---|
| 3611 | filePath: "",
|
---|
| 3612 | plugins: {
|
---|
| 3613 | "": new ConfigDependency({
|
---|
| 3614 | definition: {
|
---|
| 3615 | rules: rulePaths.reduce(
|
---|
| 3616 | (map, rulesPath) => Object.assign(
|
---|
| 3617 | map,
|
---|
| 3618 | loadRules(rulesPath, cwd)
|
---|
| 3619 | ),
|
---|
| 3620 | {}
|
---|
| 3621 | )
|
---|
| 3622 | },
|
---|
| 3623 | filePath: "",
|
---|
| 3624 | id: "",
|
---|
| 3625 | importerName: "--rulesdir",
|
---|
| 3626 | importerPath: ""
|
---|
| 3627 | })
|
---|
| 3628 | }
|
---|
| 3629 | });
|
---|
| 3630 | }
|
---|
| 3631 |
|
---|
| 3632 | return baseConfigArray;
|
---|
| 3633 | }
|
---|
| 3634 |
|
---|
| 3635 | /**
|
---|
| 3636 | * Create the config array from CLI options.
|
---|
| 3637 | * @param {CascadingConfigArrayFactoryInternalSlots} slots The slots.
|
---|
| 3638 | * @returns {ConfigArray} The config array of the base configs.
|
---|
| 3639 | */
|
---|
| 3640 | function createCLIConfigArray({
|
---|
| 3641 | cliConfigData,
|
---|
| 3642 | configArrayFactory,
|
---|
| 3643 | cwd,
|
---|
| 3644 | ignorePath,
|
---|
| 3645 | specificConfigPath
|
---|
| 3646 | }) {
|
---|
| 3647 | const cliConfigArray = configArrayFactory.create(
|
---|
| 3648 | cliConfigData,
|
---|
| 3649 | { name: "CLIOptions" }
|
---|
| 3650 | );
|
---|
| 3651 |
|
---|
| 3652 | cliConfigArray.unshift(
|
---|
| 3653 | ...(ignorePath
|
---|
| 3654 | ? configArrayFactory.loadESLintIgnore(ignorePath)
|
---|
| 3655 | : configArrayFactory.loadDefaultESLintIgnore())
|
---|
| 3656 | );
|
---|
| 3657 |
|
---|
| 3658 | if (specificConfigPath) {
|
---|
| 3659 | cliConfigArray.unshift(
|
---|
| 3660 | ...configArrayFactory.loadFile(
|
---|
| 3661 | specificConfigPath,
|
---|
| 3662 | { name: "--config", basePath: cwd }
|
---|
| 3663 | )
|
---|
| 3664 | );
|
---|
| 3665 | }
|
---|
| 3666 |
|
---|
| 3667 | return cliConfigArray;
|
---|
| 3668 | }
|
---|
| 3669 |
|
---|
| 3670 | /**
|
---|
| 3671 | * The error type when there are files matched by a glob, but all of them have been ignored.
|
---|
| 3672 | */
|
---|
| 3673 | class ConfigurationNotFoundError extends Error {
|
---|
| 3674 |
|
---|
| 3675 | // eslint-disable-next-line jsdoc/require-description
|
---|
| 3676 | /**
|
---|
| 3677 | * @param {string} directoryPath The directory path.
|
---|
| 3678 | */
|
---|
| 3679 | constructor(directoryPath) {
|
---|
| 3680 | super(`No ESLint configuration found in ${directoryPath}.`);
|
---|
| 3681 | this.messageTemplate = "no-config-found";
|
---|
| 3682 | this.messageData = { directoryPath };
|
---|
| 3683 | }
|
---|
| 3684 | }
|
---|
| 3685 |
|
---|
| 3686 | /**
|
---|
| 3687 | * This class provides the functionality that enumerates every file which is
|
---|
| 3688 | * matched by given glob patterns and that configuration.
|
---|
| 3689 | */
|
---|
| 3690 | class CascadingConfigArrayFactory {
|
---|
| 3691 |
|
---|
| 3692 | /**
|
---|
| 3693 | * Initialize this enumerator.
|
---|
| 3694 | * @param {CascadingConfigArrayFactoryOptions} options The options.
|
---|
| 3695 | */
|
---|
| 3696 | constructor({
|
---|
| 3697 | additionalPluginPool = new Map(),
|
---|
| 3698 | baseConfig: baseConfigData = null,
|
---|
| 3699 | cliConfig: cliConfigData = null,
|
---|
| 3700 | cwd = process.cwd(),
|
---|
| 3701 | ignorePath,
|
---|
| 3702 | resolvePluginsRelativeTo,
|
---|
| 3703 | rulePaths = [],
|
---|
| 3704 | specificConfigPath = null,
|
---|
| 3705 | useEslintrc = true,
|
---|
| 3706 | builtInRules = new Map(),
|
---|
| 3707 | loadRules,
|
---|
| 3708 | resolver,
|
---|
| 3709 | eslintRecommendedPath,
|
---|
| 3710 | getEslintRecommendedConfig,
|
---|
| 3711 | eslintAllPath,
|
---|
| 3712 | getEslintAllConfig
|
---|
| 3713 | } = {}) {
|
---|
| 3714 | const configArrayFactory = new ConfigArrayFactory({
|
---|
| 3715 | additionalPluginPool,
|
---|
| 3716 | cwd,
|
---|
| 3717 | resolvePluginsRelativeTo,
|
---|
| 3718 | builtInRules,
|
---|
| 3719 | resolver,
|
---|
| 3720 | eslintRecommendedPath,
|
---|
| 3721 | getEslintRecommendedConfig,
|
---|
| 3722 | eslintAllPath,
|
---|
| 3723 | getEslintAllConfig
|
---|
| 3724 | });
|
---|
| 3725 |
|
---|
| 3726 | internalSlotsMap.set(this, {
|
---|
| 3727 | baseConfigArray: createBaseConfigArray({
|
---|
| 3728 | baseConfigData,
|
---|
| 3729 | configArrayFactory,
|
---|
| 3730 | cwd,
|
---|
| 3731 | rulePaths,
|
---|
| 3732 | loadRules
|
---|
| 3733 | }),
|
---|
| 3734 | baseConfigData,
|
---|
| 3735 | cliConfigArray: createCLIConfigArray({
|
---|
| 3736 | cliConfigData,
|
---|
| 3737 | configArrayFactory,
|
---|
| 3738 | cwd,
|
---|
| 3739 | ignorePath,
|
---|
| 3740 | specificConfigPath
|
---|
| 3741 | }),
|
---|
| 3742 | cliConfigData,
|
---|
| 3743 | configArrayFactory,
|
---|
| 3744 | configCache: new Map(),
|
---|
| 3745 | cwd,
|
---|
| 3746 | finalizeCache: new WeakMap(),
|
---|
| 3747 | ignorePath,
|
---|
| 3748 | rulePaths,
|
---|
| 3749 | specificConfigPath,
|
---|
| 3750 | useEslintrc,
|
---|
| 3751 | builtInRules,
|
---|
| 3752 | loadRules
|
---|
| 3753 | });
|
---|
| 3754 | }
|
---|
| 3755 |
|
---|
| 3756 | /**
|
---|
| 3757 | * The path to the current working directory.
|
---|
| 3758 | * This is used by tests.
|
---|
| 3759 | * @type {string}
|
---|
| 3760 | */
|
---|
| 3761 | get cwd() {
|
---|
| 3762 | const { cwd } = internalSlotsMap.get(this);
|
---|
| 3763 |
|
---|
| 3764 | return cwd;
|
---|
| 3765 | }
|
---|
| 3766 |
|
---|
| 3767 | /**
|
---|
| 3768 | * Get the config array of a given file.
|
---|
| 3769 | * If `filePath` was not given, it returns the config which contains only
|
---|
| 3770 | * `baseConfigData` and `cliConfigData`.
|
---|
| 3771 | * @param {string} [filePath] The file path to a file.
|
---|
| 3772 | * @param {Object} [options] The options.
|
---|
| 3773 | * @param {boolean} [options.ignoreNotFoundError] If `true` then it doesn't throw `ConfigurationNotFoundError`.
|
---|
| 3774 | * @returns {ConfigArray} The config array of the file.
|
---|
| 3775 | */
|
---|
| 3776 | getConfigArrayForFile(filePath, { ignoreNotFoundError = false } = {}) {
|
---|
| 3777 | const {
|
---|
| 3778 | baseConfigArray,
|
---|
| 3779 | cliConfigArray,
|
---|
| 3780 | cwd
|
---|
| 3781 | } = internalSlotsMap.get(this);
|
---|
| 3782 |
|
---|
| 3783 | if (!filePath) {
|
---|
| 3784 | return new ConfigArray(...baseConfigArray, ...cliConfigArray);
|
---|
| 3785 | }
|
---|
| 3786 |
|
---|
| 3787 | const directoryPath = path__default["default"].dirname(path__default["default"].resolve(cwd, filePath));
|
---|
| 3788 |
|
---|
| 3789 | debug$1(`Load config files for ${directoryPath}.`);
|
---|
| 3790 |
|
---|
| 3791 | return this._finalizeConfigArray(
|
---|
| 3792 | this._loadConfigInAncestors(directoryPath),
|
---|
| 3793 | directoryPath,
|
---|
| 3794 | ignoreNotFoundError
|
---|
| 3795 | );
|
---|
| 3796 | }
|
---|
| 3797 |
|
---|
| 3798 | /**
|
---|
| 3799 | * Set the config data to override all configs.
|
---|
| 3800 | * Require to call `clearCache()` method after this method is called.
|
---|
| 3801 | * @param {ConfigData} configData The config data to override all configs.
|
---|
| 3802 | * @returns {void}
|
---|
| 3803 | */
|
---|
| 3804 | setOverrideConfig(configData) {
|
---|
| 3805 | const slots = internalSlotsMap.get(this);
|
---|
| 3806 |
|
---|
| 3807 | slots.cliConfigData = configData;
|
---|
| 3808 | }
|
---|
| 3809 |
|
---|
| 3810 | /**
|
---|
| 3811 | * Clear config cache.
|
---|
| 3812 | * @returns {void}
|
---|
| 3813 | */
|
---|
| 3814 | clearCache() {
|
---|
| 3815 | const slots = internalSlotsMap.get(this);
|
---|
| 3816 |
|
---|
| 3817 | slots.baseConfigArray = createBaseConfigArray(slots);
|
---|
| 3818 | slots.cliConfigArray = createCLIConfigArray(slots);
|
---|
| 3819 | slots.configCache.clear();
|
---|
| 3820 | }
|
---|
| 3821 |
|
---|
| 3822 | /**
|
---|
| 3823 | * Load and normalize config files from the ancestor directories.
|
---|
| 3824 | * @param {string} directoryPath The path to a leaf directory.
|
---|
| 3825 | * @param {boolean} configsExistInSubdirs `true` if configurations exist in subdirectories.
|
---|
| 3826 | * @returns {ConfigArray} The loaded config.
|
---|
| 3827 | * @private
|
---|
| 3828 | */
|
---|
| 3829 | _loadConfigInAncestors(directoryPath, configsExistInSubdirs = false) {
|
---|
| 3830 | const {
|
---|
| 3831 | baseConfigArray,
|
---|
| 3832 | configArrayFactory,
|
---|
| 3833 | configCache,
|
---|
| 3834 | cwd,
|
---|
| 3835 | useEslintrc
|
---|
| 3836 | } = internalSlotsMap.get(this);
|
---|
| 3837 |
|
---|
| 3838 | if (!useEslintrc) {
|
---|
| 3839 | return baseConfigArray;
|
---|
| 3840 | }
|
---|
| 3841 |
|
---|
| 3842 | let configArray = configCache.get(directoryPath);
|
---|
| 3843 |
|
---|
| 3844 | // Hit cache.
|
---|
| 3845 | if (configArray) {
|
---|
| 3846 | debug$1(`Cache hit: ${directoryPath}.`);
|
---|
| 3847 | return configArray;
|
---|
| 3848 | }
|
---|
| 3849 | debug$1(`No cache found: ${directoryPath}.`);
|
---|
| 3850 |
|
---|
| 3851 | const homePath = os__default["default"].homedir();
|
---|
| 3852 |
|
---|
| 3853 | // Consider this is root.
|
---|
| 3854 | if (directoryPath === homePath && cwd !== homePath) {
|
---|
| 3855 | debug$1("Stop traversing because of considered root.");
|
---|
| 3856 | if (configsExistInSubdirs) {
|
---|
| 3857 | const filePath = ConfigArrayFactory.getPathToConfigFileInDirectory(directoryPath);
|
---|
| 3858 |
|
---|
| 3859 | if (filePath) {
|
---|
| 3860 | emitDeprecationWarning(
|
---|
| 3861 | filePath,
|
---|
| 3862 | "ESLINT_PERSONAL_CONFIG_SUPPRESS"
|
---|
| 3863 | );
|
---|
| 3864 | }
|
---|
| 3865 | }
|
---|
| 3866 | return this._cacheConfig(directoryPath, baseConfigArray);
|
---|
| 3867 | }
|
---|
| 3868 |
|
---|
| 3869 | // Load the config on this directory.
|
---|
| 3870 | try {
|
---|
| 3871 | configArray = configArrayFactory.loadInDirectory(directoryPath);
|
---|
| 3872 | } catch (error) {
|
---|
| 3873 | /* istanbul ignore next */
|
---|
| 3874 | if (error.code === "EACCES") {
|
---|
| 3875 | debug$1("Stop traversing because of 'EACCES' error.");
|
---|
| 3876 | return this._cacheConfig(directoryPath, baseConfigArray);
|
---|
| 3877 | }
|
---|
| 3878 | throw error;
|
---|
| 3879 | }
|
---|
| 3880 |
|
---|
| 3881 | if (configArray.length > 0 && configArray.isRoot()) {
|
---|
| 3882 | debug$1("Stop traversing because of 'root:true'.");
|
---|
| 3883 | configArray.unshift(...baseConfigArray);
|
---|
| 3884 | return this._cacheConfig(directoryPath, configArray);
|
---|
| 3885 | }
|
---|
| 3886 |
|
---|
| 3887 | // Load from the ancestors and merge it.
|
---|
| 3888 | const parentPath = path__default["default"].dirname(directoryPath);
|
---|
| 3889 | const parentConfigArray = parentPath && parentPath !== directoryPath
|
---|
| 3890 | ? this._loadConfigInAncestors(
|
---|
| 3891 | parentPath,
|
---|
| 3892 | configsExistInSubdirs || configArray.length > 0
|
---|
| 3893 | )
|
---|
| 3894 | : baseConfigArray;
|
---|
| 3895 |
|
---|
| 3896 | if (configArray.length > 0) {
|
---|
| 3897 | configArray.unshift(...parentConfigArray);
|
---|
| 3898 | } else {
|
---|
| 3899 | configArray = parentConfigArray;
|
---|
| 3900 | }
|
---|
| 3901 |
|
---|
| 3902 | // Cache and return.
|
---|
| 3903 | return this._cacheConfig(directoryPath, configArray);
|
---|
| 3904 | }
|
---|
| 3905 |
|
---|
| 3906 | /**
|
---|
| 3907 | * Freeze and cache a given config.
|
---|
| 3908 | * @param {string} directoryPath The path to a directory as a cache key.
|
---|
| 3909 | * @param {ConfigArray} configArray The config array as a cache value.
|
---|
| 3910 | * @returns {ConfigArray} The `configArray` (frozen).
|
---|
| 3911 | */
|
---|
| 3912 | _cacheConfig(directoryPath, configArray) {
|
---|
| 3913 | const { configCache } = internalSlotsMap.get(this);
|
---|
| 3914 |
|
---|
| 3915 | Object.freeze(configArray);
|
---|
| 3916 | configCache.set(directoryPath, configArray);
|
---|
| 3917 |
|
---|
| 3918 | return configArray;
|
---|
| 3919 | }
|
---|
| 3920 |
|
---|
| 3921 | /**
|
---|
| 3922 | * Finalize a given config array.
|
---|
| 3923 | * Concatenate `--config` and other CLI options.
|
---|
| 3924 | * @param {ConfigArray} configArray The parent config array.
|
---|
| 3925 | * @param {string} directoryPath The path to the leaf directory to find config files.
|
---|
| 3926 | * @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`.
|
---|
| 3927 | * @returns {ConfigArray} The loaded config.
|
---|
| 3928 | * @private
|
---|
| 3929 | */
|
---|
| 3930 | _finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) {
|
---|
| 3931 | const {
|
---|
| 3932 | cliConfigArray,
|
---|
| 3933 | configArrayFactory,
|
---|
| 3934 | finalizeCache,
|
---|
| 3935 | useEslintrc,
|
---|
| 3936 | builtInRules
|
---|
| 3937 | } = internalSlotsMap.get(this);
|
---|
| 3938 |
|
---|
| 3939 | let finalConfigArray = finalizeCache.get(configArray);
|
---|
| 3940 |
|
---|
| 3941 | if (!finalConfigArray) {
|
---|
| 3942 | finalConfigArray = configArray;
|
---|
| 3943 |
|
---|
| 3944 | // Load the personal config if there are no regular config files.
|
---|
| 3945 | if (
|
---|
| 3946 | useEslintrc &&
|
---|
| 3947 | configArray.every(c => !c.filePath) &&
|
---|
| 3948 | cliConfigArray.every(c => !c.filePath) // `--config` option can be a file.
|
---|
| 3949 | ) {
|
---|
| 3950 | const homePath = os__default["default"].homedir();
|
---|
| 3951 |
|
---|
| 3952 | debug$1("Loading the config file of the home directory:", homePath);
|
---|
| 3953 |
|
---|
| 3954 | const personalConfigArray = configArrayFactory.loadInDirectory(
|
---|
| 3955 | homePath,
|
---|
| 3956 | { name: "PersonalConfig" }
|
---|
| 3957 | );
|
---|
| 3958 |
|
---|
| 3959 | if (
|
---|
| 3960 | personalConfigArray.length > 0 &&
|
---|
| 3961 | !directoryPath.startsWith(homePath)
|
---|
| 3962 | ) {
|
---|
| 3963 | const lastElement =
|
---|
| 3964 | personalConfigArray[personalConfigArray.length - 1];
|
---|
| 3965 |
|
---|
| 3966 | emitDeprecationWarning(
|
---|
| 3967 | lastElement.filePath,
|
---|
| 3968 | "ESLINT_PERSONAL_CONFIG_LOAD"
|
---|
| 3969 | );
|
---|
| 3970 | }
|
---|
| 3971 |
|
---|
| 3972 | finalConfigArray = finalConfigArray.concat(personalConfigArray);
|
---|
| 3973 | }
|
---|
| 3974 |
|
---|
| 3975 | // Apply CLI options.
|
---|
| 3976 | if (cliConfigArray.length > 0) {
|
---|
| 3977 | finalConfigArray = finalConfigArray.concat(cliConfigArray);
|
---|
| 3978 | }
|
---|
| 3979 |
|
---|
| 3980 | // Validate rule settings and environments.
|
---|
| 3981 | const validator = new ConfigValidator({
|
---|
| 3982 | builtInRules
|
---|
| 3983 | });
|
---|
| 3984 |
|
---|
| 3985 | validator.validateConfigArray(finalConfigArray);
|
---|
| 3986 |
|
---|
| 3987 | // Cache it.
|
---|
| 3988 | Object.freeze(finalConfigArray);
|
---|
| 3989 | finalizeCache.set(configArray, finalConfigArray);
|
---|
| 3990 |
|
---|
| 3991 | debug$1(
|
---|
| 3992 | "Configuration was determined: %o on %s",
|
---|
| 3993 | finalConfigArray,
|
---|
| 3994 | directoryPath
|
---|
| 3995 | );
|
---|
| 3996 | }
|
---|
| 3997 |
|
---|
| 3998 | // At least one element (the default ignore patterns) exists.
|
---|
| 3999 | if (!ignoreNotFoundError && useEslintrc && finalConfigArray.length <= 1) {
|
---|
| 4000 | throw new ConfigurationNotFoundError(directoryPath);
|
---|
| 4001 | }
|
---|
| 4002 |
|
---|
| 4003 | return finalConfigArray;
|
---|
| 4004 | }
|
---|
| 4005 | }
|
---|
| 4006 |
|
---|
| 4007 | /**
|
---|
| 4008 | * @fileoverview Compatibility class for flat config.
|
---|
| 4009 | * @author Nicholas C. Zakas
|
---|
| 4010 | */
|
---|
| 4011 |
|
---|
| 4012 | //-----------------------------------------------------------------------------
|
---|
| 4013 | // Helpers
|
---|
| 4014 | //-----------------------------------------------------------------------------
|
---|
| 4015 |
|
---|
| 4016 | /** @typedef {import("../../shared/types").Environment} Environment */
|
---|
| 4017 | /** @typedef {import("../../shared/types").Processor} Processor */
|
---|
| 4018 |
|
---|
| 4019 | const debug = debugOrig__default["default"]("eslintrc:flat-compat");
|
---|
| 4020 | const cafactory = Symbol("cafactory");
|
---|
| 4021 |
|
---|
| 4022 | /**
|
---|
| 4023 | * Translates an ESLintRC-style config object into a flag-config-style config
|
---|
| 4024 | * object.
|
---|
| 4025 | * @param {Object} eslintrcConfig An ESLintRC-style config object.
|
---|
| 4026 | * @param {Object} options Options to help translate the config.
|
---|
| 4027 | * @param {string} options.resolveConfigRelativeTo To the directory to resolve
|
---|
| 4028 | * configs from.
|
---|
| 4029 | * @param {string} options.resolvePluginsRelativeTo The directory to resolve
|
---|
| 4030 | * plugins from.
|
---|
| 4031 | * @param {ReadOnlyMap<string,Environment>} options.pluginEnvironments A map of plugin environment
|
---|
| 4032 | * names to objects.
|
---|
| 4033 | * @param {ReadOnlyMap<string,Processor>} options.pluginProcessors A map of plugin processor
|
---|
| 4034 | * names to objects.
|
---|
| 4035 | * @returns {Object} A flag-config-style config object.
|
---|
| 4036 | */
|
---|
| 4037 | function translateESLintRC(eslintrcConfig, {
|
---|
| 4038 | resolveConfigRelativeTo,
|
---|
| 4039 | resolvePluginsRelativeTo,
|
---|
| 4040 | pluginEnvironments,
|
---|
| 4041 | pluginProcessors
|
---|
| 4042 | }) {
|
---|
| 4043 |
|
---|
| 4044 | const flatConfig = {};
|
---|
| 4045 | const configs = [];
|
---|
| 4046 | const languageOptions = {};
|
---|
| 4047 | const linterOptions = {};
|
---|
| 4048 | const keysToCopy = ["settings", "rules", "processor"];
|
---|
| 4049 | const languageOptionsKeysToCopy = ["globals", "parser", "parserOptions"];
|
---|
| 4050 | const linterOptionsKeysToCopy = ["noInlineConfig", "reportUnusedDisableDirectives"];
|
---|
| 4051 |
|
---|
| 4052 | // copy over simple translations
|
---|
| 4053 | for (const key of keysToCopy) {
|
---|
| 4054 | if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") {
|
---|
| 4055 | flatConfig[key] = eslintrcConfig[key];
|
---|
| 4056 | }
|
---|
| 4057 | }
|
---|
| 4058 |
|
---|
| 4059 | // copy over languageOptions
|
---|
| 4060 | for (const key of languageOptionsKeysToCopy) {
|
---|
| 4061 | if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") {
|
---|
| 4062 |
|
---|
| 4063 | // create the languageOptions key in the flat config
|
---|
| 4064 | flatConfig.languageOptions = languageOptions;
|
---|
| 4065 |
|
---|
| 4066 | if (key === "parser") {
|
---|
| 4067 | debug(`Resolving parser '${languageOptions[key]}' relative to ${resolveConfigRelativeTo}`);
|
---|
| 4068 |
|
---|
| 4069 | if (eslintrcConfig[key].error) {
|
---|
| 4070 | throw eslintrcConfig[key].error;
|
---|
| 4071 | }
|
---|
| 4072 |
|
---|
| 4073 | languageOptions[key] = eslintrcConfig[key].definition;
|
---|
| 4074 | continue;
|
---|
| 4075 | }
|
---|
| 4076 |
|
---|
| 4077 | // clone any object values that are in the eslintrc config
|
---|
| 4078 | if (eslintrcConfig[key] && typeof eslintrcConfig[key] === "object") {
|
---|
| 4079 | languageOptions[key] = {
|
---|
| 4080 | ...eslintrcConfig[key]
|
---|
| 4081 | };
|
---|
| 4082 | } else {
|
---|
| 4083 | languageOptions[key] = eslintrcConfig[key];
|
---|
| 4084 | }
|
---|
| 4085 | }
|
---|
| 4086 | }
|
---|
| 4087 |
|
---|
| 4088 | // copy over linterOptions
|
---|
| 4089 | for (const key of linterOptionsKeysToCopy) {
|
---|
| 4090 | if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") {
|
---|
| 4091 | flatConfig.linterOptions = linterOptions;
|
---|
| 4092 | linterOptions[key] = eslintrcConfig[key];
|
---|
| 4093 | }
|
---|
| 4094 | }
|
---|
| 4095 |
|
---|
| 4096 | // move ecmaVersion a level up
|
---|
| 4097 | if (languageOptions.parserOptions) {
|
---|
| 4098 |
|
---|
| 4099 | if ("ecmaVersion" in languageOptions.parserOptions) {
|
---|
| 4100 | languageOptions.ecmaVersion = languageOptions.parserOptions.ecmaVersion;
|
---|
| 4101 | delete languageOptions.parserOptions.ecmaVersion;
|
---|
| 4102 | }
|
---|
| 4103 |
|
---|
| 4104 | if ("sourceType" in languageOptions.parserOptions) {
|
---|
| 4105 | languageOptions.sourceType = languageOptions.parserOptions.sourceType;
|
---|
| 4106 | delete languageOptions.parserOptions.sourceType;
|
---|
| 4107 | }
|
---|
| 4108 |
|
---|
| 4109 | // check to see if we even need parserOptions anymore and remove it if not
|
---|
| 4110 | if (Object.keys(languageOptions.parserOptions).length === 0) {
|
---|
| 4111 | delete languageOptions.parserOptions;
|
---|
| 4112 | }
|
---|
| 4113 | }
|
---|
| 4114 |
|
---|
| 4115 | // overrides
|
---|
| 4116 | if (eslintrcConfig.criteria) {
|
---|
| 4117 | flatConfig.files = [absoluteFilePath => eslintrcConfig.criteria.test(absoluteFilePath)];
|
---|
| 4118 | }
|
---|
| 4119 |
|
---|
| 4120 | // translate plugins
|
---|
| 4121 | if (eslintrcConfig.plugins && typeof eslintrcConfig.plugins === "object") {
|
---|
| 4122 | debug(`Translating plugins: ${eslintrcConfig.plugins}`);
|
---|
| 4123 |
|
---|
| 4124 | flatConfig.plugins = {};
|
---|
| 4125 |
|
---|
| 4126 | for (const pluginName of Object.keys(eslintrcConfig.plugins)) {
|
---|
| 4127 |
|
---|
| 4128 | debug(`Translating plugin: ${pluginName}`);
|
---|
| 4129 | debug(`Resolving plugin '${pluginName} relative to ${resolvePluginsRelativeTo}`);
|
---|
| 4130 |
|
---|
| 4131 | const { original: plugin, error } = eslintrcConfig.plugins[pluginName];
|
---|
| 4132 |
|
---|
| 4133 | if (error) {
|
---|
| 4134 | throw error;
|
---|
| 4135 | }
|
---|
| 4136 |
|
---|
| 4137 | flatConfig.plugins[pluginName] = plugin;
|
---|
| 4138 |
|
---|
| 4139 | // create a config for any processors
|
---|
| 4140 | if (plugin.processors) {
|
---|
| 4141 | for (const processorName of Object.keys(plugin.processors)) {
|
---|
| 4142 | if (processorName.startsWith(".")) {
|
---|
| 4143 | debug(`Assigning processor: ${pluginName}/${processorName}`);
|
---|
| 4144 |
|
---|
| 4145 | configs.unshift({
|
---|
| 4146 | files: [`**/*${processorName}`],
|
---|
| 4147 | processor: pluginProcessors.get(`${pluginName}/${processorName}`)
|
---|
| 4148 | });
|
---|
| 4149 | }
|
---|
| 4150 |
|
---|
| 4151 | }
|
---|
| 4152 | }
|
---|
| 4153 | }
|
---|
| 4154 | }
|
---|
| 4155 |
|
---|
| 4156 | // translate env - must come after plugins
|
---|
| 4157 | if (eslintrcConfig.env && typeof eslintrcConfig.env === "object") {
|
---|
| 4158 | for (const envName of Object.keys(eslintrcConfig.env)) {
|
---|
| 4159 |
|
---|
| 4160 | // only add environments that are true
|
---|
| 4161 | if (eslintrcConfig.env[envName]) {
|
---|
| 4162 | debug(`Translating environment: ${envName}`);
|
---|
| 4163 |
|
---|
| 4164 | if (environments.has(envName)) {
|
---|
| 4165 |
|
---|
| 4166 | // built-in environments should be defined first
|
---|
| 4167 | configs.unshift(...translateESLintRC({
|
---|
| 4168 | criteria: eslintrcConfig.criteria,
|
---|
| 4169 | ...environments.get(envName)
|
---|
| 4170 | }, {
|
---|
| 4171 | resolveConfigRelativeTo,
|
---|
| 4172 | resolvePluginsRelativeTo
|
---|
| 4173 | }));
|
---|
| 4174 | } else if (pluginEnvironments.has(envName)) {
|
---|
| 4175 |
|
---|
| 4176 | // if the environment comes from a plugin, it should come after the plugin config
|
---|
| 4177 | configs.push(...translateESLintRC({
|
---|
| 4178 | criteria: eslintrcConfig.criteria,
|
---|
| 4179 | ...pluginEnvironments.get(envName)
|
---|
| 4180 | }, {
|
---|
| 4181 | resolveConfigRelativeTo,
|
---|
| 4182 | resolvePluginsRelativeTo
|
---|
| 4183 | }));
|
---|
| 4184 | }
|
---|
| 4185 | }
|
---|
| 4186 | }
|
---|
| 4187 | }
|
---|
| 4188 |
|
---|
| 4189 | // only add if there are actually keys in the config
|
---|
| 4190 | if (Object.keys(flatConfig).length > 0) {
|
---|
| 4191 | configs.push(flatConfig);
|
---|
| 4192 | }
|
---|
| 4193 |
|
---|
| 4194 | return configs;
|
---|
| 4195 | }
|
---|
| 4196 |
|
---|
| 4197 |
|
---|
| 4198 | //-----------------------------------------------------------------------------
|
---|
| 4199 | // Exports
|
---|
| 4200 | //-----------------------------------------------------------------------------
|
---|
| 4201 |
|
---|
| 4202 | /**
|
---|
| 4203 | * A compatibility class for working with configs.
|
---|
| 4204 | */
|
---|
| 4205 | class FlatCompat {
|
---|
| 4206 |
|
---|
| 4207 | constructor({
|
---|
| 4208 | baseDirectory = process.cwd(),
|
---|
| 4209 | resolvePluginsRelativeTo = baseDirectory,
|
---|
| 4210 | recommendedConfig,
|
---|
| 4211 | allConfig
|
---|
| 4212 | } = {}) {
|
---|
| 4213 | this.baseDirectory = baseDirectory;
|
---|
| 4214 | this.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
|
---|
| 4215 | this[cafactory] = new ConfigArrayFactory({
|
---|
| 4216 | cwd: baseDirectory,
|
---|
| 4217 | resolvePluginsRelativeTo,
|
---|
| 4218 | getEslintAllConfig: () => {
|
---|
| 4219 |
|
---|
| 4220 | if (!allConfig) {
|
---|
| 4221 | throw new TypeError("Missing parameter 'allConfig' in FlatCompat constructor.");
|
---|
| 4222 | }
|
---|
| 4223 |
|
---|
| 4224 | return allConfig;
|
---|
| 4225 | },
|
---|
| 4226 | getEslintRecommendedConfig: () => {
|
---|
| 4227 |
|
---|
| 4228 | if (!recommendedConfig) {
|
---|
| 4229 | throw new TypeError("Missing parameter 'recommendedConfig' in FlatCompat constructor.");
|
---|
| 4230 | }
|
---|
| 4231 |
|
---|
| 4232 | return recommendedConfig;
|
---|
| 4233 | }
|
---|
| 4234 | });
|
---|
| 4235 | }
|
---|
| 4236 |
|
---|
| 4237 | /**
|
---|
| 4238 | * Translates an ESLintRC-style config into a flag-config-style config.
|
---|
| 4239 | * @param {Object} eslintrcConfig The ESLintRC-style config object.
|
---|
| 4240 | * @returns {Object} A flag-config-style config object.
|
---|
| 4241 | */
|
---|
| 4242 | config(eslintrcConfig) {
|
---|
| 4243 | const eslintrcArray = this[cafactory].create(eslintrcConfig, {
|
---|
| 4244 | basePath: this.baseDirectory
|
---|
| 4245 | });
|
---|
| 4246 |
|
---|
| 4247 | const flatArray = [];
|
---|
| 4248 | let hasIgnorePatterns = false;
|
---|
| 4249 |
|
---|
| 4250 | eslintrcArray.forEach(configData => {
|
---|
| 4251 | if (configData.type === "config") {
|
---|
| 4252 | hasIgnorePatterns = hasIgnorePatterns || configData.ignorePattern;
|
---|
| 4253 | flatArray.push(...translateESLintRC(configData, {
|
---|
| 4254 | resolveConfigRelativeTo: path__default["default"].join(this.baseDirectory, "__placeholder.js"),
|
---|
| 4255 | resolvePluginsRelativeTo: path__default["default"].join(this.resolvePluginsRelativeTo, "__placeholder.js"),
|
---|
| 4256 | pluginEnvironments: eslintrcArray.pluginEnvironments,
|
---|
| 4257 | pluginProcessors: eslintrcArray.pluginProcessors
|
---|
| 4258 | }));
|
---|
| 4259 | }
|
---|
| 4260 | });
|
---|
| 4261 |
|
---|
| 4262 | // combine ignorePatterns to emulate ESLintRC behavior better
|
---|
| 4263 | if (hasIgnorePatterns) {
|
---|
| 4264 | flatArray.unshift({
|
---|
| 4265 | ignores: [filePath => {
|
---|
| 4266 |
|
---|
| 4267 | // Compute the final config for this file.
|
---|
| 4268 | // This filters config array elements by `files`/`excludedFiles` then merges the elements.
|
---|
| 4269 | const finalConfig = eslintrcArray.extractConfig(filePath);
|
---|
| 4270 |
|
---|
| 4271 | // Test the `ignorePattern` properties of the final config.
|
---|
| 4272 | return Boolean(finalConfig.ignores) && finalConfig.ignores(filePath);
|
---|
| 4273 | }]
|
---|
| 4274 | });
|
---|
| 4275 | }
|
---|
| 4276 |
|
---|
| 4277 | return flatArray;
|
---|
| 4278 | }
|
---|
| 4279 |
|
---|
| 4280 | /**
|
---|
| 4281 | * Translates the `env` section of an ESLintRC-style config.
|
---|
| 4282 | * @param {Object} envConfig The `env` section of an ESLintRC config.
|
---|
| 4283 | * @returns {Object[]} An array of flag-config objects representing the environments.
|
---|
| 4284 | */
|
---|
| 4285 | env(envConfig) {
|
---|
| 4286 | return this.config({
|
---|
| 4287 | env: envConfig
|
---|
| 4288 | });
|
---|
| 4289 | }
|
---|
| 4290 |
|
---|
| 4291 | /**
|
---|
| 4292 | * Translates the `extends` section of an ESLintRC-style config.
|
---|
| 4293 | * @param {...string} configsToExtend The names of the configs to load.
|
---|
| 4294 | * @returns {Object[]} An array of flag-config objects representing the config.
|
---|
| 4295 | */
|
---|
| 4296 | extends(...configsToExtend) {
|
---|
| 4297 | return this.config({
|
---|
| 4298 | extends: configsToExtend
|
---|
| 4299 | });
|
---|
| 4300 | }
|
---|
| 4301 |
|
---|
| 4302 | /**
|
---|
| 4303 | * Translates the `plugins` section of an ESLintRC-style config.
|
---|
| 4304 | * @param {...string} plugins The names of the plugins to load.
|
---|
| 4305 | * @returns {Object[]} An array of flag-config objects representing the plugins.
|
---|
| 4306 | */
|
---|
| 4307 | plugins(...plugins) {
|
---|
| 4308 | return this.config({
|
---|
| 4309 | plugins
|
---|
| 4310 | });
|
---|
| 4311 | }
|
---|
| 4312 | }
|
---|
| 4313 |
|
---|
| 4314 | /**
|
---|
| 4315 | * @fileoverview Package exports for @eslint/eslintrc
|
---|
| 4316 | * @author Nicholas C. Zakas
|
---|
| 4317 | */
|
---|
| 4318 |
|
---|
| 4319 | //-----------------------------------------------------------------------------
|
---|
| 4320 | // Exports
|
---|
| 4321 | //-----------------------------------------------------------------------------
|
---|
| 4322 |
|
---|
| 4323 | const Legacy = {
|
---|
| 4324 | ConfigArray,
|
---|
| 4325 | createConfigArrayFactoryContext: createContext,
|
---|
| 4326 | CascadingConfigArrayFactory,
|
---|
| 4327 | ConfigArrayFactory,
|
---|
| 4328 | ConfigDependency,
|
---|
| 4329 | ExtractedConfig,
|
---|
| 4330 | IgnorePattern,
|
---|
| 4331 | OverrideTester,
|
---|
| 4332 | getUsedExtractedConfigs,
|
---|
| 4333 | environments,
|
---|
| 4334 |
|
---|
| 4335 | // shared
|
---|
| 4336 | ConfigOps,
|
---|
| 4337 | ConfigValidator,
|
---|
| 4338 | ModuleResolver,
|
---|
| 4339 | naming
|
---|
| 4340 | };
|
---|
| 4341 |
|
---|
| 4342 | exports.FlatCompat = FlatCompat;
|
---|
| 4343 | exports.Legacy = Legacy;
|
---|
| 4344 | //# sourceMappingURL=eslintrc.cjs.map
|
---|