Spaces:
Sleeping
Sleeping
| /** | |
| * @fileoverview Compatibility class for flat config. | |
| * @author Nicholas C. Zakas | |
| */ | |
| //----------------------------------------------------------------------------- | |
| // Requirements | |
| //----------------------------------------------------------------------------- | |
| import createDebug from "debug"; | |
| import path from "path"; | |
| import environments from "../conf/environments.js"; | |
| import { ConfigArrayFactory } from "./config-array-factory.js"; | |
| //----------------------------------------------------------------------------- | |
| // Helpers | |
| //----------------------------------------------------------------------------- | |
| /** @typedef {import("../../shared/types").Environment} Environment */ | |
| /** @typedef {import("../../shared/types").Processor} Processor */ | |
| const debug = createDebug("eslintrc:flat-compat"); | |
| const cafactory = Symbol("cafactory"); | |
| /** | |
| * Translates an ESLintRC-style config object into a flag-config-style config | |
| * object. | |
| * @param {Object} eslintrcConfig An ESLintRC-style config object. | |
| * @param {Object} options Options to help translate the config. | |
| * @param {string} options.resolveConfigRelativeTo To the directory to resolve | |
| * configs from. | |
| * @param {string} options.resolvePluginsRelativeTo The directory to resolve | |
| * plugins from. | |
| * @param {ReadOnlyMap<string,Environment>} options.pluginEnvironments A map of plugin environment | |
| * names to objects. | |
| * @param {ReadOnlyMap<string,Processor>} options.pluginProcessors A map of plugin processor | |
| * names to objects. | |
| * @returns {Object} A flag-config-style config object. | |
| */ | |
| function translateESLintRC(eslintrcConfig, { | |
| resolveConfigRelativeTo, | |
| resolvePluginsRelativeTo, | |
| pluginEnvironments, | |
| pluginProcessors | |
| }) { | |
| const flatConfig = {}; | |
| const configs = []; | |
| const languageOptions = {}; | |
| const linterOptions = {}; | |
| const keysToCopy = ["settings", "rules", "processor"]; | |
| const languageOptionsKeysToCopy = ["globals", "parser", "parserOptions"]; | |
| const linterOptionsKeysToCopy = ["noInlineConfig", "reportUnusedDisableDirectives"]; | |
| // copy over simple translations | |
| for (const key of keysToCopy) { | |
| if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") { | |
| flatConfig[key] = eslintrcConfig[key]; | |
| } | |
| } | |
| // copy over languageOptions | |
| for (const key of languageOptionsKeysToCopy) { | |
| if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") { | |
| // create the languageOptions key in the flat config | |
| flatConfig.languageOptions = languageOptions; | |
| if (key === "parser") { | |
| debug(`Resolving parser '${languageOptions[key]}' relative to ${resolveConfigRelativeTo}`); | |
| if (eslintrcConfig[key].error) { | |
| throw eslintrcConfig[key].error; | |
| } | |
| languageOptions[key] = eslintrcConfig[key].definition; | |
| continue; | |
| } | |
| // clone any object values that are in the eslintrc config | |
| if (eslintrcConfig[key] && typeof eslintrcConfig[key] === "object") { | |
| languageOptions[key] = { | |
| ...eslintrcConfig[key] | |
| }; | |
| } else { | |
| languageOptions[key] = eslintrcConfig[key]; | |
| } | |
| } | |
| } | |
| // copy over linterOptions | |
| for (const key of linterOptionsKeysToCopy) { | |
| if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") { | |
| flatConfig.linterOptions = linterOptions; | |
| linterOptions[key] = eslintrcConfig[key]; | |
| } | |
| } | |
| // move ecmaVersion a level up | |
| if (languageOptions.parserOptions) { | |
| if ("ecmaVersion" in languageOptions.parserOptions) { | |
| languageOptions.ecmaVersion = languageOptions.parserOptions.ecmaVersion; | |
| delete languageOptions.parserOptions.ecmaVersion; | |
| } | |
| if ("sourceType" in languageOptions.parserOptions) { | |
| languageOptions.sourceType = languageOptions.parserOptions.sourceType; | |
| delete languageOptions.parserOptions.sourceType; | |
| } | |
| // check to see if we even need parserOptions anymore and remove it if not | |
| if (Object.keys(languageOptions.parserOptions).length === 0) { | |
| delete languageOptions.parserOptions; | |
| } | |
| } | |
| // overrides | |
| if (eslintrcConfig.criteria) { | |
| flatConfig.files = [absoluteFilePath => eslintrcConfig.criteria.test(absoluteFilePath)]; | |
| } | |
| // translate plugins | |
| if (eslintrcConfig.plugins && typeof eslintrcConfig.plugins === "object") { | |
| debug(`Translating plugins: ${eslintrcConfig.plugins}`); | |
| flatConfig.plugins = {}; | |
| for (const pluginName of Object.keys(eslintrcConfig.plugins)) { | |
| debug(`Translating plugin: ${pluginName}`); | |
| debug(`Resolving plugin '${pluginName} relative to ${resolvePluginsRelativeTo}`); | |
| const { definition: plugin, error } = eslintrcConfig.plugins[pluginName]; | |
| if (error) { | |
| throw error; | |
| } | |
| flatConfig.plugins[pluginName] = plugin; | |
| // create a config for any processors | |
| if (plugin.processors) { | |
| for (const processorName of Object.keys(plugin.processors)) { | |
| if (processorName.startsWith(".")) { | |
| debug(`Assigning processor: ${pluginName}/${processorName}`); | |
| configs.unshift({ | |
| files: [`**/*${processorName}`], | |
| processor: pluginProcessors.get(`${pluginName}/${processorName}`) | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // translate env - must come after plugins | |
| if (eslintrcConfig.env && typeof eslintrcConfig.env === "object") { | |
| for (const envName of Object.keys(eslintrcConfig.env)) { | |
| // only add environments that are true | |
| if (eslintrcConfig.env[envName]) { | |
| debug(`Translating environment: ${envName}`); | |
| if (environments.has(envName)) { | |
| // built-in environments should be defined first | |
| configs.unshift(...translateESLintRC({ | |
| criteria: eslintrcConfig.criteria, | |
| ...environments.get(envName) | |
| }, { | |
| resolveConfigRelativeTo, | |
| resolvePluginsRelativeTo | |
| })); | |
| } else if (pluginEnvironments.has(envName)) { | |
| // if the environment comes from a plugin, it should come after the plugin config | |
| configs.push(...translateESLintRC({ | |
| criteria: eslintrcConfig.criteria, | |
| ...pluginEnvironments.get(envName) | |
| }, { | |
| resolveConfigRelativeTo, | |
| resolvePluginsRelativeTo | |
| })); | |
| } | |
| } | |
| } | |
| } | |
| // only add if there are actually keys in the config | |
| if (Object.keys(flatConfig).length > 0) { | |
| configs.push(flatConfig); | |
| } | |
| return configs; | |
| } | |
| //----------------------------------------------------------------------------- | |
| // Exports | |
| //----------------------------------------------------------------------------- | |
| /** | |
| * A compatibility class for working with configs. | |
| */ | |
| class FlatCompat { | |
| constructor({ | |
| baseDirectory = process.cwd(), | |
| resolvePluginsRelativeTo = baseDirectory, | |
| recommendedConfig, | |
| allConfig | |
| } = {}) { | |
| this.baseDirectory = baseDirectory; | |
| this.resolvePluginsRelativeTo = resolvePluginsRelativeTo; | |
| this[cafactory] = new ConfigArrayFactory({ | |
| cwd: baseDirectory, | |
| resolvePluginsRelativeTo, | |
| getEslintAllConfig: () => { | |
| if (!allConfig) { | |
| throw new TypeError("Missing parameter 'allConfig' in FlatCompat constructor."); | |
| } | |
| return allConfig; | |
| }, | |
| getEslintRecommendedConfig: () => { | |
| if (!recommendedConfig) { | |
| throw new TypeError("Missing parameter 'recommendedConfig' in FlatCompat constructor."); | |
| } | |
| return recommendedConfig; | |
| } | |
| }); | |
| } | |
| /** | |
| * Translates an ESLintRC-style config into a flag-config-style config. | |
| * @param {Object} eslintrcConfig The ESLintRC-style config object. | |
| * @returns {Object} A flag-config-style config object. | |
| */ | |
| config(eslintrcConfig) { | |
| const eslintrcArray = this[cafactory].create(eslintrcConfig, { | |
| basePath: this.baseDirectory | |
| }); | |
| const flatArray = []; | |
| let hasIgnorePatterns = false; | |
| eslintrcArray.forEach(configData => { | |
| if (configData.type === "config") { | |
| hasIgnorePatterns = hasIgnorePatterns || configData.ignorePattern; | |
| flatArray.push(...translateESLintRC(configData, { | |
| resolveConfigRelativeTo: path.join(this.baseDirectory, "__placeholder.js"), | |
| resolvePluginsRelativeTo: path.join(this.resolvePluginsRelativeTo, "__placeholder.js"), | |
| pluginEnvironments: eslintrcArray.pluginEnvironments, | |
| pluginProcessors: eslintrcArray.pluginProcessors | |
| })); | |
| } | |
| }); | |
| // combine ignorePatterns to emulate ESLintRC behavior better | |
| if (hasIgnorePatterns) { | |
| flatArray.unshift({ | |
| ignores: [filePath => { | |
| // Compute the final config for this file. | |
| // This filters config array elements by `files`/`excludedFiles` then merges the elements. | |
| const finalConfig = eslintrcArray.extractConfig(filePath); | |
| // Test the `ignorePattern` properties of the final config. | |
| return Boolean(finalConfig.ignores) && finalConfig.ignores(filePath); | |
| }] | |
| }); | |
| } | |
| return flatArray; | |
| } | |
| /** | |
| * Translates the `env` section of an ESLintRC-style config. | |
| * @param {Object} envConfig The `env` section of an ESLintRC config. | |
| * @returns {Object[]} An array of flag-config objects representing the environments. | |
| */ | |
| env(envConfig) { | |
| return this.config({ | |
| env: envConfig | |
| }); | |
| } | |
| /** | |
| * Translates the `extends` section of an ESLintRC-style config. | |
| * @param {...string} configsToExtend The names of the configs to load. | |
| * @returns {Object[]} An array of flag-config objects representing the config. | |
| */ | |
| extends(...configsToExtend) { | |
| return this.config({ | |
| extends: configsToExtend | |
| }); | |
| } | |
| /** | |
| * Translates the `plugins` section of an ESLintRC-style config. | |
| * @param {...string} plugins The names of the plugins to load. | |
| * @returns {Object[]} An array of flag-config objects representing the plugins. | |
| */ | |
| plugins(...plugins) { | |
| return this.config({ | |
| plugins | |
| }); | |
| } | |
| } | |
| export { FlatCompat }; | |