| /** | |
| * @fileoverview Flat Config Array | |
| * @author Nicholas C. Zakas | |
| */ | |
| ; | |
| //----------------------------------------------------------------------------- | |
| // Requirements | |
| //----------------------------------------------------------------------------- | |
| const { ConfigArray, ConfigArraySymbol } = require("@eslint/config-array"); | |
| const { flatConfigSchema } = require("./flat-config-schema"); | |
| const { defaultConfig } = require("./default-config"); | |
| const { Config } = require("./config"); | |
| //----------------------------------------------------------------------------- | |
| // Helpers | |
| //----------------------------------------------------------------------------- | |
| /** | |
| * Fields that are considered metadata and not part of the config object. | |
| */ | |
| const META_FIELDS = new Set(["name", "basePath"]); | |
| /** | |
| * Wraps a config error with details about where the error occurred. | |
| * @param {Error} error The original error. | |
| * @param {number} originalLength The original length of the config array. | |
| * @param {number} baseLength The length of the base config. | |
| * @returns {TypeError} The new error with details. | |
| */ | |
| function wrapConfigErrorWithDetails(error, originalLength, baseLength) { | |
| let location = "user-defined"; | |
| let configIndex = error.index; | |
| /* | |
| * A config array is set up in this order: | |
| * 1. Base config | |
| * 2. Original configs | |
| * 3. User-defined configs | |
| * 4. CLI-defined configs | |
| * | |
| * So we need to adjust the index to account for the base config. | |
| * | |
| * - If the index is less than the base length, it's in the base config | |
| * (as specified by `baseConfig` argument to `FlatConfigArray` constructor). | |
| * - If the index is greater than the base length but less than the original | |
| * length + base length, it's in the original config. The original config | |
| * is passed to the `FlatConfigArray` constructor as the first argument. | |
| * - Otherwise, it's in the user-defined config, which is loaded from the | |
| * config file and merged with any command-line options. | |
| */ | |
| if (error.index < baseLength) { | |
| location = "base"; | |
| } else if (error.index < originalLength + baseLength) { | |
| location = "original"; | |
| configIndex = error.index - baseLength; | |
| } else { | |
| configIndex = error.index - originalLength - baseLength; | |
| } | |
| return new TypeError( | |
| `${error.message.slice(0, -1)} at ${location} index ${configIndex}.`, | |
| { cause: error }, | |
| ); | |
| } | |
| const originalBaseConfig = Symbol("originalBaseConfig"); | |
| const originalLength = Symbol("originalLength"); | |
| const baseLength = Symbol("baseLength"); | |
| //----------------------------------------------------------------------------- | |
| // Exports | |
| //----------------------------------------------------------------------------- | |
| /** | |
| * Represents an array containing configuration information for ESLint. | |
| */ | |
| class FlatConfigArray extends ConfigArray { | |
| /** | |
| * Creates a new instance. | |
| * @param {*[]} configs An array of configuration information. | |
| * @param {{basePath: string, shouldIgnore: boolean, baseConfig: FlatConfig}} options The options | |
| * to use for the config array instance. | |
| */ | |
| constructor( | |
| configs, | |
| { basePath, shouldIgnore = true, baseConfig = defaultConfig } = {}, | |
| ) { | |
| super(configs, { | |
| basePath, | |
| schema: flatConfigSchema, | |
| }); | |
| /** | |
| * The original length of the array before any modifications. | |
| * @type {number} | |
| */ | |
| this[originalLength] = this.length; | |
| if (baseConfig[Symbol.iterator]) { | |
| this.unshift(...baseConfig); | |
| } else { | |
| this.unshift(baseConfig); | |
| } | |
| /** | |
| * The length of the array after applying the base config. | |
| * @type {number} | |
| */ | |
| this[baseLength] = this.length - this[originalLength]; | |
| /** | |
| * The base config used to build the config array. | |
| * @type {Array<FlatConfig>} | |
| */ | |
| this[originalBaseConfig] = baseConfig; | |
| Object.defineProperty(this, originalBaseConfig, { writable: false }); | |
| /** | |
| * Determines if `ignores` fields should be honored. | |
| * If true, then all `ignores` fields are honored. | |
| * if false, then only `ignores` fields in the baseConfig are honored. | |
| * @type {boolean} | |
| */ | |
| this.shouldIgnore = shouldIgnore; | |
| Object.defineProperty(this, "shouldIgnore", { writable: false }); | |
| } | |
| /** | |
| * Normalizes the array by calling the superclass method and catching/rethrowing | |
| * any ConfigError exceptions with additional details. | |
| * @param {any} [context] The context to use to normalize the array. | |
| * @returns {Promise<FlatConfigArray>} A promise that resolves when the array is normalized. | |
| */ | |
| normalize(context) { | |
| return super.normalize(context).catch(error => { | |
| if (error.name === "ConfigError") { | |
| throw wrapConfigErrorWithDetails( | |
| error, | |
| this[originalLength], | |
| this[baseLength], | |
| ); | |
| } | |
| throw error; | |
| }); | |
| } | |
| /** | |
| * Normalizes the array by calling the superclass method and catching/rethrowing | |
| * any ConfigError exceptions with additional details. | |
| * @param {any} [context] The context to use to normalize the array. | |
| * @returns {FlatConfigArray} The current instance. | |
| * @throws {TypeError} If the config is invalid. | |
| */ | |
| normalizeSync(context) { | |
| try { | |
| return super.normalizeSync(context); | |
| } catch (error) { | |
| if (error.name === "ConfigError") { | |
| throw wrapConfigErrorWithDetails( | |
| error, | |
| this[originalLength], | |
| this[baseLength], | |
| ); | |
| } | |
| throw error; | |
| } | |
| } | |
| /* eslint-disable class-methods-use-this -- Desired as instance method */ | |
| /** | |
| * Replaces a config with another config to allow us to put strings | |
| * in the config array that will be replaced by objects before | |
| * normalization. | |
| * @param {Object} config The config to preprocess. | |
| * @returns {Object} The preprocessed config. | |
| */ | |
| [ConfigArraySymbol.preprocessConfig](config) { | |
| /* | |
| * If a config object has `ignores` and no other non-meta fields, then it's an object | |
| * for global ignores. If `shouldIgnore` is false, that object shouldn't apply, | |
| * so we'll remove its `ignores`. | |
| */ | |
| if ( | |
| !this.shouldIgnore && | |
| !this[originalBaseConfig].includes(config) && | |
| config.ignores && | |
| Object.keys(config).filter(key => !META_FIELDS.has(key)).length === | |
| 1 | |
| ) { | |
| /* eslint-disable-next-line no-unused-vars -- need to strip off other keys */ | |
| const { ignores, ...otherKeys } = config; | |
| return otherKeys; | |
| } | |
| return config; | |
| } | |
| /** | |
| * Finalizes the config by replacing plugin references with their objects | |
| * and validating rule option schemas. | |
| * @param {Object} config The config to finalize. | |
| * @returns {Object} The finalized config. | |
| * @throws {TypeError} If the config is invalid. | |
| */ | |
| [ConfigArraySymbol.finalizeConfig](config) { | |
| return new Config(config); | |
| } | |
| /* eslint-enable class-methods-use-this -- Desired as instance method */ | |
| } | |
| exports.FlatConfigArray = FlatConfigArray; | |