2 * @fileoverview Compatibility class for flat config.
3 * @author Nicholas C. Zakas
8 //-----------------------------------------------------------------------------
10 //-----------------------------------------------------------------------------
12 const path = require("path");
13 const environments = require("../conf/environments");
14 const createDebug = require("debug");
15 const { ConfigArrayFactory } = require("./config-array-factory");
17 //-----------------------------------------------------------------------------
19 //-----------------------------------------------------------------------------
21 /** @typedef {import("../../shared/types").Environment} Environment */
22 /** @typedef {import("../../shared/types").Processor} Processor */
24 const debug = createDebug("eslintrc:flat-compat");
25 const cafactory = Symbol("cafactory");
28 * Translates an ESLintRC-style config object into a flag-config-style config
30 * @param {Object} eslintrcConfig An ESLintRC-style config object.
31 * @param {Object} options Options to help translate the config.
32 * @param {string} options.resolveConfigRelativeTo To the directory to resolve
34 * @param {string} options.resolvePluginsRelativeTo The directory to resolve
36 * @param {ReadOnlyMap<string,Environment>} options.pluginEnvironments A map of plugin environment
38 * @param {ReadOnlyMap<string,Processor>} options.pluginProcessors A map of plugin processor
40 * @returns {Object} A flag-config-style config object.
42 function translateESLintRC(eslintrcConfig, {
43 resolveConfigRelativeTo,
44 resolvePluginsRelativeTo,
49 const flatConfig = {};
51 const languageOptions = {};
52 const linterOptions = {};
53 const keysToCopy = ["settings", "rules", "processor"];
54 const languageOptionsKeysToCopy = ["globals", "parser", "parserOptions"];
55 const linterOptionsKeysToCopy = ["noInlineConfig", "reportUnusedDisableDirectives"];
57 // check for special settings for eslint:all and eslint:recommended:
58 if (eslintrcConfig.settings) {
59 if (eslintrcConfig.settings["eslint:all"] === true) {
60 return ["eslint:all"];
63 if (eslintrcConfig.settings["eslint:recommended"] === true) {
64 return ["eslint:recommended"];
68 // copy over simple translations
69 for (const key of keysToCopy) {
70 if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") {
71 flatConfig[key] = eslintrcConfig[key];
75 // copy over languageOptions
76 for (const key of languageOptionsKeysToCopy) {
77 if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") {
79 // create the languageOptions key in the flat config
80 flatConfig.languageOptions = languageOptions;
82 if (key === "parser") {
83 debug(`Resolving parser '${languageOptions[key]}' relative to ${resolveConfigRelativeTo}`);
85 if (eslintrcConfig[key].error) {
86 throw eslintrcConfig[key].error;
89 languageOptions[key] = eslintrcConfig[key].definition;
93 // clone any object values that are in the eslintrc config
94 if (eslintrcConfig[key] && typeof eslintrcConfig[key] === "object") {
95 languageOptions[key] = {
96 ...eslintrcConfig[key]
99 languageOptions[key] = eslintrcConfig[key];
104 // copy over linterOptions
105 for (const key of linterOptionsKeysToCopy) {
106 if (key in eslintrcConfig && typeof eslintrcConfig[key] !== "undefined") {
107 flatConfig.linterOptions = linterOptions;
108 linterOptions[key] = eslintrcConfig[key];
112 // move ecmaVersion a level up
113 if (languageOptions.parserOptions) {
115 if ("ecmaVersion" in languageOptions.parserOptions) {
116 languageOptions.ecmaVersion = languageOptions.parserOptions.ecmaVersion;
117 delete languageOptions.parserOptions.ecmaVersion;
120 if ("sourceType" in languageOptions.parserOptions) {
121 languageOptions.sourceType = languageOptions.parserOptions.sourceType;
122 delete languageOptions.parserOptions.sourceType;
125 // check to see if we even need parserOptions anymore and remove it if not
126 if (Object.keys(languageOptions.parserOptions).length === 0) {
127 delete languageOptions.parserOptions;
132 if (eslintrcConfig.criteria) {
133 flatConfig.files = [absoluteFilePath => eslintrcConfig.criteria.test(absoluteFilePath)];
137 if (eslintrcConfig.plugins && typeof eslintrcConfig.plugins === "object") {
138 debug(`Translating plugins: ${eslintrcConfig.plugins}`);
140 flatConfig.plugins = {};
142 for (const pluginName of Object.keys(eslintrcConfig.plugins)) {
144 debug(`Translating plugin: ${pluginName}`);
145 debug(`Resolving plugin '${pluginName} relative to ${resolvePluginsRelativeTo}`);
147 const { definition: plugin, error } = eslintrcConfig.plugins[pluginName];
153 flatConfig.plugins[pluginName] = plugin;
155 // create a config for any processors
156 if (plugin.processors) {
157 for (const processorName of Object.keys(plugin.processors)) {
158 if (processorName.startsWith(".")) {
159 debug(`Assigning processor: ${pluginName}/${processorName}`);
162 files: [`**/*${processorName}`],
163 processor: pluginProcessors.get(`${pluginName}/${processorName}`)
172 // translate env - must come after plugins
173 if (eslintrcConfig.env && typeof eslintrcConfig.env === "object") {
174 for (const envName of Object.keys(eslintrcConfig.env)) {
176 // only add environments that are true
177 if (eslintrcConfig.env[envName]) {
178 debug(`Translating environment: ${envName}`);
180 if (environments.has(envName)) {
182 // built-in environments should be defined first
183 configs.unshift(...translateESLintRC(environments.get(envName), {
184 resolveConfigRelativeTo,
185 resolvePluginsRelativeTo
187 } else if (pluginEnvironments.has(envName)) {
189 // if the environment comes from a plugin, it should come after the plugin config
190 configs.push(...translateESLintRC(pluginEnvironments.get(envName), {
191 resolveConfigRelativeTo,
192 resolvePluginsRelativeTo
199 // only add if there are actually keys in the config
200 if (Object.keys(flatConfig).length > 0) {
201 configs.push(flatConfig);
208 //-----------------------------------------------------------------------------
210 //-----------------------------------------------------------------------------
213 * A compatibility class for working with configs.
218 baseDirectory = process.cwd(),
219 resolvePluginsRelativeTo = baseDirectory
221 this.baseDirectory = baseDirectory;
222 this.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
223 this[cafactory] = new ConfigArrayFactory({
225 resolvePluginsRelativeTo,
226 eslintAllPath: path.resolve(__dirname, "../conf/eslint-all.js"),
227 eslintRecommendedPath: path.resolve(__dirname, "../conf/eslint-recommended.js")
232 * Translates an ESLintRC-style config into a flag-config-style config.
233 * @param {Object} eslintrcConfig The ESLintRC-style config object.
234 * @returns {Object} A flag-config-style config object.
236 config(eslintrcConfig) {
237 const eslintrcArray = this[cafactory].create(eslintrcConfig, {
238 basePath: this.baseDirectory
241 const flatArray = [];
242 let hasIgnorePatterns = false;
244 eslintrcArray.forEach(configData => {
245 if (configData.type === "config") {
246 hasIgnorePatterns = hasIgnorePatterns || configData.ignorePattern;
247 flatArray.push(...translateESLintRC(configData, {
248 resolveConfigRelativeTo: path.join(this.baseDirectory, "__placeholder.js"),
249 resolvePluginsRelativeTo: path.join(this.resolvePluginsRelativeTo, "__placeholder.js"),
250 pluginEnvironments: eslintrcArray.pluginEnvironments,
251 pluginProcessors: eslintrcArray.pluginProcessors
256 // combine ignorePatterns to emulate ESLintRC behavior better
257 if (hasIgnorePatterns) {
259 ignores: [filePath => {
261 // Compute the final config for this file.
262 // This filters config array elements by `files`/`excludedFiles` then merges the elements.
263 const finalConfig = eslintrcArray.extractConfig(filePath);
265 // Test the `ignorePattern` properties of the final config.
266 return Boolean(finalConfig.ignores) && finalConfig.ignores(filePath);
275 * Translates the `env` section of an ESLintRC-style config.
276 * @param {Object} envConfig The `env` section of an ESLintRC config.
277 * @returns {Object} A flag-config object representing the environments.
286 * Translates the `extends` section of an ESLintRC-style config.
287 * @param {...string} configsToExtend The names of the configs to load.
288 * @returns {Object} A flag-config object representing the config.
290 extends(...configsToExtend) {
292 extends: configsToExtend
297 * Translates the `plugins` section of an ESLintRC-style config.
298 * @param {...string} plugins The names of the plugins to load.
299 * @returns {Object} A flag-config object representing the plugins.
301 plugins(...plugins) {
308 exports.FlatCompat = FlatCompat;