diff --git a/Makefile.js b/Makefile.js index f5f3fd589ee..840a51a86d7 100644 --- a/Makefile.js +++ b/Makefile.js @@ -550,7 +550,7 @@ target.mocha = () => { errors++; } - lastReturn = exec(`${getBinFile("nyc")} check-coverage --statement 99 --branch 98 --function 99 --lines 99`); + lastReturn = exec(`${getBinFile("nyc")} check-coverage --statement 98 --branch 97 --function 98 --lines 98`); if (lastReturn.code !== 0) { errors++; } diff --git a/lib/cli-engine/cascading-config-array-factory.js b/lib/cli-engine/cascading-config-array-factory.js deleted file mode 100644 index df49c27772a..00000000000 --- a/lib/cli-engine/cascading-config-array-factory.js +++ /dev/null @@ -1,502 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview `CascadingConfigArrayFactory` class. - * - * `CascadingConfigArrayFactory` class has a responsibility: - * - * 1. Handles cascading of config files. - * - * It provides two methods: - * - * - `getConfigArrayForFile(filePath)` - * Get the corresponded configuration of a given file. This method doesn't - * throw even if the given file didn't exist. - * - `clearCache()` - * Clear the internal cache. You have to call this method when - * `additionalPluginPool` was updated if `baseConfig` or `cliConfig` depends - * on the additional plugins. (`CLIEngine#addPlugin()` method calls this.) - * - * @author Toru Nagashima - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const os = require("os"); -const path = require("path"); -const { validateConfigArray } = require("../shared/config-validator"); -const { emitDeprecationWarning } = require("../shared/deprecation-warnings"); -const { ConfigArrayFactory } = require("./config-array-factory"); -const { ConfigArray, ConfigDependency, IgnorePattern } = require("./config-array"); -const loadRules = require("./load-rules"); -const debug = require("debug")("eslint:cascading-config-array-factory"); - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -// Define types for VSCode IntelliSense. -/** @typedef {import("../shared/types").ConfigData} ConfigData */ -/** @typedef {import("../shared/types").Parser} Parser */ -/** @typedef {import("../shared/types").Plugin} Plugin */ -/** @typedef {ReturnType} ConfigArray */ - -/** - * @typedef {Object} CascadingConfigArrayFactoryOptions - * @property {Map} [additionalPluginPool] The map for additional plugins. - * @property {ConfigData} [baseConfig] The config by `baseConfig` option. - * @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. - * @property {string} [cwd] The base directory to start lookup. - * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. - * @property {string[]} [rulePaths] The value of `--rulesdir` option. - * @property {string} [specificConfigPath] The value of `--config` option. - * @property {boolean} [useEslintrc] if `false` then it doesn't load config files. - */ - -/** - * @typedef {Object} CascadingConfigArrayFactoryInternalSlots - * @property {ConfigArray} baseConfigArray The config array of `baseConfig` option. - * @property {ConfigData} baseConfigData The config data of `baseConfig` option. This is used to reset `baseConfigArray`. - * @property {ConfigArray} cliConfigArray The config array of CLI options. - * @property {ConfigData} cliConfigData The config data of CLI options. This is used to reset `cliConfigArray`. - * @property {ConfigArrayFactory} configArrayFactory The factory for config arrays. - * @property {Map} configCache The cache from directory paths to config arrays. - * @property {string} cwd The base directory to start lookup. - * @property {WeakMap} finalizeCache The cache from config arrays to finalized config arrays. - * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. - * @property {string[]|null} rulePaths The value of `--rulesdir` option. This is used to reset `baseConfigArray`. - * @property {string|null} specificConfigPath The value of `--config` option. This is used to reset `cliConfigArray`. - * @property {boolean} useEslintrc if `false` then it doesn't load config files. - */ - -/** @type {WeakMap} */ -const internalSlotsMap = new WeakMap(); - -/** - * Create the config array from `baseConfig` and `rulePaths`. - * @param {CascadingConfigArrayFactoryInternalSlots} slots The slots. - * @returns {ConfigArray} The config array of the base configs. - */ -function createBaseConfigArray({ - configArrayFactory, - baseConfigData, - rulePaths, - cwd -}) { - const baseConfigArray = configArrayFactory.create( - baseConfigData, - { name: "BaseConfig" } - ); - - /* - * Create the config array element for the default ignore patterns. - * This element has `ignorePattern` property that ignores the default - * patterns in the current working directory. - */ - baseConfigArray.unshift(configArrayFactory.create( - { ignorePatterns: IgnorePattern.DefaultPatterns }, - { name: "DefaultIgnorePattern" } - )[0]); - - /* - * Load rules `--rulesdir` option as a pseudo plugin. - * Use a pseudo plugin to define rules of `--rulesdir`, so we can validate - * the rule's options with only information in the config array. - */ - if (rulePaths && rulePaths.length > 0) { - baseConfigArray.push({ - type: "config", - name: "--rulesdir", - filePath: "", - plugins: { - "": new ConfigDependency({ - definition: { - rules: rulePaths.reduce( - (map, rulesPath) => Object.assign( - map, - loadRules(rulesPath, cwd) - ), - {} - ) - }, - filePath: "", - id: "", - importerName: "--rulesdir", - importerPath: "" - }) - } - }); - } - - return baseConfigArray; -} - -/** - * Create the config array from CLI options. - * @param {CascadingConfigArrayFactoryInternalSlots} slots The slots. - * @returns {ConfigArray} The config array of the base configs. - */ -function createCLIConfigArray({ - cliConfigData, - configArrayFactory, - cwd, - ignorePath, - specificConfigPath -}) { - const cliConfigArray = configArrayFactory.create( - cliConfigData, - { name: "CLIOptions" } - ); - - cliConfigArray.unshift( - ...(ignorePath - ? configArrayFactory.loadESLintIgnore(ignorePath) - : configArrayFactory.loadDefaultESLintIgnore()) - ); - - if (specificConfigPath) { - cliConfigArray.unshift( - ...configArrayFactory.loadFile( - specificConfigPath, - { name: "--config", basePath: cwd } - ) - ); - } - - return cliConfigArray; -} - -/** - * The error type when there are files matched by a glob, but all of them have been ignored. - */ -class ConfigurationNotFoundError extends Error { - - // eslint-disable-next-line jsdoc/require-description - /** - * @param {string} directoryPath The directory path. - */ - constructor(directoryPath) { - super(`No ESLint configuration found in ${directoryPath}.`); - this.messageTemplate = "no-config-found"; - this.messageData = { directoryPath }; - } -} - -/** - * This class provides the functionality that enumerates every file which is - * matched by given glob patterns and that configuration. - */ -class CascadingConfigArrayFactory { - - /** - * Initialize this enumerator. - * @param {CascadingConfigArrayFactoryOptions} options The options. - */ - constructor({ - additionalPluginPool = new Map(), - baseConfig: baseConfigData = null, - cliConfig: cliConfigData = null, - cwd = process.cwd(), - ignorePath, - resolvePluginsRelativeTo, - rulePaths = [], - specificConfigPath = null, - useEslintrc = true - } = {}) { - const configArrayFactory = new ConfigArrayFactory({ - additionalPluginPool, - cwd, - resolvePluginsRelativeTo - }); - - internalSlotsMap.set(this, { - baseConfigArray: createBaseConfigArray({ - baseConfigData, - configArrayFactory, - cwd, - rulePaths - }), - baseConfigData, - cliConfigArray: createCLIConfigArray({ - cliConfigData, - configArrayFactory, - cwd, - ignorePath, - specificConfigPath - }), - cliConfigData, - configArrayFactory, - configCache: new Map(), - cwd, - finalizeCache: new WeakMap(), - ignorePath, - rulePaths, - specificConfigPath, - useEslintrc - }); - } - - /** - * The path to the current working directory. - * This is used by tests. - * @type {string} - */ - get cwd() { - const { cwd } = internalSlotsMap.get(this); - - return cwd; - } - - /** - * Get the config array of a given file. - * If `filePath` was not given, it returns the config which contains only - * `baseConfigData` and `cliConfigData`. - * @param {string} [filePath] The file path to a file. - * @param {Object} [options] The options. - * @param {boolean} [options.ignoreNotFoundError] If `true` then it doesn't throw `ConfigurationNotFoundError`. - * @returns {ConfigArray} The config array of the file. - */ - getConfigArrayForFile(filePath, { ignoreNotFoundError = false } = {}) { - const { - baseConfigArray, - cliConfigArray, - cwd - } = internalSlotsMap.get(this); - - if (!filePath) { - return new ConfigArray(...baseConfigArray, ...cliConfigArray); - } - - const directoryPath = path.dirname(path.resolve(cwd, filePath)); - - debug(`Load config files for ${directoryPath}.`); - - return this._finalizeConfigArray( - this._loadConfigInAncestors(directoryPath), - directoryPath, - ignoreNotFoundError - ); - } - - /** - * Set the config data to override all configs. - * Require to call `clearCache()` method after this method is called. - * @param {ConfigData} configData The config data to override all configs. - * @returns {void} - */ - setOverrideConfig(configData) { - const slots = internalSlotsMap.get(this); - - slots.cliConfigData = configData; - } - - /** - * Clear config cache. - * @returns {void} - */ - clearCache() { - const slots = internalSlotsMap.get(this); - - slots.baseConfigArray = createBaseConfigArray(slots); - slots.cliConfigArray = createCLIConfigArray(slots); - slots.configCache.clear(); - } - - /** - * Load and normalize config files from the ancestor directories. - * @param {string} directoryPath The path to a leaf directory. - * @param {boolean} configsExistInSubdirs `true` if configurations exist in subdirectories. - * @returns {ConfigArray} The loaded config. - * @private - */ - _loadConfigInAncestors(directoryPath, configsExistInSubdirs = false) { - const { - baseConfigArray, - configArrayFactory, - configCache, - cwd, - useEslintrc - } = internalSlotsMap.get(this); - - if (!useEslintrc) { - return baseConfigArray; - } - - let configArray = configCache.get(directoryPath); - - // Hit cache. - if (configArray) { - debug(`Cache hit: ${directoryPath}.`); - return configArray; - } - debug(`No cache found: ${directoryPath}.`); - - const homePath = os.homedir(); - - // Consider this is root. - if (directoryPath === homePath && cwd !== homePath) { - debug("Stop traversing because of considered root."); - if (configsExistInSubdirs) { - const filePath = ConfigArrayFactory.getPathToConfigFileInDirectory(directoryPath); - - if (filePath) { - emitDeprecationWarning( - filePath, - "ESLINT_PERSONAL_CONFIG_SUPPRESS" - ); - } - } - return this._cacheConfig(directoryPath, baseConfigArray); - } - - // Load the config on this directory. - try { - configArray = configArrayFactory.loadInDirectory(directoryPath); - } catch (error) { - /* istanbul ignore next */ - if (error.code === "EACCES") { - debug("Stop traversing because of 'EACCES' error."); - return this._cacheConfig(directoryPath, baseConfigArray); - } - throw error; - } - - if (configArray.length > 0 && configArray.isRoot()) { - debug("Stop traversing because of 'root:true'."); - configArray.unshift(...baseConfigArray); - return this._cacheConfig(directoryPath, configArray); - } - - // Load from the ancestors and merge it. - const parentPath = path.dirname(directoryPath); - const parentConfigArray = parentPath && parentPath !== directoryPath - ? this._loadConfigInAncestors( - parentPath, - configsExistInSubdirs || configArray.length > 0 - ) - : baseConfigArray; - - if (configArray.length > 0) { - configArray.unshift(...parentConfigArray); - } else { - configArray = parentConfigArray; - } - - // Cache and return. - return this._cacheConfig(directoryPath, configArray); - } - - /** - * Freeze and cache a given config. - * @param {string} directoryPath The path to a directory as a cache key. - * @param {ConfigArray} configArray The config array as a cache value. - * @returns {ConfigArray} The `configArray` (frozen). - */ - _cacheConfig(directoryPath, configArray) { - const { configCache } = internalSlotsMap.get(this); - - Object.freeze(configArray); - configCache.set(directoryPath, configArray); - - return configArray; - } - - /** - * Finalize a given config array. - * Concatenate `--config` and other CLI options. - * @param {ConfigArray} configArray The parent config array. - * @param {string} directoryPath The path to the leaf directory to find config files. - * @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`. - * @returns {ConfigArray} The loaded config. - * @private - */ - _finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) { - const { - cliConfigArray, - configArrayFactory, - finalizeCache, - useEslintrc - } = internalSlotsMap.get(this); - - let finalConfigArray = finalizeCache.get(configArray); - - if (!finalConfigArray) { - finalConfigArray = configArray; - - // Load the personal config if there are no regular config files. - if ( - useEslintrc && - configArray.every(c => !c.filePath) && - cliConfigArray.every(c => !c.filePath) // `--config` option can be a file. - ) { - const homePath = os.homedir(); - - debug("Loading the config file of the home directory:", homePath); - - const personalConfigArray = configArrayFactory.loadInDirectory( - homePath, - { name: "PersonalConfig" } - ); - - if ( - personalConfigArray.length > 0 && - !directoryPath.startsWith(homePath) - ) { - const lastElement = - personalConfigArray[personalConfigArray.length - 1]; - - emitDeprecationWarning( - lastElement.filePath, - "ESLINT_PERSONAL_CONFIG_LOAD" - ); - } - - finalConfigArray = finalConfigArray.concat(personalConfigArray); - } - - // Apply CLI options. - if (cliConfigArray.length > 0) { - finalConfigArray = finalConfigArray.concat(cliConfigArray); - } - - // Validate rule settings and environments. - validateConfigArray(finalConfigArray); - - // Cache it. - Object.freeze(finalConfigArray); - finalizeCache.set(configArray, finalConfigArray); - - debug( - "Configuration was determined: %o on %s", - finalConfigArray, - directoryPath - ); - } - - // At least one element (the default ignore patterns) exists. - if (!ignoreNotFoundError && useEslintrc && finalConfigArray.length <= 1) { - throw new ConfigurationNotFoundError(directoryPath); - } - - return finalConfigArray; - } -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -module.exports = { CascadingConfigArrayFactory }; diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js index 70c6f6f39f7..9a414061501 100644 --- a/lib/cli-engine/cli-engine.js +++ b/lib/cli-engine/cli-engine.js @@ -19,14 +19,29 @@ const fs = require("fs"); const path = require("path"); const defaultOptions = require("../../conf/default-cli-options"); const pkg = require("../../package.json"); -const ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"); -const naming = require("@eslint/eslintrc/lib/shared/naming"); -const ModuleResolver = require("../shared/relative-module-resolver"); + + +const { + Legacy: { + ConfigOps, + naming, + CascadingConfigArrayFactory, + IgnorePattern, + getUsedExtractedConfigs + } +} = require("@eslint/eslintrc"); + +/* + * For some reason, ModuleResolver must be included via filepath instead of by + * API exports in order to work properly. That's why this is separated out onto + * its own require() statement. + */ +const ModuleResolver = require("@eslint/eslintrc/lib/shared/relative-module-resolver"); +const { FileEnumerator } = require("./file-enumerator"); + const { Linter } = require("../linter"); const builtInRules = require("../rules"); -const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array"); -const { FileEnumerator } = require("./file-enumerator"); +const loadRules = require("./load-rules"); const hash = require("./hash"); const LintResultCache = require("./lint-result-cache"); @@ -559,7 +574,11 @@ class CLIEngine { resolvePluginsRelativeTo: options.resolvePluginsRelativeTo, rulePaths: options.rulePaths, specificConfigPath: options.configFile, - useEslintrc: options.useEslintrc + useEslintrc: options.useEslintrc, + builtInRules, + loadRules, + eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"), + eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js") }); const fileEnumerator = new FileEnumerator({ configArrayFactory, diff --git a/lib/cli-engine/config-array-factory.js b/lib/cli-engine/config-array-factory.js deleted file mode 100644 index 2c7a79b491e..00000000000 --- a/lib/cli-engine/config-array-factory.js +++ /dev/null @@ -1,1092 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview The factory of `ConfigArray` objects. - * - * This class provides methods to create `ConfigArray` instance. - * - * - `create(configData, options)` - * Create a `ConfigArray` instance from a config data. This is to handle CLI - * options except `--config`. - * - `loadFile(filePath, options)` - * Create a `ConfigArray` instance from a config file. This is to handle - * `--config` option. If the file was not found, throws the following error: - * - If the filename was `*.js`, a `MODULE_NOT_FOUND` error. - * - If the filename was `package.json`, an IO error or an - * `ESLINT_CONFIG_FIELD_NOT_FOUND` error. - * - Otherwise, an IO error such as `ENOENT`. - * - `loadInDirectory(directoryPath, options)` - * Create a `ConfigArray` instance from a config file which is on a given - * directory. This tries to load `.eslintrc.*` or `package.json`. If not - * found, returns an empty `ConfigArray`. - * - `loadESLintIgnore(filePath)` - * Create a `ConfigArray` instance from a config file that is `.eslintignore` - * format. This is to handle `--ignore-path` option. - * - `loadDefaultESLintIgnore()` - * Create a `ConfigArray` instance from `.eslintignore` or `package.json` in - * the current working directory. - * - * `ConfigArrayFactory` class has the responsibility that loads configuration - * files, including loading `extends`, `parser`, and `plugins`. The created - * `ConfigArray` instance has the loaded `extends`, `parser`, and `plugins`. - * - * But this class doesn't handle cascading. `CascadingConfigArrayFactory` class - * handles cascading and hierarchy. - * - * @author Toru Nagashima - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const fs = require("fs"); -const path = require("path"); -const importFresh = require("import-fresh"); -const stripComments = require("strip-json-comments"); -const { validateConfigSchema } = require("../shared/config-validator"); -const naming = require("@eslint/eslintrc/lib/shared/naming"); -const ModuleResolver = require("../shared/relative-module-resolver"); -const { - ConfigArray, - ConfigDependency, - IgnorePattern, - OverrideTester -} = require("./config-array"); -const debug = require("debug")("eslint:config-array-factory"); - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -const eslintRecommendedPath = path.resolve(__dirname, "../../conf/eslint-recommended.js"); -const eslintAllPath = path.resolve(__dirname, "../../conf/eslint-all.js"); -const configFilenames = [ - ".eslintrc.js", - ".eslintrc.cjs", - ".eslintrc.yaml", - ".eslintrc.yml", - ".eslintrc.json", - ".eslintrc", - "package.json" -]; - -// Define types for VSCode IntelliSense. -/** @typedef {import("../shared/types").ConfigData} ConfigData */ -/** @typedef {import("../shared/types").OverrideConfigData} OverrideConfigData */ -/** @typedef {import("../shared/types").Parser} Parser */ -/** @typedef {import("../shared/types").Plugin} Plugin */ -/** @typedef {import("./config-array/config-dependency").DependentParser} DependentParser */ -/** @typedef {import("./config-array/config-dependency").DependentPlugin} DependentPlugin */ -/** @typedef {ConfigArray[0]} ConfigArrayElement */ - -/** - * @typedef {Object} ConfigArrayFactoryOptions - * @property {Map} [additionalPluginPool] The map for additional plugins. - * @property {string} [cwd] The path to the current working directory. - * @property {string} [resolvePluginsRelativeTo] A path to the directory that plugins should be resolved from. Defaults to `cwd`. - */ - -/** - * @typedef {Object} ConfigArrayFactoryInternalSlots - * @property {Map} additionalPluginPool The map for additional plugins. - * @property {string} cwd The path to the current working directory. - * @property {string | undefined} resolvePluginsRelativeTo An absolute path the the directory that plugins should be resolved from. - */ - -/** - * @typedef {Object} ConfigArrayFactoryLoadingContext - * @property {string} filePath The path to the current configuration. - * @property {string} matchBasePath The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`. - * @property {string} name The name of the current configuration. - * @property {string} pluginBasePath The base path to resolve plugins. - * @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. - */ - -/** - * @typedef {Object} ConfigArrayFactoryLoadingContext - * @property {string} filePath The path to the current configuration. - * @property {string} matchBasePath The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`. - * @property {string} name The name of the current configuration. - * @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. - */ - -/** @type {WeakMap} */ -const internalSlotsMap = new WeakMap(); - -/** - * Check if a given string is a file path. - * @param {string} nameOrPath A module name or file path. - * @returns {boolean} `true` if the `nameOrPath` is a file path. - */ -function isFilePath(nameOrPath) { - return ( - /^\.{1,2}[/\\]/u.test(nameOrPath) || - path.isAbsolute(nameOrPath) - ); -} - -/** - * Convenience wrapper for synchronously reading file contents. - * @param {string} filePath The filename to read. - * @returns {string} The file contents, with the BOM removed. - * @private - */ -function readFile(filePath) { - return fs.readFileSync(filePath, "utf8").replace(/^\ufeff/u, ""); -} - -/** - * Loads a YAML configuration from a file. - * @param {string} filePath The filename to load. - * @returns {ConfigData} The configuration object from the file. - * @throws {Error} If the file cannot be read. - * @private - */ -function loadYAMLConfigFile(filePath) { - debug(`Loading YAML config file: ${filePath}`); - - // lazy load YAML to improve performance when not used - const yaml = require("js-yaml"); - - try { - - // empty YAML file can be null, so always use - return yaml.safeLoad(readFile(filePath)) || {}; - } catch (e) { - debug(`Error reading YAML file: ${filePath}`); - e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`; - throw e; - } -} - -/** - * Loads a JSON configuration from a file. - * @param {string} filePath The filename to load. - * @returns {ConfigData} The configuration object from the file. - * @throws {Error} If the file cannot be read. - * @private - */ -function loadJSONConfigFile(filePath) { - debug(`Loading JSON config file: ${filePath}`); - - try { - return JSON.parse(stripComments(readFile(filePath))); - } catch (e) { - debug(`Error reading JSON file: ${filePath}`); - e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`; - e.messageTemplate = "failed-to-read-json"; - e.messageData = { - path: filePath, - message: e.message - }; - throw e; - } -} - -/** - * Loads a legacy (.eslintrc) configuration from a file. - * @param {string} filePath The filename to load. - * @returns {ConfigData} The configuration object from the file. - * @throws {Error} If the file cannot be read. - * @private - */ -function loadLegacyConfigFile(filePath) { - debug(`Loading legacy config file: ${filePath}`); - - // lazy load YAML to improve performance when not used - const yaml = require("js-yaml"); - - try { - return yaml.safeLoad(stripComments(readFile(filePath))) || /* istanbul ignore next */ {}; - } catch (e) { - debug("Error reading YAML file: %s\n%o", filePath, e); - e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`; - throw e; - } -} - -/** - * Loads a JavaScript configuration from a file. - * @param {string} filePath The filename to load. - * @returns {ConfigData} The configuration object from the file. - * @throws {Error} If the file cannot be read. - * @private - */ -function loadJSConfigFile(filePath) { - debug(`Loading JS config file: ${filePath}`); - try { - return importFresh(filePath); - } catch (e) { - debug(`Error reading JavaScript file: ${filePath}`); - e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`; - throw e; - } -} - -/** - * Loads a configuration from a package.json file. - * @param {string} filePath The filename to load. - * @returns {ConfigData} The configuration object from the file. - * @throws {Error} If the file cannot be read. - * @private - */ -function loadPackageJSONConfigFile(filePath) { - debug(`Loading package.json config file: ${filePath}`); - try { - const packageData = loadJSONConfigFile(filePath); - - if (!Object.hasOwnProperty.call(packageData, "eslintConfig")) { - throw Object.assign( - new Error("package.json file doesn't have 'eslintConfig' field."), - { code: "ESLINT_CONFIG_FIELD_NOT_FOUND" } - ); - } - - return packageData.eslintConfig; - } catch (e) { - debug(`Error reading package.json file: ${filePath}`); - e.message = `Cannot read config file: ${filePath}\nError: ${e.message}`; - throw e; - } -} - -/** - * Loads a `.eslintignore` from a file. - * @param {string} filePath The filename to load. - * @returns {string[]} The ignore patterns from the file. - * @private - */ -function loadESLintIgnoreFile(filePath) { - debug(`Loading .eslintignore file: ${filePath}`); - - try { - return readFile(filePath) - .split(/\r?\n/gu) - .filter(line => line.trim() !== "" && !line.startsWith("#")); - } catch (e) { - debug(`Error reading .eslintignore file: ${filePath}`); - e.message = `Cannot read .eslintignore file: ${filePath}\nError: ${e.message}`; - throw e; - } -} - -/** - * Creates an error to notify about a missing config to extend from. - * @param {string} configName The name of the missing config. - * @param {string} importerName The name of the config that imported the missing config - * @param {string} messageTemplate The text template to source error strings from. - * @returns {Error} The error object to throw - * @private - */ -function configInvalidError(configName, importerName, messageTemplate) { - return Object.assign( - new Error(`Failed to load config "${configName}" to extend from.`), - { - messageTemplate, - messageData: { configName, importerName } - } - ); -} - -/** - * Loads a configuration file regardless of the source. Inspects the file path - * to determine the correctly way to load the config file. - * @param {string} filePath The path to the configuration. - * @returns {ConfigData|null} The configuration information. - * @private - */ -function loadConfigFile(filePath) { - switch (path.extname(filePath)) { - case ".js": - case ".cjs": - return loadJSConfigFile(filePath); - - case ".json": - if (path.basename(filePath) === "package.json") { - return loadPackageJSONConfigFile(filePath); - } - return loadJSONConfigFile(filePath); - - case ".yaml": - case ".yml": - return loadYAMLConfigFile(filePath); - - default: - return loadLegacyConfigFile(filePath); - } -} - -/** - * Write debug log. - * @param {string} request The requested module name. - * @param {string} relativeTo The file path to resolve the request relative to. - * @param {string} filePath The resolved file path. - * @returns {void} - */ -function writeDebugLogForLoading(request, relativeTo, filePath) { - /* istanbul ignore next */ - if (debug.enabled) { - let nameAndVersion = null; - - try { - const packageJsonPath = ModuleResolver.resolve( - `${request}/package.json`, - relativeTo - ); - const { version = "unknown" } = require(packageJsonPath); - - nameAndVersion = `${request}@${version}`; - } catch (error) { - debug("package.json was not found:", error.message); - nameAndVersion = request; - } - - debug("Loaded: %s (%s)", nameAndVersion, filePath); - } -} - -/** - * Create a new context with default values. - * @param {ConfigArrayFactoryInternalSlots} slots The internal slots. - * @param {"config" | "ignore" | "implicit-processor" | undefined} providedType The type of the current configuration. Default is `"config"`. - * @param {string | undefined} providedName The name of the current configuration. Default is the relative path from `cwd` to `filePath`. - * @param {string | undefined} providedFilePath The path to the current configuration. Default is empty string. - * @param {string | undefined} providedMatchBasePath The type of the current configuration. Default is the directory of `filePath` or `cwd`. - * @returns {ConfigArrayFactoryLoadingContext} The created context. - */ -function createContext( - { cwd, resolvePluginsRelativeTo }, - providedType, - providedName, - providedFilePath, - providedMatchBasePath -) { - const filePath = providedFilePath - ? path.resolve(cwd, providedFilePath) - : ""; - const matchBasePath = - (providedMatchBasePath && path.resolve(cwd, providedMatchBasePath)) || - (filePath && path.dirname(filePath)) || - cwd; - const name = - providedName || - (filePath && path.relative(cwd, filePath)) || - ""; - const pluginBasePath = - resolvePluginsRelativeTo || - (filePath && path.dirname(filePath)) || - cwd; - const type = providedType || "config"; - - return { filePath, matchBasePath, name, pluginBasePath, type }; -} - -/** - * Normalize a given plugin. - * - Ensure the object to have four properties: configs, environments, processors, and rules. - * - Ensure the object to not have other properties. - * @param {Plugin} plugin The plugin to normalize. - * @returns {Plugin} The normalized plugin. - */ -function normalizePlugin(plugin) { - return { - configs: plugin.configs || {}, - environments: plugin.environments || {}, - processors: plugin.processors || {}, - rules: plugin.rules || {} - }; -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -/** - * The factory of `ConfigArray` objects. - */ -class ConfigArrayFactory { - - /** - * Initialize this instance. - * @param {ConfigArrayFactoryOptions} [options] The map for additional plugins. - */ - constructor({ - additionalPluginPool = new Map(), - cwd = process.cwd(), - resolvePluginsRelativeTo - } = {}) { - internalSlotsMap.set(this, { - additionalPluginPool, - cwd, - resolvePluginsRelativeTo: - resolvePluginsRelativeTo && - path.resolve(cwd, resolvePluginsRelativeTo) - }); - } - - /** - * Create `ConfigArray` instance from a config data. - * @param {ConfigData|null} configData The config data to create. - * @param {Object} [options] The options. - * @param {string} [options.basePath] The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`. - * @param {string} [options.filePath] The path to this config data. - * @param {string} [options.name] The config name. - * @returns {ConfigArray} Loaded config. - */ - create(configData, { basePath, filePath, name } = {}) { - if (!configData) { - return new ConfigArray(); - } - - const slots = internalSlotsMap.get(this); - const ctx = createContext(slots, "config", name, filePath, basePath); - const elements = this._normalizeConfigData(configData, ctx); - - return new ConfigArray(...elements); - } - - /** - * Load a config file. - * @param {string} filePath The path to a config file. - * @param {Object} [options] The options. - * @param {string} [options.basePath] The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`. - * @param {string} [options.name] The config name. - * @returns {ConfigArray} Loaded config. - */ - loadFile(filePath, { basePath, name } = {}) { - const slots = internalSlotsMap.get(this); - const ctx = createContext(slots, "config", name, filePath, basePath); - - return new ConfigArray(...this._loadConfigData(ctx)); - } - - /** - * Load the config file on a given directory if exists. - * @param {string} directoryPath The path to a directory. - * @param {Object} [options] The options. - * @param {string} [options.basePath] The base path to resolve relative paths in `overrides[].files`, `overrides[].excludedFiles`, and `ignorePatterns`. - * @param {string} [options.name] The config name. - * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. - */ - loadInDirectory(directoryPath, { basePath, name } = {}) { - const slots = internalSlotsMap.get(this); - - for (const filename of configFilenames) { - const ctx = createContext( - slots, - "config", - name, - path.join(directoryPath, filename), - basePath - ); - - if (fs.existsSync(ctx.filePath)) { - let configData; - - try { - configData = loadConfigFile(ctx.filePath); - } catch (error) { - if (!error || error.code !== "ESLINT_CONFIG_FIELD_NOT_FOUND") { - throw error; - } - } - - if (configData) { - debug(`Config file found: ${ctx.filePath}`); - return new ConfigArray( - ...this._normalizeConfigData(configData, ctx) - ); - } - } - } - - debug(`Config file not found on ${directoryPath}`); - return new ConfigArray(); - } - - /** - * Check if a config file on a given directory exists or not. - * @param {string} directoryPath The path to a directory. - * @returns {string | null} The path to the found config file. If not found then null. - */ - static getPathToConfigFileInDirectory(directoryPath) { - for (const filename of configFilenames) { - const filePath = path.join(directoryPath, filename); - - if (fs.existsSync(filePath)) { - if (filename === "package.json") { - try { - loadPackageJSONConfigFile(filePath); - return filePath; - } catch { /* ignore */ } - } else { - return filePath; - } - } - } - return null; - } - - /** - * Load `.eslintignore` file. - * @param {string} filePath The path to a `.eslintignore` file to load. - * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. - */ - loadESLintIgnore(filePath) { - const slots = internalSlotsMap.get(this); - const ctx = createContext( - slots, - "ignore", - void 0, - filePath, - slots.cwd - ); - const ignorePatterns = loadESLintIgnoreFile(ctx.filePath); - - return new ConfigArray( - ...this._normalizeESLintIgnoreData(ignorePatterns, ctx) - ); - } - - /** - * Load `.eslintignore` file in the current working directory. - * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. - */ - loadDefaultESLintIgnore() { - const slots = internalSlotsMap.get(this); - const eslintIgnorePath = path.resolve(slots.cwd, ".eslintignore"); - const packageJsonPath = path.resolve(slots.cwd, "package.json"); - - if (fs.existsSync(eslintIgnorePath)) { - return this.loadESLintIgnore(eslintIgnorePath); - } - if (fs.existsSync(packageJsonPath)) { - const data = loadJSONConfigFile(packageJsonPath); - - if (Object.hasOwnProperty.call(data, "eslintIgnore")) { - if (!Array.isArray(data.eslintIgnore)) { - throw new Error("Package.json eslintIgnore property requires an array of paths"); - } - const ctx = createContext( - slots, - "ignore", - "eslintIgnore in package.json", - packageJsonPath, - slots.cwd - ); - - return new ConfigArray( - ...this._normalizeESLintIgnoreData(data.eslintIgnore, ctx) - ); - } - } - - return new ConfigArray(); - } - - /** - * Load a given config file. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} Loaded config. - * @private - */ - _loadConfigData(ctx) { - return this._normalizeConfigData(loadConfigFile(ctx.filePath), ctx); - } - - /** - * Normalize a given `.eslintignore` data to config array elements. - * @param {string[]} ignorePatterns The patterns to ignore files. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - *_normalizeESLintIgnoreData(ignorePatterns, ctx) { - const elements = this._normalizeObjectConfigData( - { ignorePatterns }, - ctx - ); - - // Set `ignorePattern.loose` flag for backward compatibility. - for (const element of elements) { - if (element.ignorePattern) { - element.ignorePattern.loose = true; - } - yield element; - } - } - - /** - * Normalize a given config to an array. - * @param {ConfigData} configData The config data to normalize. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - _normalizeConfigData(configData, ctx) { - validateConfigSchema(configData, ctx.name || ctx.filePath); - return this._normalizeObjectConfigData(configData, ctx); - } - - /** - * Normalize a given config to an array. - * @param {ConfigData|OverrideConfigData} configData The config data to normalize. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - *_normalizeObjectConfigData(configData, ctx) { - const { files, excludedFiles, ...configBody } = configData; - const criteria = OverrideTester.create( - files, - excludedFiles, - ctx.matchBasePath - ); - const elements = this._normalizeObjectConfigDataBody(configBody, ctx); - - // Apply the criteria to every element. - for (const element of elements) { - - /* - * Merge the criteria. - * This is for the `overrides` entries that came from the - * configurations of `overrides[].extends`. - */ - element.criteria = OverrideTester.and(criteria, element.criteria); - - /* - * Remove `root` property to ignore `root` settings which came from - * `extends` in `overrides`. - */ - if (element.criteria) { - element.root = void 0; - } - - yield element; - } - } - - /** - * Normalize a given config to an array. - * @param {ConfigData} configData The config data to normalize. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - *_normalizeObjectConfigDataBody( - { - env, - extends: extend, - globals, - ignorePatterns, - noInlineConfig, - parser: parserName, - parserOptions, - plugins: pluginList, - processor, - reportUnusedDisableDirectives, - root, - rules, - settings, - overrides: overrideList = [] - }, - ctx - ) { - const extendList = Array.isArray(extend) ? extend : [extend]; - const ignorePattern = ignorePatterns && new IgnorePattern( - Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns], - ctx.matchBasePath - ); - - // Flatten `extends`. - for (const extendName of extendList.filter(Boolean)) { - yield* this._loadExtends(extendName, ctx); - } - - // Load parser & plugins. - const parser = parserName && this._loadParser(parserName, ctx); - const plugins = pluginList && this._loadPlugins(pluginList, ctx); - - // Yield pseudo config data for file extension processors. - if (plugins) { - yield* this._takeFileExtensionProcessors(plugins, ctx); - } - - // Yield the config data except `extends` and `overrides`. - yield { - - // Debug information. - type: ctx.type, - name: ctx.name, - filePath: ctx.filePath, - - // Config data. - criteria: null, - env, - globals, - ignorePattern, - noInlineConfig, - parser, - parserOptions, - plugins, - processor, - reportUnusedDisableDirectives, - root, - rules, - settings - }; - - // Flatten `overries`. - for (let i = 0; i < overrideList.length; ++i) { - yield* this._normalizeObjectConfigData( - overrideList[i], - { ...ctx, name: `${ctx.name}#overrides[${i}]` } - ); - } - } - - /** - * Load configs of an element in `extends`. - * @param {string} extendName The name of a base config. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - _loadExtends(extendName, ctx) { - debug("Loading {extends:%j} relative to %s", extendName, ctx.filePath); - try { - if (extendName.startsWith("eslint:")) { - return this._loadExtendedBuiltInConfig(extendName, ctx); - } - if (extendName.startsWith("plugin:")) { - return this._loadExtendedPluginConfig(extendName, ctx); - } - return this._loadExtendedShareableConfig(extendName, ctx); - } catch (error) { - error.message += `\nReferenced from: ${ctx.filePath || ctx.name}`; - throw error; - } - } - - /** - * Load configs of an element in `extends`. - * @param {string} extendName The name of a base config. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - _loadExtendedBuiltInConfig(extendName, ctx) { - if (extendName === "eslint:recommended") { - return this._loadConfigData({ - ...ctx, - filePath: eslintRecommendedPath, - name: `${ctx.name} » ${extendName}` - }); - } - if (extendName === "eslint:all") { - return this._loadConfigData({ - ...ctx, - filePath: eslintAllPath, - name: `${ctx.name} » ${extendName}` - }); - } - - throw configInvalidError(extendName, ctx.name, "extend-config-missing"); - } - - /** - * Load configs of an element in `extends`. - * @param {string} extendName The name of a base config. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - _loadExtendedPluginConfig(extendName, ctx) { - const slashIndex = extendName.lastIndexOf("/"); - - if (slashIndex === -1) { - throw configInvalidError(extendName, ctx.filePath, "plugin-invalid"); - } - - const pluginName = extendName.slice("plugin:".length, slashIndex); - const configName = extendName.slice(slashIndex + 1); - - if (isFilePath(pluginName)) { - throw new Error("'extends' cannot use a file path for plugins."); - } - - const plugin = this._loadPlugin(pluginName, ctx); - const configData = - plugin.definition && - plugin.definition.configs[configName]; - - if (configData) { - return this._normalizeConfigData(configData, { - ...ctx, - filePath: plugin.filePath || ctx.filePath, - name: `${ctx.name} » plugin:${plugin.id}/${configName}` - }); - } - - throw plugin.error || configInvalidError(extendName, ctx.filePath, "extend-config-missing"); - } - - /** - * Load configs of an element in `extends`. - * @param {string} extendName The name of a base config. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The normalized config. - * @private - */ - _loadExtendedShareableConfig(extendName, ctx) { - const { cwd } = internalSlotsMap.get(this); - const relativeTo = ctx.filePath || path.join(cwd, "__placeholder__.js"); - let request; - - if (isFilePath(extendName)) { - request = extendName; - } else if (extendName.startsWith(".")) { - request = `./${extendName}`; // For backward compatibility. A ton of tests depended on this behavior. - } else { - request = naming.normalizePackageName( - extendName, - "eslint-config" - ); - } - - let filePath; - - try { - filePath = ModuleResolver.resolve(request, relativeTo); - } catch (error) { - /* istanbul ignore else */ - if (error && error.code === "MODULE_NOT_FOUND") { - throw configInvalidError(extendName, ctx.filePath, "extend-config-missing"); - } - throw error; - } - - writeDebugLogForLoading(request, relativeTo, filePath); - return this._loadConfigData({ - ...ctx, - filePath, - name: `${ctx.name} » ${request}` - }); - } - - /** - * Load given plugins. - * @param {string[]} names The plugin names to load. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {Record} The loaded parser. - * @private - */ - _loadPlugins(names, ctx) { - return names.reduce((map, name) => { - if (isFilePath(name)) { - throw new Error("Plugins array cannot includes file paths."); - } - const plugin = this._loadPlugin(name, ctx); - - map[plugin.id] = plugin; - - return map; - }, {}); - } - - /** - * Load a given parser. - * @param {string} nameOrPath The package name or the path to a parser file. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {DependentParser} The loaded parser. - */ - _loadParser(nameOrPath, ctx) { - debug("Loading parser %j from %s", nameOrPath, ctx.filePath); - - const { cwd } = internalSlotsMap.get(this); - const relativeTo = ctx.filePath || path.join(cwd, "__placeholder__.js"); - - try { - const filePath = ModuleResolver.resolve(nameOrPath, relativeTo); - - writeDebugLogForLoading(nameOrPath, relativeTo, filePath); - - return new ConfigDependency({ - definition: require(filePath), - filePath, - id: nameOrPath, - importerName: ctx.name, - importerPath: ctx.filePath - }); - } catch (error) { - - // If the parser name is "espree", load the espree of ESLint. - if (nameOrPath === "espree") { - debug("Fallback espree."); - return new ConfigDependency({ - definition: require("espree"), - filePath: require.resolve("espree"), - id: nameOrPath, - importerName: ctx.name, - importerPath: ctx.filePath - }); - } - - debug("Failed to load parser '%s' declared in '%s'.", nameOrPath, ctx.name); - error.message = `Failed to load parser '${nameOrPath}' declared in '${ctx.name}': ${error.message}`; - - return new ConfigDependency({ - error, - id: nameOrPath, - importerName: ctx.name, - importerPath: ctx.filePath - }); - } - } - - /** - * Load a given plugin. - * @param {string} name The plugin name to load. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {DependentPlugin} The loaded plugin. - * @private - */ - _loadPlugin(name, ctx) { - debug("Loading plugin %j from %s", name, ctx.filePath); - - const { additionalPluginPool } = internalSlotsMap.get(this); - const request = naming.normalizePackageName(name, "eslint-plugin"); - const id = naming.getShorthandName(request, "eslint-plugin"); - const relativeTo = path.join(ctx.pluginBasePath, "__placeholder__.js"); - - if (name.match(/\s+/u)) { - const error = Object.assign( - new Error(`Whitespace found in plugin name '${name}'`), - { - messageTemplate: "whitespace-found", - messageData: { pluginName: request } - } - ); - - return new ConfigDependency({ - error, - id, - importerName: ctx.name, - importerPath: ctx.filePath - }); - } - - // Check for additional pool. - const plugin = - additionalPluginPool.get(request) || - additionalPluginPool.get(id); - - if (plugin) { - return new ConfigDependency({ - definition: normalizePlugin(plugin), - filePath: "", // It's unknown where the plugin came from. - id, - importerName: ctx.name, - importerPath: ctx.filePath - }); - } - - let filePath; - let error; - - try { - filePath = ModuleResolver.resolve(request, relativeTo); - } catch (resolveError) { - error = resolveError; - /* istanbul ignore else */ - if (error && error.code === "MODULE_NOT_FOUND") { - error.messageTemplate = "plugin-missing"; - error.messageData = { - pluginName: request, - resolvePluginsRelativeTo: ctx.pluginBasePath, - importerName: ctx.name - }; - } - } - - if (filePath) { - try { - writeDebugLogForLoading(request, relativeTo, filePath); - - const startTime = Date.now(); - const pluginDefinition = require(filePath); - - debug(`Plugin ${filePath} loaded in: ${Date.now() - startTime}ms`); - - return new ConfigDependency({ - definition: normalizePlugin(pluginDefinition), - filePath, - id, - importerName: ctx.name, - importerPath: ctx.filePath - }); - } catch (loadError) { - error = loadError; - } - } - - debug("Failed to load plugin '%s' declared in '%s'.", name, ctx.name); - error.message = `Failed to load plugin '${name}' declared in '${ctx.name}': ${error.message}`; - return new ConfigDependency({ - error, - id, - importerName: ctx.name, - importerPath: ctx.filePath - }); - } - - /** - * Take file expression processors as config array elements. - * @param {Record} plugins The plugin definitions. - * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. - * @returns {IterableIterator} The config array elements of file expression processors. - * @private - */ - *_takeFileExtensionProcessors(plugins, ctx) { - for (const pluginId of Object.keys(plugins)) { - const processors = - plugins[pluginId] && - plugins[pluginId].definition && - plugins[pluginId].definition.processors; - - if (!processors) { - continue; - } - - for (const processorId of Object.keys(processors)) { - if (processorId.startsWith(".")) { - yield* this._normalizeObjectConfigData( - { - files: [`*${processorId}`], - processor: `${pluginId}/${processorId}` - }, - { - ...ctx, - type: "implicit-processor", - name: `${ctx.name}#processors["${pluginId}/${processorId}"]` - } - ); - } - } - } - } -} - -module.exports = { ConfigArrayFactory, createContext }; diff --git a/lib/cli-engine/config-array/config-array.js b/lib/cli-engine/config-array/config-array.js deleted file mode 100644 index c2ef6c94ab0..00000000000 --- a/lib/cli-engine/config-array/config-array.js +++ /dev/null @@ -1,536 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview `ConfigArray` class. - * - * `ConfigArray` class expresses the full of a configuration. It has the entry - * config file, base config files that were extended, loaded parsers, and loaded - * plugins. - * - * `ConfigArray` class provides three properties and two methods. - * - * - `pluginEnvironments` - * - `pluginProcessors` - * - `pluginRules` - * The `Map` objects that contain the members of all plugins that this - * config array contains. Those map objects don't have mutation methods. - * Those keys are the member ID such as `pluginId/memberName`. - * - `isRoot()` - * If `true` then this configuration has `root:true` property. - * - `extractConfig(filePath)` - * Extract the final configuration for a given file. This means merging - * every config array element which that `criteria` property matched. The - * `filePath` argument must be an absolute path. - * - * `ConfigArrayFactory` provides the loading logic of config files. - * - * @author Toru Nagashima - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const { ExtractedConfig } = require("./extracted-config"); -const { IgnorePattern } = require("./ignore-pattern"); - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -// Define types for VSCode IntelliSense. -/** @typedef {import("../../shared/types").Environment} Environment */ -/** @typedef {import("../../shared/types").GlobalConf} GlobalConf */ -/** @typedef {import("../../shared/types").RuleConf} RuleConf */ -/** @typedef {import("../../shared/types").Rule} Rule */ -/** @typedef {import("../../shared/types").Plugin} Plugin */ -/** @typedef {import("../../shared/types").Processor} Processor */ -/** @typedef {import("./config-dependency").DependentParser} DependentParser */ -/** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */ -/** @typedef {import("./override-tester")["OverrideTester"]} OverrideTester */ - -/** - * @typedef {Object} ConfigArrayElement - * @property {string} name The name of this config element. - * @property {string} filePath The path to the source file of this config element. - * @property {InstanceType|null} criteria The tester for the `files` and `excludedFiles` of this config element. - * @property {Record|undefined} env The environment settings. - * @property {Record|undefined} globals The global variable settings. - * @property {IgnorePattern|undefined} ignorePattern The ignore patterns. - * @property {boolean|undefined} noInlineConfig The flag that disables directive comments. - * @property {DependentParser|undefined} parser The parser loader. - * @property {Object|undefined} parserOptions The parser options. - * @property {Record|undefined} plugins The plugin loaders. - * @property {string|undefined} processor The processor name to refer plugin's processor. - * @property {boolean|undefined} reportUnusedDisableDirectives The flag to report unused `eslint-disable` comments. - * @property {boolean|undefined} root The flag to express root. - * @property {Record|undefined} rules The rule settings - * @property {Object|undefined} settings The shared settings. - * @property {"config" | "ignore" | "implicit-processor"} type The element type. - */ - -/** - * @typedef {Object} ConfigArrayInternalSlots - * @property {Map} cache The cache to extract configs. - * @property {ReadonlyMap|null} envMap The map from environment ID to environment definition. - * @property {ReadonlyMap|null} processorMap The map from processor ID to environment definition. - * @property {ReadonlyMap|null} ruleMap The map from rule ID to rule definition. - */ - -/** @type {WeakMap} */ -const internalSlotsMap = new class extends WeakMap { - get(key) { - let value = super.get(key); - - if (!value) { - value = { - cache: new Map(), - envMap: null, - processorMap: null, - ruleMap: null - }; - super.set(key, value); - } - - return value; - } -}(); - -/** - * Get the indices which are matched to a given file. - * @param {ConfigArrayElement[]} elements The elements. - * @param {string} filePath The path to a target file. - * @returns {number[]} The indices. - */ -function getMatchedIndices(elements, filePath) { - const indices = []; - - for (let i = elements.length - 1; i >= 0; --i) { - const element = elements[i]; - - if (!element.criteria || (filePath && element.criteria.test(filePath))) { - indices.push(i); - } - } - - return indices; -} - -/** - * Check if a value is a non-null object. - * @param {any} x The value to check. - * @returns {boolean} `true` if the value is a non-null object. - */ -function isNonNullObject(x) { - return typeof x === "object" && x !== null; -} - -/** - * Merge two objects. - * - * Assign every property values of `y` to `x` if `x` doesn't have the property. - * If `x`'s property value is an object, it does recursive. - * @param {Object} target The destination to merge - * @param {Object|undefined} source The source to merge. - * @returns {void} - */ -function mergeWithoutOverwrite(target, source) { - if (!isNonNullObject(source)) { - return; - } - - for (const key of Object.keys(source)) { - if (key === "__proto__") { - continue; - } - - if (isNonNullObject(target[key])) { - mergeWithoutOverwrite(target[key], source[key]); - } else if (target[key] === void 0) { - if (isNonNullObject(source[key])) { - target[key] = Array.isArray(source[key]) ? [] : {}; - mergeWithoutOverwrite(target[key], source[key]); - } else if (source[key] !== void 0) { - target[key] = source[key]; - } - } - } -} - -/** - * The error for plugin conflicts. - */ -class PluginConflictError extends Error { - - /** - * Initialize this error object. - * @param {string} pluginId The plugin ID. - * @param {{filePath:string, importerName:string}[]} plugins The resolved plugins. - */ - constructor(pluginId, plugins) { - super(`Plugin "${pluginId}" was conflicted between ${plugins.map(p => `"${p.importerName}"`).join(" and ")}.`); - this.messageTemplate = "plugin-conflict"; - this.messageData = { pluginId, plugins }; - } -} - -/** - * Merge plugins. - * `target`'s definition is prior to `source`'s. - * @param {Record} target The destination to merge - * @param {Record|undefined} source The source to merge. - * @returns {void} - */ -function mergePlugins(target, source) { - if (!isNonNullObject(source)) { - return; - } - - for (const key of Object.keys(source)) { - if (key === "__proto__") { - continue; - } - const targetValue = target[key]; - const sourceValue = source[key]; - - // Adopt the plugin which was found at first. - if (targetValue === void 0) { - if (sourceValue.error) { - throw sourceValue.error; - } - target[key] = sourceValue; - } else if (sourceValue.filePath !== targetValue.filePath) { - throw new PluginConflictError(key, [ - { - filePath: targetValue.filePath, - importerName: targetValue.importerName - }, - { - filePath: sourceValue.filePath, - importerName: sourceValue.importerName - } - ]); - } - } -} - -/** - * Merge rule configs. - * `target`'s definition is prior to `source`'s. - * @param {Record} target The destination to merge - * @param {Record|undefined} source The source to merge. - * @returns {void} - */ -function mergeRuleConfigs(target, source) { - if (!isNonNullObject(source)) { - return; - } - - for (const key of Object.keys(source)) { - if (key === "__proto__") { - continue; - } - const targetDef = target[key]; - const sourceDef = source[key]; - - // Adopt the rule config which was found at first. - if (targetDef === void 0) { - if (Array.isArray(sourceDef)) { - target[key] = [...sourceDef]; - } else { - target[key] = [sourceDef]; - } - - /* - * If the first found rule config is severity only and the current rule - * config has options, merge the severity and the options. - */ - } else if ( - targetDef.length === 1 && - Array.isArray(sourceDef) && - sourceDef.length >= 2 - ) { - targetDef.push(...sourceDef.slice(1)); - } - } -} - -/** - * Create the extracted config. - * @param {ConfigArray} instance The config elements. - * @param {number[]} indices The indices to use. - * @returns {ExtractedConfig} The extracted config. - */ -function createConfig(instance, indices) { - const config = new ExtractedConfig(); - const ignorePatterns = []; - - // Merge elements. - for (const index of indices) { - const element = instance[index]; - - // Adopt the parser which was found at first. - if (!config.parser && element.parser) { - if (element.parser.error) { - throw element.parser.error; - } - config.parser = element.parser; - } - - // Adopt the processor which was found at first. - if (!config.processor && element.processor) { - config.processor = element.processor; - } - - // Adopt the noInlineConfig which was found at first. - if (config.noInlineConfig === void 0 && element.noInlineConfig !== void 0) { - config.noInlineConfig = element.noInlineConfig; - config.configNameOfNoInlineConfig = element.name; - } - - // Adopt the reportUnusedDisableDirectives which was found at first. - if (config.reportUnusedDisableDirectives === void 0 && element.reportUnusedDisableDirectives !== void 0) { - config.reportUnusedDisableDirectives = element.reportUnusedDisableDirectives; - } - - // Collect ignorePatterns - if (element.ignorePattern) { - ignorePatterns.push(element.ignorePattern); - } - - // Merge others. - mergeWithoutOverwrite(config.env, element.env); - mergeWithoutOverwrite(config.globals, element.globals); - mergeWithoutOverwrite(config.parserOptions, element.parserOptions); - mergeWithoutOverwrite(config.settings, element.settings); - mergePlugins(config.plugins, element.plugins); - mergeRuleConfigs(config.rules, element.rules); - } - - // Create the predicate function for ignore patterns. - if (ignorePatterns.length > 0) { - config.ignores = IgnorePattern.createIgnore(ignorePatterns.reverse()); - } - - return config; -} - -/** - * Collect definitions. - * @template T, U - * @param {string} pluginId The plugin ID for prefix. - * @param {Record} defs The definitions to collect. - * @param {Map} map The map to output. - * @param {function(T): U} [normalize] The normalize function for each value. - * @returns {void} - */ -function collect(pluginId, defs, map, normalize) { - if (defs) { - const prefix = pluginId && `${pluginId}/`; - - for (const [key, value] of Object.entries(defs)) { - map.set( - `${prefix}${key}`, - normalize ? normalize(value) : value - ); - } - } -} - -/** - * Normalize a rule definition. - * @param {Function|Rule} rule The rule definition to normalize. - * @returns {Rule} The normalized rule definition. - */ -function normalizePluginRule(rule) { - return typeof rule === "function" ? { create: rule } : rule; -} - -/** - * Delete the mutation methods from a given map. - * @param {Map} map The map object to delete. - * @returns {void} - */ -function deleteMutationMethods(map) { - Object.defineProperties(map, { - clear: { configurable: true, value: void 0 }, - delete: { configurable: true, value: void 0 }, - set: { configurable: true, value: void 0 } - }); -} - -/** - * Create `envMap`, `processorMap`, `ruleMap` with the plugins in the config array. - * @param {ConfigArrayElement[]} elements The config elements. - * @param {ConfigArrayInternalSlots} slots The internal slots. - * @returns {void} - */ -function initPluginMemberMaps(elements, slots) { - const processed = new Set(); - - slots.envMap = new Map(); - slots.processorMap = new Map(); - slots.ruleMap = new Map(); - - for (const element of elements) { - if (!element.plugins) { - continue; - } - - for (const [pluginId, value] of Object.entries(element.plugins)) { - const plugin = value.definition; - - if (!plugin || processed.has(pluginId)) { - continue; - } - processed.add(pluginId); - - collect(pluginId, plugin.environments, slots.envMap); - collect(pluginId, plugin.processors, slots.processorMap); - collect(pluginId, plugin.rules, slots.ruleMap, normalizePluginRule); - } - } - - deleteMutationMethods(slots.envMap); - deleteMutationMethods(slots.processorMap); - deleteMutationMethods(slots.ruleMap); -} - -/** - * Create `envMap`, `processorMap`, `ruleMap` with the plugins in the config array. - * @param {ConfigArray} instance The config elements. - * @returns {ConfigArrayInternalSlots} The extracted config. - */ -function ensurePluginMemberMaps(instance) { - const slots = internalSlotsMap.get(instance); - - if (!slots.ruleMap) { - initPluginMemberMaps(instance, slots); - } - - return slots; -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -/** - * The Config Array. - * - * `ConfigArray` instance contains all settings, parsers, and plugins. - * You need to call `ConfigArray#extractConfig(filePath)` method in order to - * extract, merge and get only the config data which is related to an arbitrary - * file. - * @extends {Array} - */ -class ConfigArray extends Array { - - /** - * Get the plugin environments. - * The returned map cannot be mutated. - * @type {ReadonlyMap} The plugin environments. - */ - get pluginEnvironments() { - return ensurePluginMemberMaps(this).envMap; - } - - /** - * Get the plugin processors. - * The returned map cannot be mutated. - * @type {ReadonlyMap} The plugin processors. - */ - get pluginProcessors() { - return ensurePluginMemberMaps(this).processorMap; - } - - /** - * Get the plugin rules. - * The returned map cannot be mutated. - * @returns {ReadonlyMap} The plugin rules. - */ - get pluginRules() { - return ensurePluginMemberMaps(this).ruleMap; - } - - /** - * Check if this config has `root` flag. - * @returns {boolean} `true` if this config array is root. - */ - isRoot() { - for (let i = this.length - 1; i >= 0; --i) { - const root = this[i].root; - - if (typeof root === "boolean") { - return root; - } - } - return false; - } - - /** - * Extract the config data which is related to a given file. - * @param {string} filePath The absolute path to the target file. - * @returns {ExtractedConfig} The extracted config data. - */ - extractConfig(filePath) { - const { cache } = internalSlotsMap.get(this); - const indices = getMatchedIndices(this, filePath); - const cacheKey = indices.join(","); - - if (!cache.has(cacheKey)) { - cache.set(cacheKey, createConfig(this, indices)); - } - - return cache.get(cacheKey); - } - - /** - * Check if a given path is an additional lint target. - * @param {string} filePath The absolute path to the target file. - * @returns {boolean} `true` if the file is an additional lint target. - */ - isAdditionalTargetPath(filePath) { - for (const { criteria, type } of this) { - if ( - type === "config" && - criteria && - !criteria.endsWithWildcard && - criteria.test(filePath) - ) { - return true; - } - } - return false; - } -} - -const exportObject = { - ConfigArray, - - /** - * Get the used extracted configs. - * CLIEngine will use this method to collect used deprecated rules. - * @param {ConfigArray} instance The config array object to get. - * @returns {ExtractedConfig[]} The used extracted configs. - * @private - */ - getUsedExtractedConfigs(instance) { - const { cache } = internalSlotsMap.get(instance); - - return Array.from(cache.values()); - } -}; - -module.exports = exportObject; diff --git a/lib/cli-engine/config-array/config-dependency.js b/lib/cli-engine/config-array/config-dependency.js deleted file mode 100644 index 9fc67b01057..00000000000 --- a/lib/cli-engine/config-array/config-dependency.js +++ /dev/null @@ -1,128 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview `ConfigDependency` class. - * - * `ConfigDependency` class expresses a loaded parser or plugin. - * - * If the parser or plugin was loaded successfully, it has `definition` property - * and `filePath` property. Otherwise, it has `error` property. - * - * When `JSON.stringify()` converted a `ConfigDependency` object to a JSON, it - * omits `definition` property. - * - * `ConfigArrayFactory` creates `ConfigDependency` objects when it loads parsers - * or plugins. - * - * @author Toru Nagashima - */ -"use strict"; - -const util = require("util"); - -/** - * The class is to store parsers or plugins. - * This class hides the loaded object from `JSON.stringify()` and `console.log`. - * @template T - */ -class ConfigDependency { - - /** - * Initialize this instance. - * @param {Object} data The dependency data. - * @param {T} [data.definition] The dependency if the loading succeeded. - * @param {Error} [data.error] The error object if the loading failed. - * @param {string} [data.filePath] The actual path to the dependency if the loading succeeded. - * @param {string} data.id The ID of this dependency. - * @param {string} data.importerName The name of the config file which loads this dependency. - * @param {string} data.importerPath The path to the config file which loads this dependency. - */ - constructor({ - definition = null, - error = null, - filePath = null, - id, - importerName, - importerPath - }) { - - /** - * The loaded dependency if the loading succeeded. - * @type {T|null} - */ - this.definition = definition; - - /** - * The error object if the loading failed. - * @type {Error|null} - */ - this.error = error; - - /** - * The loaded dependency if the loading succeeded. - * @type {string|null} - */ - this.filePath = filePath; - - /** - * The ID of this dependency. - * @type {string} - */ - this.id = id; - - /** - * The name of the config file which loads this dependency. - * @type {string} - */ - this.importerName = importerName; - - /** - * The path to the config file which loads this dependency. - * @type {string} - */ - this.importerPath = importerPath; - } - - // eslint-disable-next-line jsdoc/require-description - /** - * @returns {Object} a JSON compatible object. - */ - toJSON() { - const obj = this[util.inspect.custom](); - - // Display `error.message` (`Error#message` is unenumerable). - if (obj.error instanceof Error) { - obj.error = { ...obj.error, message: obj.error.message }; - } - - return obj; - } - - // eslint-disable-next-line jsdoc/require-description - /** - * @returns {Object} an object to display by `console.log()`. - */ - [util.inspect.custom]() { - const { - definition: _ignore, // eslint-disable-line no-unused-vars - ...obj - } = this; - - return obj; - } -} - -/** @typedef {ConfigDependency} DependentParser */ -/** @typedef {ConfigDependency} DependentPlugin */ - -module.exports = { ConfigDependency }; diff --git a/lib/cli-engine/config-array/extracted-config.js b/lib/cli-engine/config-array/extracted-config.js deleted file mode 100644 index fd7cabab3e9..00000000000 --- a/lib/cli-engine/config-array/extracted-config.js +++ /dev/null @@ -1,158 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview `ExtractedConfig` class. - * - * `ExtractedConfig` class expresses a final configuration for a specific file. - * - * It provides one method. - * - * - `toCompatibleObjectAsConfigFileContent()` - * Convert this configuration to the compatible object as the content of - * config files. It converts the loaded parser and plugins to strings. - * `CLIEngine#getConfigForFile(filePath)` method uses this method. - * - * `ConfigArray#extractConfig(filePath)` creates a `ExtractedConfig` instance. - * - * @author Toru Nagashima - */ -"use strict"; - -const { IgnorePattern } = require("./ignore-pattern"); - -// For VSCode intellisense -/** @typedef {import("../../shared/types").ConfigData} ConfigData */ -/** @typedef {import("../../shared/types").GlobalConf} GlobalConf */ -/** @typedef {import("../../shared/types").SeverityConf} SeverityConf */ -/** @typedef {import("./config-dependency").DependentParser} DependentParser */ -/** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */ - -/** - * Check if `xs` starts with `ys`. - * @template T - * @param {T[]} xs The array to check. - * @param {T[]} ys The array that may be the first part of `xs`. - * @returns {boolean} `true` if `xs` starts with `ys`. - */ -function startsWith(xs, ys) { - return xs.length >= ys.length && ys.every((y, i) => y === xs[i]); -} - -/** - * The class for extracted config data. - */ -class ExtractedConfig { - constructor() { - - /** - * The config name what `noInlineConfig` setting came from. - * @type {string} - */ - this.configNameOfNoInlineConfig = ""; - - /** - * Environments. - * @type {Record} - */ - this.env = {}; - - /** - * Global variables. - * @type {Record} - */ - this.globals = {}; - - /** - * The glob patterns that ignore to lint. - * @type {(((filePath:string, dot?:boolean) => boolean) & { basePath:string; patterns:string[] }) | undefined} - */ - this.ignores = void 0; - - /** - * The flag that disables directive comments. - * @type {boolean|undefined} - */ - this.noInlineConfig = void 0; - - /** - * Parser definition. - * @type {DependentParser|null} - */ - this.parser = null; - - /** - * Options for the parser. - * @type {Object} - */ - this.parserOptions = {}; - - /** - * Plugin definitions. - * @type {Record} - */ - this.plugins = {}; - - /** - * Processor ID. - * @type {string|null} - */ - this.processor = null; - - /** - * The flag that reports unused `eslint-disable` directive comments. - * @type {boolean|undefined} - */ - this.reportUnusedDisableDirectives = void 0; - - /** - * Rule settings. - * @type {Record} - */ - this.rules = {}; - - /** - * Shared settings. - * @type {Object} - */ - this.settings = {}; - } - - /** - * Convert this config to the compatible object as a config file content. - * @returns {ConfigData} The converted object. - */ - toCompatibleObjectAsConfigFileContent() { - const { - /* eslint-disable no-unused-vars */ - configNameOfNoInlineConfig: _ignore1, - processor: _ignore2, - /* eslint-enable no-unused-vars */ - ignores, - ...config - } = this; - - config.parser = config.parser && config.parser.filePath; - config.plugins = Object.keys(config.plugins).filter(Boolean).reverse(); - config.ignorePatterns = ignores ? ignores.patterns : []; - - // Strip the default patterns from `ignorePatterns`. - if (startsWith(config.ignorePatterns, IgnorePattern.DefaultPatterns)) { - config.ignorePatterns = - config.ignorePatterns.slice(IgnorePattern.DefaultPatterns.length); - } - - return config; - } -} - -module.exports = { ExtractedConfig }; diff --git a/lib/cli-engine/config-array/ignore-pattern.js b/lib/cli-engine/config-array/ignore-pattern.js deleted file mode 100644 index 37d686dd1bb..00000000000 --- a/lib/cli-engine/config-array/ignore-pattern.js +++ /dev/null @@ -1,249 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview `IgnorePattern` class. - * - * `IgnorePattern` class has the set of glob patterns and the base path. - * - * It provides two static methods. - * - * - `IgnorePattern.createDefaultIgnore(cwd)` - * Create the default predicate function. - * - `IgnorePattern.createIgnore(ignorePatterns)` - * Create the predicate function from multiple `IgnorePattern` objects. - * - * It provides two properties and a method. - * - * - `patterns` - * The glob patterns that ignore to lint. - * - `basePath` - * The base path of the glob patterns. If absolute paths existed in the - * glob patterns, those are handled as relative paths to the base path. - * - `getPatternsRelativeTo(basePath)` - * Get `patterns` as modified for a given base path. It modifies the - * absolute paths in the patterns as prepending the difference of two base - * paths. - * - * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes - * `ignorePatterns` properties. - * - * @author Toru Nagashima - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const assert = require("assert"); -const path = require("path"); -const ignore = require("ignore"); -const debug = require("debug")("eslint:ignore-pattern"); - -/** @typedef {ReturnType} Ignore */ - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -/** - * Get the path to the common ancestor directory of given paths. - * @param {string[]} sourcePaths The paths to calculate the common ancestor. - * @returns {string} The path to the common ancestor directory. - */ -function getCommonAncestorPath(sourcePaths) { - let result = sourcePaths[0]; - - for (let i = 1; i < sourcePaths.length; ++i) { - const a = result; - const b = sourcePaths[i]; - - // Set the shorter one (it's the common ancestor if one includes the other). - result = a.length < b.length ? a : b; - - // Set the common ancestor. - for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) { - if (a[j] !== b[j]) { - result = a.slice(0, lastSepPos); - break; - } - if (a[j] === path.sep) { - lastSepPos = j; - } - } - } - - let resolvedResult = result || path.sep; - - // if Windows common ancestor is root of drive must have trailing slash to be absolute. - if (resolvedResult && resolvedResult.endsWith(":") && process.platform === "win32") { - resolvedResult += path.sep; - } - return resolvedResult; -} - -/** - * Make relative path. - * @param {string} from The source path to get relative path. - * @param {string} to The destination path to get relative path. - * @returns {string} The relative path. - */ -function relative(from, to) { - const relPath = path.relative(from, to); - - if (path.sep === "/") { - return relPath; - } - return relPath.split(path.sep).join("/"); -} - -/** - * Get the trailing slash if existed. - * @param {string} filePath The path to check. - * @returns {string} The trailing slash if existed. - */ -function dirSuffix(filePath) { - const isDir = ( - filePath.endsWith(path.sep) || - (process.platform === "win32" && filePath.endsWith("/")) - ); - - return isDir ? "/" : ""; -} - -const DefaultPatterns = Object.freeze(["/**/node_modules/*"]); -const DotPatterns = Object.freeze([".*", "!.eslintrc.*", "!../"]); - -//------------------------------------------------------------------------------ -// Public -//------------------------------------------------------------------------------ - -class IgnorePattern { - - /** - * The default patterns. - * @type {string[]} - */ - static get DefaultPatterns() { - return DefaultPatterns; - } - - /** - * Create the default predicate function. - * @param {string} cwd The current working directory. - * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}} - * The preficate function. - * The first argument is an absolute path that is checked. - * The second argument is the flag to not ignore dotfiles. - * If the predicate function returned `true`, it means the path should be ignored. - */ - static createDefaultIgnore(cwd) { - return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]); - } - - /** - * Create the predicate function from multiple `IgnorePattern` objects. - * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns. - * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}} - * The preficate function. - * The first argument is an absolute path that is checked. - * The second argument is the flag to not ignore dotfiles. - * If the predicate function returned `true`, it means the path should be ignored. - */ - static createIgnore(ignorePatterns) { - debug("Create with: %o", ignorePatterns); - - const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath)); - const patterns = [].concat( - ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath)) - ); - const ig = ignore().add([...DotPatterns, ...patterns]); - const dotIg = ignore().add(patterns); - - debug(" processed: %o", { basePath, patterns }); - - return Object.assign( - (filePath, dot = false) => { - assert(path.isAbsolute(filePath), "'filePath' should be an absolute path."); - const relPathRaw = relative(basePath, filePath); - const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath)); - const adoptedIg = dot ? dotIg : ig; - const result = relPath !== "" && adoptedIg.ignores(relPath); - - debug("Check", { filePath, dot, relativePath: relPath, result }); - return result; - }, - { basePath, patterns } - ); - } - - /** - * Initialize a new `IgnorePattern` instance. - * @param {string[]} patterns The glob patterns that ignore to lint. - * @param {string} basePath The base path of `patterns`. - */ - constructor(patterns, basePath) { - assert(path.isAbsolute(basePath), "'basePath' should be an absolute path."); - - /** - * The glob patterns that ignore to lint. - * @type {string[]} - */ - this.patterns = patterns; - - /** - * The base path of `patterns`. - * @type {string} - */ - this.basePath = basePath; - - /** - * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`. - * - * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility. - * It's `false` as-is for `ignorePatterns` property in config files. - * @type {boolean} - */ - this.loose = false; - } - - /** - * Get `patterns` as modified for a given base path. It modifies the - * absolute paths in the patterns as prepending the difference of two base - * paths. - * @param {string} newBasePath The base path. - * @returns {string[]} Modifired patterns. - */ - getPatternsRelativeTo(newBasePath) { - assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path."); - const { basePath, loose, patterns } = this; - - if (newBasePath === basePath) { - return patterns; - } - const prefix = `/${relative(newBasePath, basePath)}`; - - return patterns.map(pattern => { - const negative = pattern.startsWith("!"); - const head = negative ? "!" : ""; - const body = negative ? pattern.slice(1) : pattern; - - if (body.startsWith("/") || body.startsWith("../")) { - return `${head}${prefix}${body}`; - } - return loose ? pattern : `${head}${prefix}/**/${body}`; - }); - } -} - -module.exports = { IgnorePattern }; diff --git a/lib/cli-engine/config-array/index.js b/lib/cli-engine/config-array/index.js deleted file mode 100644 index 146c6f89279..00000000000 --- a/lib/cli-engine/config-array/index.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview `ConfigArray` class. - * @author Toru Nagashima - */ -"use strict"; - -const { ConfigArray, getUsedExtractedConfigs } = require("./config-array"); -const { ConfigDependency } = require("./config-dependency"); -const { ExtractedConfig } = require("./extracted-config"); -const { IgnorePattern } = require("./ignore-pattern"); -const { OverrideTester } = require("./override-tester"); - -module.exports = { - ConfigArray, - ConfigDependency, - ExtractedConfig, - IgnorePattern, - OverrideTester, - getUsedExtractedConfigs -}; diff --git a/lib/cli-engine/config-array/override-tester.js b/lib/cli-engine/config-array/override-tester.js deleted file mode 100644 index 75dffe2352e..00000000000 --- a/lib/cli-engine/config-array/override-tester.js +++ /dev/null @@ -1,235 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview `OverrideTester` class. - * - * `OverrideTester` class handles `files` property and `excludedFiles` property - * of `overrides` config. - * - * It provides one method. - * - * - `test(filePath)` - * Test if a file path matches the pair of `files` property and - * `excludedFiles` property. The `filePath` argument must be an absolute - * path. - * - * `ConfigArrayFactory` creates `OverrideTester` objects when it processes - * `overrides` properties. - * - * @author Toru Nagashima - */ -"use strict"; - -const assert = require("assert"); -const path = require("path"); -const util = require("util"); -const { Minimatch } = require("minimatch"); -const minimatchOpts = { dot: true, matchBase: true }; - -/** - * @typedef {Object} Pattern - * @property {InstanceType[] | null} includes The positive matchers. - * @property {InstanceType[] | null} excludes The negative matchers. - */ - -/** - * Normalize a given pattern to an array. - * @param {string|string[]|undefined} patterns A glob pattern or an array of glob patterns. - * @returns {string[]|null} Normalized patterns. - * @private - */ -function normalizePatterns(patterns) { - if (Array.isArray(patterns)) { - return patterns.filter(Boolean); - } - if (typeof patterns === "string" && patterns) { - return [patterns]; - } - return []; -} - -/** - * Create the matchers of given patterns. - * @param {string[]} patterns The patterns. - * @returns {InstanceType[] | null} The matchers. - */ -function toMatcher(patterns) { - if (patterns.length === 0) { - return null; - } - return patterns.map(pattern => { - if (/^\.[/\\]/u.test(pattern)) { - return new Minimatch( - pattern.slice(2), - - // `./*.js` should not match with `subdir/foo.js` - { ...minimatchOpts, matchBase: false } - ); - } - return new Minimatch(pattern, minimatchOpts); - }); -} - -/** - * Convert a given matcher to string. - * @param {Pattern} matchers The matchers. - * @returns {string} The string expression of the matcher. - */ -function patternToJson({ includes, excludes }) { - return { - includes: includes && includes.map(m => m.pattern), - excludes: excludes && excludes.map(m => m.pattern) - }; -} - -/** - * The class to test given paths are matched by the patterns. - */ -class OverrideTester { - - /** - * Create a tester with given criteria. - * If there are no criteria, returns `null`. - * @param {string|string[]} files The glob patterns for included files. - * @param {string|string[]} excludedFiles The glob patterns for excluded files. - * @param {string} basePath The path to the base directory to test paths. - * @returns {OverrideTester|null} The created instance or `null`. - */ - static create(files, excludedFiles, basePath) { - const includePatterns = normalizePatterns(files); - const excludePatterns = normalizePatterns(excludedFiles); - let endsWithWildcard = false; - - if (includePatterns.length === 0) { - return null; - } - - // Rejects absolute paths or relative paths to parents. - for (const pattern of includePatterns) { - if (path.isAbsolute(pattern) || pattern.includes("..")) { - throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`); - } - if (pattern.endsWith("*")) { - endsWithWildcard = true; - } - } - for (const pattern of excludePatterns) { - if (path.isAbsolute(pattern) || pattern.includes("..")) { - throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`); - } - } - - const includes = toMatcher(includePatterns); - const excludes = toMatcher(excludePatterns); - - return new OverrideTester( - [{ includes, excludes }], - basePath, - endsWithWildcard - ); - } - - /** - * Combine two testers by logical and. - * If either of the testers was `null`, returns the other tester. - * The `basePath` property of the two must be the same value. - * @param {OverrideTester|null} a A tester. - * @param {OverrideTester|null} b Another tester. - * @returns {OverrideTester|null} Combined tester. - */ - static and(a, b) { - if (!b) { - return a && new OverrideTester( - a.patterns, - a.basePath, - a.endsWithWildcard - ); - } - if (!a) { - return new OverrideTester( - b.patterns, - b.basePath, - b.endsWithWildcard - ); - } - - assert.strictEqual(a.basePath, b.basePath); - return new OverrideTester( - a.patterns.concat(b.patterns), - a.basePath, - a.endsWithWildcard || b.endsWithWildcard - ); - } - - /** - * Initialize this instance. - * @param {Pattern[]} patterns The matchers. - * @param {string} basePath The base path. - * @param {boolean} endsWithWildcard If `true` then a pattern ends with `*`. - */ - constructor(patterns, basePath, endsWithWildcard = false) { - - /** @type {Pattern[]} */ - this.patterns = patterns; - - /** @type {string} */ - this.basePath = basePath; - - /** @type {boolean} */ - this.endsWithWildcard = endsWithWildcard; - } - - /** - * Test if a given path is matched or not. - * @param {string} filePath The absolute path to the target file. - * @returns {boolean} `true` if the path was matched. - */ - test(filePath) { - if (typeof filePath !== "string" || !path.isAbsolute(filePath)) { - throw new Error(`'filePath' should be an absolute path, but got ${filePath}.`); - } - const relativePath = path.relative(this.basePath, filePath); - - return this.patterns.every(({ includes, excludes }) => ( - (!includes || includes.some(m => m.match(relativePath))) && - (!excludes || !excludes.some(m => m.match(relativePath))) - )); - } - - // eslint-disable-next-line jsdoc/require-description - /** - * @returns {Object} a JSON compatible object. - */ - toJSON() { - if (this.patterns.length === 1) { - return { - ...patternToJson(this.patterns[0]), - basePath: this.basePath - }; - } - return { - AND: this.patterns.map(patternToJson), - basePath: this.basePath - }; - } - - // eslint-disable-next-line jsdoc/require-description - /** - * @returns {Object} an object to display by `console.log()`. - */ - [util.inspect.custom]() { - return this.toJSON(); - } -} - -module.exports = { OverrideTester }; diff --git a/lib/cli-engine/file-enumerator.js b/lib/cli-engine/file-enumerator.js index 7c433d32f44..446342a1a93 100644 --- a/lib/cli-engine/file-enumerator.js +++ b/lib/cli-engine/file-enumerator.js @@ -40,8 +40,13 @@ const getGlobParent = require("glob-parent"); const isGlob = require("is-glob"); const { escapeRegExp } = require("lodash"); const { Minimatch } = require("minimatch"); -const { IgnorePattern } = require("./config-array"); -const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); + +const { + Legacy: { + IgnorePattern, + CascadingConfigArrayFactory + } +} = require("@eslint/eslintrc"); const debug = require("debug")("eslint:file-enumerator"); //------------------------------------------------------------------------------ diff --git a/package.json b/package.json index e6930f5964c..4cd49120159 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "bugs": "https://github.com/eslint/eslint/issues/", "dependencies": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.1.3", + "@eslint/eslintrc": "^0.2.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", diff --git a/tests/_utils/in-memory-fs.js b/tests/_utils/in-memory-fs.js index a45820917f2..ddacfc7bec6 100644 --- a/tests/_utils/in-memory-fs.js +++ b/tests/_utils/in-memory-fs.js @@ -60,11 +60,11 @@ const { Volume, createFsFromVolume } = require("memfs"); const Proxyquire = require("proxyquire/lib/proxyquire"); const CascadingConfigArrayFactoryPath = - require.resolve("../../lib/cli-engine/cascading-config-array-factory"); + require.resolve("@eslint/eslintrc/lib/cascading-config-array-factory"); const CLIEnginePath = require.resolve("../../lib/cli-engine/cli-engine"); const ConfigArrayFactoryPath = - require.resolve("../../lib/cli-engine/config-array-factory"); + require.resolve("@eslint/eslintrc/lib/config-array-factory"); const FileEnumeratorPath = require.resolve("../../lib/cli-engine/file-enumerator"); const LoadRulesPath = diff --git a/tests/_utils/index.js b/tests/_utils/index.js index 0431e3aced4..7615613d25c 100644 --- a/tests/_utils/index.js +++ b/tests/_utils/index.js @@ -1,5 +1,13 @@ +/** + * @fileoverview Utilities used in tests + */ + "use strict"; +//----------------------------------------------------------------------------- +// Requirements +//----------------------------------------------------------------------------- + const { defineInMemoryFs, defineConfigArrayFactoryWithInMemoryFileSystem, @@ -9,6 +17,11 @@ const { defineESLintWithInMemoryFileSystem } = require("./in-memory-fs"); +const { createTeardown, addFile } = require("fs-teardown"); + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- /** * Prevents leading spaces in a multiline template literal from appearing in the resulting string @@ -27,6 +40,26 @@ function unIndent(strings, ...values) { return lines.map(line => line.slice(minLineIndent)).join("\n"); } +/** + * Creates a new filesystem volume at the given location with the given files. + * @param {Object} desc A description of the filesystem volume to create. + * @param {string} desc.cwd The current working directory ESLint is using. + * @param {Object} desc.files A map of filename to file contents to create. + * @returns {Teardown} An object with prepare(), cleanup(), and getPath() + * methods. + */ +function createCustomTeardown({ cwd, files }) { + const { prepare, cleanup, getPath } = createTeardown( + cwd, + ...Object.keys(files).map(filename => addFile(filename, files[filename])) + ); + + return { prepare, cleanup, getPath }; +} + +//----------------------------------------------------------------------------- +// Exports +//----------------------------------------------------------------------------- module.exports = { unIndent, @@ -35,5 +68,6 @@ module.exports = { defineCascadingConfigArrayFactoryWithInMemoryFileSystem, defineFileEnumeratorWithInMemoryFileSystem, defineCLIEngineWithInMemoryFileSystem, - defineESLintWithInMemoryFileSystem + defineESLintWithInMemoryFileSystem, + createCustomTeardown }; diff --git a/tests/lib/cli-engine/cascading-config-array-factory.js b/tests/lib/cli-engine/cascading-config-array-factory.js deleted file mode 100644 index 7d312c6243a..00000000000 --- a/tests/lib/cli-engine/cascading-config-array-factory.js +++ /dev/null @@ -1,1613 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview Tests for CascadingConfigArrayFactory class. - * @author Toru Nagashima - */ -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); -const { assert } = require("chai"); -const sh = require("shelljs"); -const sinon = require("sinon"); -const { ConfigArrayFactory } = require("../../../lib/cli-engine/config-array-factory"); -const { ExtractedConfig } = require("../../../lib/cli-engine/config-array/extracted-config"); -const { defineCascadingConfigArrayFactoryWithInMemoryFileSystem } = require("../../_utils"); - -/** @typedef {InstanceType["CascadingConfigArrayFactory"]>} CascadingConfigArrayFactory */ -/** @typedef {ReturnType} ConfigArray */ - -const cwdIgnorePatterns = new ConfigArrayFactory() - .loadDefaultESLintIgnore()[0] - .ignorePattern - .patterns; - -describe("CascadingConfigArrayFactory", () => { - describe("'getConfigArrayForFile(filePath)' method should retrieve the proper configuration.", () => { - describe("with three directories ('lib', 'lib/nested', 'test') that contains 'one.js' and 'two.js'", () => { - const root = path.join(os.tmpdir(), "eslint/cli-engine/cascading-config-array-factory"); - const files = { - /* eslint-disable quote-props */ - "lib": { - "nested": { - "one.js": "", - "two.js": "", - "parser.js": "", - ".eslintrc.yml": "parser: './parser'" - }, - "one.js": "", - "two.js": "" - }, - "test": { - "one.js": "", - "two.js": "", - ".eslintrc.yml": "env: { mocha: true }" - }, - ".eslintignore": "/lib/nested/parser.js", - ".eslintrc.json": JSON.stringify({ - rules: { - "no-undef": "error", - "no-unused-vars": "error" - } - }) - /* eslint-enable quote-props */ - }; - const { CascadingConfigArrayFactory } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ cwd: () => root, files }); - - /** @type {CascadingConfigArrayFactory} */ - let factory; - - beforeEach(() => { - factory = new CascadingConfigArrayFactory(); - }); - - it("should retrieve the config '.eslintrc.json' if 'lib/one.js' was given.", () => { - const config = factory.getConfigArrayForFile("lib/one.js"); - - assert.strictEqual(config.length, 3); - assert.strictEqual(config[0].name, "DefaultIgnorePattern"); - assert.strictEqual(config[1].filePath, path.join(root, ".eslintrc.json")); - assert.strictEqual(config[2].filePath, path.join(root, ".eslintignore")); - }); - - it("should retrieve the merged config of '.eslintrc.json' and 'lib/nested/.eslintrc.yml' if 'lib/nested/one.js' was given.", () => { - const config = factory.getConfigArrayForFile("lib/nested/one.js"); - - assert.strictEqual(config.length, 4); - assert.strictEqual(config[0].name, "DefaultIgnorePattern"); - assert.strictEqual(config[1].filePath, path.join(root, ".eslintrc.json")); - assert.strictEqual(config[2].filePath, path.join(root, "lib/nested/.eslintrc.yml")); - assert.strictEqual(config[3].filePath, path.join(root, ".eslintignore")); - }); - - it("should retrieve the config '.eslintrc.json' if 'lib/non-exist.js' was given.", () => { - const config = factory.getConfigArrayForFile("lib/non-exist.js"); - - assert.strictEqual(config.length, 3); - assert.strictEqual(config[0].name, "DefaultIgnorePattern"); - assert.strictEqual(config[1].filePath, path.join(root, ".eslintrc.json")); - assert.strictEqual(config[2].filePath, path.join(root, ".eslintignore")); - }); - }); - - describe("deprecation warnings", () => { - let uid = 0; - let uniqueHomeDirName = ""; - let homeDir = ""; - let cwd = ""; - - /** @type {{code:string, message:string}[]} */ - let warnings = []; - - /** @type {CascadingConfigArrayFactory} */ - let factory = null; - - /** @type {ConfigArray} */ - let config = null; - - /** - * Store a reported warning object if that code starts with `ESLINT_`. - * @param {{code:string, message:string}} w The warning object to store. - * @returns {void} - */ - function onWarning(w) { - if (w.code.startsWith("ESLINT_")) { - warnings.push({ code: w.code, message: w.message }); - } - } - - /** - * Delay to wait for 'warning' events. - * @returns {Promise} The promise that will be fulfilled after wait a timer. - */ - function delay() { - return new Promise(resolve => setTimeout(resolve, 0)); - } - - beforeEach(() => { - uniqueHomeDirName = `home_${++uid}`; - homeDir = path.join(__dirname, `../../../${uniqueHomeDirName}`); - warnings = []; - sinon.stub(os, "homedir").returns(homeDir); - process.on("warning", onWarning); - }); - afterEach(() => { - os.homedir.restore(); - process.removeListener("warning", onWarning); - }); - - describe("when '~/.eslintrc.json' exists and CWD is `~/`", () => { - beforeEach(() => { - cwd = homeDir; - const { CascadingConfigArrayFactory } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => cwd, - files: { - - // ~/.eslintrc.json - ".eslintrc.json": JSON.stringify({ rules: { eqeqeq: "error" } }), - - // other files - "exist-with-root/test.js": "", - "exist-with-root/.eslintrc.json": JSON.stringify({ root: true, rules: { yoda: "error" } }), - "exist/test.js": "", - "exist/.eslintrc.json": JSON.stringify({ rules: { yoda: "error" } }), - "not-exist/test.js": "" - } - }); - - factory = new CascadingConfigArrayFactory({ cwd }); - }); - - // no warning. - describe("when it lints 'subdir/exist-with-root/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("exist-with-root/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - - it("should not load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { yoda: ["error"] } - ); - }); - }); - - // no warning. - describe("when it lints 'subdir/exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("exist/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - - it("should load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { eqeqeq: ["error"], yoda: ["error"] } - ); - }); - }); - - // no warning - describe("when it lints 'subdir/not-exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("not-exist/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - - it("should load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { eqeqeq: ["error"] } - ); - }); - }); - }); - - describe("when '~/.eslintrc.json' exists and CWD is `~/subdir`", () => { - beforeEach(() => { - cwd = path.join(homeDir, "subdir"); - const { CascadingConfigArrayFactory } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => cwd, - files: { - - // ~/.eslintrc.json - "../.eslintrc.json": JSON.stringify({ rules: { eqeqeq: "error" } }), - - // other files - "exist-with-root/test.js": "", - "exist-with-root/.eslintrc.json": JSON.stringify({ root: true, rules: { yoda: "error" } }), - "exist/test.js": "", - "exist/.eslintrc.json": JSON.stringify({ rules: { yoda: "error" } }), - "not-exist/test.js": "" - } - }); - - factory = new CascadingConfigArrayFactory({ cwd }); - }); - - // Project's config file has `root:true`, then no warning. - describe("when it lints 'subdir/exist-with-root/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("exist-with-root/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - - it("should not load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { yoda: ["error"] } - ); - }); - }); - - // Project's config file doesn't have `root:true` and home is ancestor, then ESLINT_PERSONAL_CONFIG_SUPPRESS. - describe("when it lints 'subdir/exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("exist/test.js"); - await delay(); - }); - - it("should raise an ESLINT_PERSONAL_CONFIG_SUPPRESS warning.", () => { - assert.deepStrictEqual(warnings, [ - { - code: "ESLINT_PERSONAL_CONFIG_SUPPRESS", - message: `'~/.eslintrc.*' config files have been deprecated. Please remove it or add 'root:true' to the config files in your projects in order to avoid loading '~/.eslintrc.*' accidentally. (found in "${uniqueHomeDirName}${path.sep}.eslintrc.json")` - } - ]); - }); - - it("should not load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { yoda: ["error"] } - ); - }); - }); - - /* - * Project's config file doesn't exist and home is ancestor, then no warning. - * In this case, ESLint will continue to use `~/.eslintrc.json` even if personal config file feature is removed. - */ - describe("when it lints 'subdir/not-exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("not-exist/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - - it("should load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { eqeqeq: ["error"] } - ); - }); - }); - }); - - describe("when '~/.eslintrc.json' exists and CWD is `~/../another`", () => { - beforeEach(() => { - cwd = path.join(homeDir, "../another"); - const { CascadingConfigArrayFactory } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => cwd, - files: { - - // ~/.eslintrc.json - [`../${uniqueHomeDirName}/.eslintrc.json`]: JSON.stringify({ rules: { eqeqeq: "error" } }), - - // other files - "exist-with-root/test.js": "", - "exist-with-root/.eslintrc.json": JSON.stringify({ root: true, rules: { yoda: "error" } }), - "exist/test.js": "", - "exist/.eslintrc.json": JSON.stringify({ rules: { yoda: "error" } }), - "not-exist/test.js": "" - } - }); - - factory = new CascadingConfigArrayFactory({ cwd }); - }); - - // Project's config file has `root:true`, then no warning. - describe("when it lints 'exist-with-root/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("exist-with-root/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - - it("should not load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { yoda: ["error"] } - ); - }); - }); - - // Project's config file doesn't have `root:true` but home is not ancestor, then no warning. - describe("when it lints 'exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("exist/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - - it("should not load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { yoda: ["error"] } - ); - }); - }); - - // Project's config file doesn't exist and home is not ancestor, then ESLINT_PERSONAL_CONFIG_LOAD. - describe("when it lints 'not-exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("not-exist/test.js"); - await delay(); - }); - - it("should raise an ESLINT_PERSONAL_CONFIG_LOAD warning.", () => { - assert.deepStrictEqual(warnings, [ - { - code: "ESLINT_PERSONAL_CONFIG_LOAD", - message: `'~/.eslintrc.*' config files have been deprecated. Please use a config file per project or the '--config' option. (found in "${uniqueHomeDirName}${path.sep}.eslintrc.json")` - } - ]); - }); - - it("should load '~/.eslintrc.json'.", () => { - assert.deepStrictEqual( - config.extractConfig("a.js").rules, - { eqeqeq: ["error"] } - ); - }); - }); - }); - - describe("when '~/.eslintrc.json' doesn't exist and CWD is `~/subdir`", () => { - beforeEach(() => { - cwd = path.join(homeDir, "subdir"); - const { CascadingConfigArrayFactory } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => cwd, - files: { - "exist-with-root/test.js": "", - "exist-with-root/.eslintrc.json": JSON.stringify({ root: true, rules: { yoda: "error" } }), - "exist/test.js": "", - "exist/.eslintrc.json": JSON.stringify({ rules: { yoda: "error" } }), - "not-exist/test.js": "" - } - }); - - factory = new CascadingConfigArrayFactory({ cwd }); - }); - - describe("when it lints 'subdir/exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("exist/test.js"); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - }); - }); - - describe("when '~/.eslintrc.json' doesn't exist and CWD is `~/../another`", () => { - beforeEach(() => { - cwd = path.join(homeDir, "../another"); - const { CascadingConfigArrayFactory } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => cwd, - files: { - "exist-with-root/test.js": "", - "exist-with-root/.eslintrc.json": JSON.stringify({ root: true, rules: { yoda: "error" } }), - "exist/test.js": "", - "exist/.eslintrc.json": JSON.stringify({ rules: { yoda: "error" } }), - "not-exist/test.js": "" - } - }); - - factory = new CascadingConfigArrayFactory({ cwd }); - }); - - describe("when it lints 'not-exist/test.js'", () => { - beforeEach(async () => { - config = factory.getConfigArrayForFile("not-exist/test.js", { ignoreNotFoundError: true }); - await delay(); - }); - - it("should not raise any warnings.", () => { - assert.deepStrictEqual(warnings, []); - }); - }); - }); - }); - - // This group moved from 'tests/lib/config.js' when refactoring to keep the cumulated test cases. - describe("with 'tests/fixtures/config-hierarchy' files", () => { - const { CascadingConfigArrayFactory } = require("../../../lib/cli-engine/cascading-config-array-factory"); - let fixtureDir; - - const DIRECTORY_CONFIG_HIERARCHY = require("../../fixtures/config-hierarchy/file-structure.json"); - - /** - * Returns the path inside of the fixture directory. - * @param {...string} args file path segments. - * @returns {string} The path inside the fixture directory. - * @private - */ - function getFixturePath(...args) { - return path.join(fixtureDir, "config-hierarchy", ...args); - } - - /** - * Mocks the current user's home path - * @param {string} fakeUserHomePath fake user's home path - * @returns {void} - * @private - */ - function mockOsHomedir(fakeUserHomePath) { - sinon.stub(os, "homedir") - .returns(fakeUserHomePath); - } - - /** - * Assert that given two objects have the same properties with the - * same value for each. - * - * The `expected` object is merged with the default values of config - * data before comparing, so you can specify only the properties you - * focus on. - * @param {Object} actual The config object to check. - * @param {Object} expected What the config object should look like. - * @returns {void} - * @private - */ - function assertConfigsEqual(actual, expected) { - const defaults = new ExtractedConfig().toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(actual, { ...defaults, ...expected }); - } - - /** - * Wait for the next tick. - * @returns {Promise} - - */ - function nextTick() { - return new Promise(resolve => process.nextTick(resolve)); - } - - /** - * Get the config data for a file. - * @param {CascadingConfigArrayFactory} factory The factory to get config. - * @param {string} filePath The path to a source code. - * @returns {Object} The gotten config. - */ - function getConfig(factory, filePath = "a.js") { - const { cwd } = factory; - const absolutePath = path.resolve(cwd, filePath); - - return factory - .getConfigArrayForFile(absolutePath) - .extractConfig(absolutePath) - .toCompatibleObjectAsConfigFileContent(); - } - - // copy into clean area so as not to get "infected" by this project's .eslintrc files - before(() => { - fixtureDir = `${os.tmpdir()}/eslint/fixtures`; - sh.mkdir("-p", fixtureDir); - sh.cp("-r", "./tests/fixtures/config-hierarchy", fixtureDir); - sh.cp("-r", "./tests/fixtures/rules", fixtureDir); - }); - - afterEach(() => { - sinon.verifyAndRestore(); - }); - - after(() => { - sh.rm("-r", fixtureDir); - }); - - it("should create config object when using baseConfig with extends", () => { - const customBaseConfig = { - extends: path.resolve(__dirname, "../../fixtures/config-extends/array/.eslintrc") - }; - const factory = new CascadingConfigArrayFactory({ baseConfig: customBaseConfig, useEslintrc: false }); - const config = getConfig(factory); - - assert.deepStrictEqual(config.env, { - browser: false, - es6: true, - node: true - }); - assert.deepStrictEqual(config.rules, { - "no-empty": [1], - "comma-dangle": [2], - "no-console": [2] - }); - }); - - it("should return the project config when called in current working directory", () => { - const factory = new CascadingConfigArrayFactory(); - const actual = getConfig(factory); - - assert.strictEqual(actual.rules.strict[1], "global"); - }); - - it("should not retain configs from previous directories when called multiple times", () => { - const firstpath = path.resolve(__dirname, "../../fixtures/configurations/single-quotes/subdir/.eslintrc"); - const secondpath = path.resolve(__dirname, "../../fixtures/configurations/single-quotes/.eslintrc"); - const factory = new CascadingConfigArrayFactory(); - let config; - - config = getConfig(factory, firstpath); - assert.deepStrictEqual(config.rules["no-new"], [0]); - config = getConfig(factory, secondpath); - assert.deepStrictEqual(config.rules["no-new"], [1]); - }); - - it("should throw error when a configuration file doesn't exist", () => { - const configPath = path.resolve(__dirname, "../../fixtures/configurations/.eslintrc"); - const factory = new CascadingConfigArrayFactory(); - - sinon.stub(fs, "readFileSync").throws(new Error()); - - assert.throws(() => { - getConfig(factory, configPath); - }, "Cannot read config file"); - - }); - - it("should throw error when a configuration file is not require-able", () => { - const configPath = ".eslintrc"; - const factory = new CascadingConfigArrayFactory(); - - sinon.stub(fs, "readFileSync").throws(new Error()); - - assert.throws(() => { - getConfig(factory, configPath); - }, "Cannot read config file"); - - }); - - it("should cache config when the same directory is passed twice", () => { - const configPath = path.resolve(__dirname, "../../fixtures/configurations/single-quotes/.eslintrc"); - const configArrayFactory = new ConfigArrayFactory(); - const factory = new CascadingConfigArrayFactory({ configArrayFactory }); - - sinon.spy(configArrayFactory, "loadInDirectory"); - - // If cached this should be called only once - getConfig(factory, configPath); - const callcount = configArrayFactory.loadInDirectory.callcount; - - getConfig(factory, configPath); - - assert.strictEqual(configArrayFactory.loadInDirectory.callcount, callcount); - }); - - // make sure JS-style comments don't throw an error - it("should load the config file when there are JS-style comments in the text", () => { - const specificConfigPath = path.resolve(__dirname, "../../fixtures/configurations/comments.json"); - const factory = new CascadingConfigArrayFactory({ specificConfigPath, useEslintrc: false }); - const config = getConfig(factory); - const { semi, strict } = config.rules; - - assert.deepStrictEqual(semi, [1]); - assert.deepStrictEqual(strict, [0]); - }); - - // make sure YAML files work correctly - it("should load the config file when a YAML file is used", () => { - const specificConfigPath = path.resolve(__dirname, "../../fixtures/configurations/env-browser.yaml"); - const factory = new CascadingConfigArrayFactory({ specificConfigPath, useEslintrc: false }); - const config = getConfig(factory); - const { "no-alert": noAlert, "no-undef": noUndef } = config.rules; - - assert.deepStrictEqual(noAlert, [0]); - assert.deepStrictEqual(noUndef, [2]); - }); - - it("should contain the correct value for parser when a custom parser is specified", () => { - const configPath = path.resolve(__dirname, "../../fixtures/configurations/parser/.eslintrc.json"); - const factory = new CascadingConfigArrayFactory(); - const config = getConfig(factory, configPath); - - assert.strictEqual(config.parser, path.resolve(path.dirname(configPath), "./custom.js")); - }); - - /* - * Configuration hierarchy --------------------------------------------- - * https://github.com/eslint/eslint/issues/3915 - */ - it("should correctly merge environment settings", () => { - const factory = new CascadingConfigArrayFactory({ useEslintrc: true }); - const file = getFixturePath("envs", "sub", "foo.js"); - const expected = { - rules: {}, - env: { - browser: true, - node: false - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Default configuration - blank - it("should return a blank config when using no .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ useEslintrc: false }); - const file = getFixturePath("broken", "console-wrong-quotes.js"); - const expected = { - rules: {}, - globals: {}, - env: {}, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - it("should return a blank config when baseConfig is set to false and no .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ baseConfig: false, useEslintrc: false }); - const file = getFixturePath("broken", "console-wrong-quotes.js"); - const expected = { - rules: {}, - globals: {}, - env: {}, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // No default configuration - it("should return an empty config when not using .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ useEslintrc: false }); - const file = getFixturePath("broken", "console-wrong-quotes.js"); - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, { ignorePatterns: cwdIgnorePatterns }); - }); - - it("should return a modified config when baseConfig is set to an object and no .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - baseConfig: { - env: { - node: true - }, - rules: { - quotes: [2, "single"] - } - }, - useEslintrc: false - }); - const file = getFixturePath("broken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - quotes: [2, "single"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - it("should return a modified config without plugin rules enabled when baseConfig is set to an object with plugin and no .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - baseConfig: { - env: { - node: true - }, - rules: { - quotes: [2, "single"] - }, - plugins: ["example-with-rules-config"] - }, - cwd: getFixturePath("plugins"), - useEslintrc: false - }); - const file = getFixturePath("broken", "plugins", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - plugins: ["example-with-rules-config"], - rules: { - quotes: [2, "single"] - } - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Project configuration - second level .eslintrc - it("should merge configs when local .eslintrc overrides parent .eslintrc", () => { - const factory = new CascadingConfigArrayFactory(); - const file = getFixturePath("broken", "subbroken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - "no-console": [1], - quotes: [2, "single"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Project configuration - third level .eslintrc - it("should merge configs when local .eslintrc overrides parent and grandparent .eslintrc", () => { - const factory = new CascadingConfigArrayFactory(); - const file = getFixturePath("broken", "subbroken", "subsubbroken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - "no-console": [0], - quotes: [1, "double"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Project configuration - root set in second level .eslintrc - it("should not return or traverse configurations in parents of config with root:true", () => { - const factory = new CascadingConfigArrayFactory(); - const file = getFixturePath("root-true", "parent", "root", "wrong-semi.js"); - const expected = { - rules: { - semi: [2, "never"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Project configuration - root set in second level .eslintrc - it("should return project config when called with a relative path from a subdir", () => { - const factory = new CascadingConfigArrayFactory({ cwd: getFixturePath("root-true", "parent", "root", "subdir") }); - const dir = "."; - const expected = { - rules: { - semi: [2, "never"] - } - }; - const actual = getConfig(factory, dir); - - assertConfigsEqual(actual, expected); - }); - - // Command line configuration - --config with first level .eslintrc - it("should merge command line config when config file adds to local .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - specificConfigPath: getFixturePath("broken", "add-conf.yaml") - }); - const file = getFixturePath("broken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - quotes: [2, "double"], - semi: [1, "never"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Command line configuration - --config with first level .eslintrc - it("should merge command line config when config file overrides local .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - specificConfigPath: getFixturePath("broken", "override-conf.yaml") - }); - const file = getFixturePath("broken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - quotes: [0, "double"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Command line configuration - --config with second level .eslintrc - it("should merge command line config when config file adds to local and parent .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - specificConfigPath: getFixturePath("broken", "add-conf.yaml") - }); - const file = getFixturePath("broken", "subbroken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - quotes: [2, "single"], - "no-console": [1], - semi: [1, "never"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Command line configuration - --config with second level .eslintrc - it("should merge command line config when config file overrides local and parent .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - specificConfigPath: getFixturePath("broken", "override-conf.yaml") - }); - const file = getFixturePath("broken", "subbroken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - quotes: [0, "single"], - "no-console": [1] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Command line configuration - --rule with --config and first level .eslintrc - it("should merge command line config and rule when rule and config file overrides local .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - cliConfig: { - rules: { - quotes: [1, "double"] - } - }, - specificConfigPath: getFixturePath("broken", "override-conf.yaml") - }); - const file = getFixturePath("broken", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - rules: { - quotes: [1, "double"] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - // Command line configuration - --plugin - it("should merge command line plugin with local .eslintrc", () => { - const factory = new CascadingConfigArrayFactory({ - cliConfig: { - plugins: ["another-plugin"] - }, - cwd: getFixturePath("plugins"), - resolvePluginsRelativeTo: getFixturePath("plugins") - }); - const file = getFixturePath("broken", "plugins", "console-wrong-quotes.js"); - const expected = { - env: { - node: true - }, - plugins: [ - "example", - "another-plugin" - ], - rules: { - quotes: [2, "double"] - } - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - - it("should merge multiple different config file formats", () => { - const factory = new CascadingConfigArrayFactory(); - const file = getFixturePath("fileexts/subdir/subsubdir/foo.js"); - const expected = { - env: { - browser: true - }, - rules: { - semi: [2, "always"], - eqeqeq: [2] - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, file); - - assertConfigsEqual(actual, expected); - }); - - - it("should load user config globals", () => { - const configPath = path.resolve(__dirname, "../../fixtures/globals/conf.yaml"); - const factory = new CascadingConfigArrayFactory({ specificConfigPath: configPath, useEslintrc: false }); - const expected = { - globals: { - foo: true - }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, configPath); - - assertConfigsEqual(actual, expected); - }); - - it("should not load disabled environments", () => { - const configPath = path.resolve(__dirname, "../../fixtures/environments/disable.yaml"); - const factory = new CascadingConfigArrayFactory({ specificConfigPath: configPath, useEslintrc: false }); - const config = getConfig(factory, configPath); - - assert.isUndefined(config.globals.window); - }); - - it("should gracefully handle empty files", () => { - const configPath = path.resolve(__dirname, "../../fixtures/configurations/env-node.json"); - const factory = new CascadingConfigArrayFactory({ specificConfigPath: configPath }); - - getConfig(factory, path.resolve(__dirname, "../../fixtures/configurations/empty/empty.json")); - }); - - // Meaningful stack-traces - it("should include references to where an `extends` configuration was loaded from", () => { - const configPath = path.resolve(__dirname, "../../fixtures/config-extends/error.json"); - - assert.throws(() => { - const factory = new CascadingConfigArrayFactory({ useEslintrc: false, specificConfigPath: configPath }); - - getConfig(factory, configPath); - }, /Referenced from:.*?error\.json/u); - }); - - // Keep order with the last array element taking highest precedence - it("should make the last element in an array take the highest precedence", () => { - const configPath = path.resolve(__dirname, "../../fixtures/config-extends/array/.eslintrc"); - const factory = new CascadingConfigArrayFactory({ useEslintrc: false, specificConfigPath: configPath }); - const expected = { - rules: { "no-empty": [1], "comma-dangle": [2], "no-console": [2] }, - env: { browser: false, node: true, es6: true }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, configPath); - - assertConfigsEqual(actual, expected); - }); - - describe("with env in a child configuration file", () => { - it("should not overwrite parserOptions of the parent with env of the child", () => { - const factory = new CascadingConfigArrayFactory(); - const targetPath = getFixturePath("overwrite-ecmaFeatures", "child", "foo.js"); - const expected = { - rules: {}, - env: { commonjs: true }, - parserOptions: { ecmaFeatures: { globalReturn: false } }, - ignorePatterns: cwdIgnorePatterns - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - }); - - describe("personal config file within home directory", () => { - const { - CascadingConfigArrayFactory: StubbedCascadingConfigArrayFactory - } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "eslint/fixtures/config-hierarchy": DIRECTORY_CONFIG_HIERARCHY - } - }); - - /** - * Returns the path inside of the fixture directory. - * @param {...string} args file path segments. - * @returns {string} The path inside the fixture directory. - * @private - */ - function getFakeFixturePath(...args) { - return path.join(process.cwd(), "eslint", "fixtures", "config-hierarchy", ...args); - } - - it("should load the personal config if no local config was found", () => { - const projectPath = getFakeFixturePath("personal-config", "project-without-config"); - const homePath = getFakeFixturePath("personal-config", "home-folder"); - const filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ cwd: projectPath }); - - mockOsHomedir(homePath); - - const actual = getConfig(factory, filePath); - const expected = { - rules: { - "home-folder-rule": [2] - } - }; - - assertConfigsEqual(actual, expected); - }); - - it("should ignore the personal config if a local config was found", () => { - const projectPath = getFakeFixturePath("personal-config", "home-folder", "project"); - const homePath = getFakeFixturePath("personal-config", "home-folder"); - const filePath = getFakeFixturePath("personal-config", "home-folder", "project", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ cwd: projectPath }); - - mockOsHomedir(homePath); - - const actual = getConfig(factory, filePath); - const expected = { - rules: { - "project-level-rule": [2] - } - }; - - assertConfigsEqual(actual, expected); - }); - - it("should ignore the personal config if config is passed through cli", () => { - const configPath = getFakeFixturePath("quotes-error.json"); - const projectPath = getFakeFixturePath("personal-config", "project-without-config"); - const homePath = getFakeFixturePath("personal-config", "home-folder"); - const filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cwd: projectPath, - specificConfigPath: configPath - }); - - mockOsHomedir(homePath); - - const actual = getConfig(factory, filePath); - const expected = { - rules: { - quotes: [2, "double"] - } - }; - - assertConfigsEqual(actual, expected); - }); - - it("should still load the project config if the current working directory is the same as the home folder", () => { - const projectPath = getFakeFixturePath("personal-config", "project-with-config"); - const filePath = getFakeFixturePath("personal-config", "project-with-config", "subfolder", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ cwd: projectPath }); - - mockOsHomedir(projectPath); - - const actual = getConfig(factory, filePath); - const expected = { - rules: { - "project-level-rule": [2], - "subfolder-level-rule": [2] - } - }; - - assertConfigsEqual(actual, expected); - }); - }); - - describe("when no local or personal config is found", () => { - const { - CascadingConfigArrayFactory: StubbedCascadingConfigArrayFactory - } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "eslint/fixtures/config-hierarchy": DIRECTORY_CONFIG_HIERARCHY - } - }); - - /** - * Returns the path inside of the fixture directory. - * @param {...string} args file path segments. - * @returns {string} The path inside the fixture directory. - * @private - */ - function getFakeFixturePath(...args) { - return path.join(process.cwd(), "eslint", "fixtures", "config-hierarchy", ...args); - } - - it("should throw an error if no local config and no personal config was found", () => { - const projectPath = getFakeFixturePath("personal-config", "project-without-config"); - const homePath = getFakeFixturePath("personal-config", "folder-does-not-exist"); - const filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ cwd: projectPath }); - - mockOsHomedir(homePath); - - assert.throws(() => { - getConfig(factory, filePath); - }, "No ESLint configuration found"); - }); - - it("should throw an error if no local config was found and ~/package.json contains no eslintConfig section", () => { - const projectPath = getFakeFixturePath("personal-config", "project-without-config"); - const homePath = getFakeFixturePath("personal-config", "home-folder-with-packagejson"); - const filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ cwd: projectPath }); - - mockOsHomedir(homePath); - - assert.throws(() => { - getConfig(factory, filePath); - }, "No ESLint configuration found"); - }); - - it("should not throw an error if no local config and no personal config was found but useEslintrc is false", () => { - const projectPath = getFakeFixturePath("personal-config", "project-without-config"); - const homePath = getFakeFixturePath("personal-config", "folder-does-not-exist"); - const filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ cwd: projectPath, useEslintrc: false }); - - mockOsHomedir(homePath); - - getConfig(factory, filePath); - }); - - it("should not throw an error if no local config and no personal config was found but rules are specified", () => { - const projectPath = getFakeFixturePath("personal-config", "project-without-config"); - const homePath = getFakeFixturePath("personal-config", "folder-does-not-exist"); - const filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cliConfig: { - rules: { quotes: [2, "single"] } - }, - cwd: projectPath - }); - - mockOsHomedir(homePath); - - getConfig(factory, filePath); - }); - - it("should not throw an error if no local config and no personal config was found but baseConfig is specified", () => { - const projectPath = getFakeFixturePath("personal-config", "project-without-config"); - const homePath = getFakeFixturePath("personal-config", "folder-does-not-exist"); - const filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ baseConfig: {}, cwd: projectPath }); - - mockOsHomedir(homePath); - - getConfig(factory, filePath); - }); - }); - - describe("with overrides", () => { - const { - CascadingConfigArrayFactory: StubbedCascadingConfigArrayFactory - } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "eslint/fixtures/config-hierarchy": DIRECTORY_CONFIG_HIERARCHY - } - }); - - /** - * Returns the path inside of the fixture directory. - * @param {...string} pathSegments One or more path segments, in order of depth, shallowest first - * @returns {string} The path inside the fixture directory. - * @private - */ - function getFakeFixturePath(...pathSegments) { - return path.join(process.cwd(), "eslint", "fixtures", "config-hierarchy", ...pathSegments); - } - - it("should merge override config when the pattern matches the file name", () => { - const factory = new StubbedCascadingConfigArrayFactory({}); - const targetPath = getFakeFixturePath("overrides", "foo.js"); - const expected = { - rules: { - quotes: [2, "single"], - "no-else-return": [0], - "no-unused-vars": [1], - semi: [1, "never"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should merge override config when the pattern matches the file path relative to the config file", () => { - const factory = new StubbedCascadingConfigArrayFactory({}); - const targetPath = getFakeFixturePath("overrides", "child", "child-one.js"); - const expected = { - rules: { - curly: ["error", "multi", "consistent"], - "no-else-return": [0], - "no-unused-vars": [1], - quotes: [2, "double"], - semi: [1, "never"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should not merge override config when the pattern matches the absolute file path", () => { - const resolvedPath = path.resolve(__dirname, "../../fixtures/config-hierarchy/overrides/bar.js"); - - assert.throws(() => new StubbedCascadingConfigArrayFactory({ - baseConfig: { - overrides: [{ - files: resolvedPath, - rules: { - quotes: [1, "double"] - } - }] - }, - useEslintrc: false - }), /Invalid override pattern/u); - }); - - it("should not merge override config when the pattern traverses up the directory tree", () => { - const parentPath = "overrides/../**/*.js"; - - assert.throws(() => new StubbedCascadingConfigArrayFactory({ - baseConfig: { - overrides: [{ - files: parentPath, - rules: { - quotes: [1, "single"] - } - }] - }, - useEslintrc: false - }), /Invalid override pattern/u); - }); - - it("should merge all local configs (override and non-override) before non-local configs", () => { - const factory = new StubbedCascadingConfigArrayFactory({}); - const targetPath = getFakeFixturePath("overrides", "two", "child-two.js"); - const expected = { - rules: { - "no-console": [0], - "no-else-return": [0], - "no-unused-vars": [2], - quotes: [2, "double"], - semi: [2, "never"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should apply overrides in parent .eslintrc over non-override rules in child .eslintrc", () => { - const targetPath = getFakeFixturePath("overrides", "three", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cwd: getFakeFixturePath("overrides"), - baseConfig: { - overrides: [ - { - files: "three/**/*.js", - rules: { - "semi-style": [2, "last"] - } - } - ] - }, - useEslintrc: false - }); - const expected = { - rules: { - "semi-style": [2, "last"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should apply overrides if all glob patterns match", () => { - const targetPath = getFakeFixturePath("overrides", "one", "child-one.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cwd: getFakeFixturePath("overrides"), - baseConfig: { - overrides: [{ - files: ["one/**/*", "*.js"], - rules: { - quotes: [2, "single"] - } - }] - }, - useEslintrc: false - }); - const expected = { - rules: { - quotes: [2, "single"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should apply overrides even if some glob patterns do not match", () => { - const targetPath = getFakeFixturePath("overrides", "one", "child-one.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cwd: getFakeFixturePath("overrides"), - baseConfig: { - overrides: [{ - files: ["one/**/*", "*two.js"], - rules: { - quotes: [2, "single"] - } - }] - }, - useEslintrc: false - }); - const expected = { - rules: { - quotes: [2, "single"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should not apply overrides if any excluded glob patterns match", () => { - const targetPath = getFakeFixturePath("overrides", "one", "child-one.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cwd: getFakeFixturePath("overrides"), - baseConfig: { - overrides: [{ - files: "one/**/*", - excludedFiles: ["two/**/*", "*one.js"], - rules: { - quotes: [2, "single"] - } - }] - }, - useEslintrc: false - }); - const expected = { - rules: {} - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should apply overrides if all excluded glob patterns fail to match", () => { - const targetPath = getFakeFixturePath("overrides", "one", "child-one.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cwd: getFakeFixturePath("overrides"), - baseConfig: { - overrides: [{ - files: "one/**/*", - excludedFiles: ["two/**/*", "*two.js"], - rules: { - quotes: [2, "single"] - } - }] - }, - useEslintrc: false - }); - const expected = { - rules: { - quotes: [2, "single"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - - it("should cascade", () => { - const targetPath = getFakeFixturePath("overrides", "foo.js"); - const factory = new StubbedCascadingConfigArrayFactory({ - cwd: getFakeFixturePath("overrides"), - baseConfig: { - overrides: [ - { - files: "foo.js", - rules: { - semi: [2, "never"], - quotes: [2, "single"] - } - }, - { - files: "foo.js", - rules: { - semi: [2, "never"], - quotes: [2, "double"] - } - } - ] - }, - useEslintrc: false - }); - const expected = { - rules: { - semi: [2, "never"], - quotes: [2, "double"] - } - }; - const actual = getConfig(factory, targetPath); - - assertConfigsEqual(actual, expected); - }); - }); - - describe("deprecation warnings", () => { - const cwd = path.resolve(__dirname, "../../fixtures/config-file/"); - let warning = null; - - /** - * Store a reported warning object if that code starts with `ESLINT_`. - * @param {{code:string, message:string}} w The warning object to store. - * @returns {void} - */ - function onWarning(w) { - if (w.code.startsWith("ESLINT_")) { - warning = w; - } - } - - /** @type {CascadingConfigArrayFactory} */ - let factory; - - beforeEach(() => { - factory = new CascadingConfigArrayFactory({ cwd }); - warning = null; - process.on("warning", onWarning); - }); - afterEach(() => { - process.removeListener("warning", onWarning); - }); - - it("should emit a deprecation warning if 'ecmaFeatures' is given.", async () => { - getConfig(factory, "ecma-features/test.js"); - - // Wait for "warning" event. - await nextTick(); - - assert.notStrictEqual(warning, null); - assert.strictEqual( - warning.message, - `The 'ecmaFeatures' config file property is deprecated and has no effect. (found in "ecma-features${path.sep}.eslintrc.yml")` - ); - }); - }); - }); - }); - - describe("'clearCache()' method should clear cache.", () => { - describe("with a '.eslintrc.js' file", () => { - const root = path.join(os.tmpdir(), "eslint/cli-engine/cascading-config-array-factory"); - const files = { - ".eslintrc.js": "" - }; - const { - CascadingConfigArrayFactory - } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ cwd: () => root, files }); - - /** @type {Map} */ - let additionalPluginPool; - - /** @type {CascadingConfigArrayFactory} */ - let factory; - - beforeEach(() => { - additionalPluginPool = new Map(); - factory = new CascadingConfigArrayFactory({ - additionalPluginPool, - cliConfig: { plugins: ["test"] } - }); - }); - - it("should use cached instance.", () => { - const one = factory.getConfigArrayForFile("a.js"); - const two = factory.getConfigArrayForFile("a.js"); - - assert.strictEqual(one, two); - }); - - it("should not use cached instance if 'clearCache()' method is called after first config is retrieved", () => { - const one = factory.getConfigArrayForFile("a.js"); - - factory.clearCache(); - const two = factory.getConfigArrayForFile("a.js"); - - assert.notStrictEqual(one, two); - }); - - it("should have a loading error in CLI config.", () => { - const config = factory.getConfigArrayForFile("a.js"); - - assert.strictEqual(config[2].plugins.test.definition, null); - }); - - it("should not have a loading error in CLI config after adding 'test' plugin to the additional plugin pool then calling 'clearCache()'.", () => { - factory.getConfigArrayForFile("a.js"); - - additionalPluginPool.set("test", { configs: { name: "test" } }); - factory.clearCache(); - - // Check. - const config = factory.getConfigArrayForFile("a.js"); - - assert.deepStrictEqual( - config[2].plugins.test.definition, - { - configs: { name: "test" }, - environments: {}, - processors: {}, - rules: {} - } - ); - }); - }); - }); -}); diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index b4d65fa1163..9e7c49c84d7 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -16,33 +16,11 @@ const assert = require("chai").assert, fs = require("fs"), os = require("os"), hash = require("../../../lib/cli-engine/hash"), - { CascadingConfigArrayFactory } = require("../../../lib/cli-engine/cascading-config-array-factory"), - { unIndent } = require("../../_utils"); + { CascadingConfigArrayFactory } = require("@eslint/eslintrc/lib/cascading-config-array-factory"), + { unIndent, createCustomTeardown } = require("../../_utils"); const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); const fCache = require("file-entry-cache"); -const { createTeardown, addFile } = require("fs-teardown"); - -//----------------------------------------------------------------------------- -// Helpers -//----------------------------------------------------------------------------- - -/** - * Creates a new filesystem volume at the given location with the given files. - * @param {Object} desc A description of the filesystem volume to create. - * @param {string} desc.cwd The current working directory ESLint is using. - * @param {Object} desc.files A map of filename to file contents to create. - * @returns {Teardown} An object with prepare(), cleanup(), and getPath() - * methods. - */ -function createCustomTeardown({ cwd, files }) { - const { prepare, cleanup, getPath } = createTeardown( - cwd, - ...Object.keys(files).map(filename => addFile(filename, files[filename])) - ); - - return { prepare, cleanup, getPath }; -} //------------------------------------------------------------------------------ // Tests @@ -1315,6 +1293,10 @@ describe("CLIEngine", () => { it("should throw an error when all given files are ignored", () => { + engine = new CLIEngine({ + ignorePath: getFixturePath(".eslintignore") + }); + assert.throws(() => { engine.executeOnFiles(["tests/fixtures/cli-engine/"]); }, "All files matched by 'tests/fixtures/cli-engine/' are ignored."); @@ -3747,10 +3729,9 @@ describe("CLIEngine", () => { } }); - engine = new CLIEngine({ cwd: teardown.getPath() }); - await teardown.prepare(); cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath() }); const { results } = engine.executeOnFiles(["test.js"]); const messages = results[0].messages; @@ -3770,10 +3751,9 @@ describe("CLIEngine", () => { } }); - engine = new CLIEngine({ cwd: teardown.getPath() }); - await teardown.prepare(); cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath() }); const { results } = engine.executeOnFiles(["test.js"]); const messages = results[0].messages; @@ -3803,10 +3783,9 @@ describe("CLIEngine", () => { } }); - engine = new CLIEngine({ cwd: teardown.getPath() }); - await teardown.prepare(); cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath() }); const { results } = engine.executeOnFiles(["test.js"]); const messages = results[0].messages; @@ -3826,14 +3805,14 @@ describe("CLIEngine", () => { } }); + await teardown.prepare(); + cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath(), reportUnusedDisableDirectives: "off" }); - await teardown.prepare(); - cleanup = teardown.cleanup; - const { results } = engine.executeOnFiles(["test.js"]); const messages = results[0].messages; @@ -3849,14 +3828,14 @@ describe("CLIEngine", () => { } }); + await teardown.prepare(); + cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath(), reportUnusedDisableDirectives: "error" }); - await teardown.prepare(); - cleanup = teardown.cleanup; - const { results } = engine.executeOnFiles(["test.js"]); const messages = results[0].messages; @@ -3930,10 +3909,9 @@ describe("CLIEngine", () => { } }); - engine = new CLIEngine({ cwd: teardown.getPath() }); - await teardown.prepare(); cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath() }); // Don't throw "failed to load config file" error. engine.executeOnFiles("."); @@ -3949,10 +3927,10 @@ describe("CLIEngine", () => { } }); - engine = new CLIEngine({ cwd: teardown.getPath() }); - await teardown.prepare(); cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath() }); + // Don't throw "file not found" error. engine.executeOnFiles("."); @@ -3968,10 +3946,9 @@ describe("CLIEngine", () => { } }); - engine = new CLIEngine({ cwd: teardown.getPath() }); - await teardown.prepare(); cleanup = teardown.cleanup; + engine = new CLIEngine({ cwd: teardown.getPath() }); // Don't throw "file not found" error. engine.executeOnFiles("subdir"); diff --git a/tests/lib/cli-engine/config-array-factory.js b/tests/lib/cli-engine/config-array-factory.js deleted file mode 100644 index 6df3d98372b..00000000000 --- a/tests/lib/cli-engine/config-array-factory.js +++ /dev/null @@ -1,2446 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview Tests for ConfigArrayFactory class. - * @author Toru Nagashima - */ -"use strict"; - -const os = require("os"); -const path = require("path"); -const { assert } = require("chai"); -const { spy } = require("sinon"); -const { ConfigArray } = require("../../../lib/cli-engine/config-array"); -const { OverrideTester } = require("../../../lib/cli-engine/config-array"); -const { createContext } = require("../../../lib/cli-engine/config-array-factory"); -const { defineConfigArrayFactoryWithInMemoryFileSystem } = require("../../_utils"); - -const tempDir = path.join(os.tmpdir(), "eslint/config-array-factory"); - -// For VSCode intellisense. -/** @typedef {InstanceType["ConfigArrayFactory"]>} ConfigArrayFactory */ - -/** - * Assert a config array element. - * @param {Object} actual The actual value. - * @param {Object} providedExpected The expected value. - * @returns {void} - */ -function assertConfigArrayElement(actual, providedExpected) { - const expected = { - name: "", - filePath: "", - criteria: null, - env: void 0, - globals: void 0, - ignorePattern: void 0, - noInlineConfig: void 0, - parser: void 0, - parserOptions: void 0, - plugins: void 0, - processor: void 0, - reportUnusedDisableDirectives: void 0, - root: void 0, - rules: void 0, - settings: void 0, - type: "config", - ...providedExpected - }; - - assert.deepStrictEqual(actual, expected); -} - -/** - * Assert a config array element. - * @param {Object} actual The actual value. - * @param {Object} providedExpected The expected value. - * @returns {void} - */ -function assertConfig(actual, providedExpected) { - const expected = { - env: {}, - globals: {}, - ignorePatterns: [], - noInlineConfig: void 0, - parser: null, - parserOptions: {}, - plugins: [], - reportUnusedDisableDirectives: void 0, - rules: {}, - settings: {}, - ...providedExpected - }; - - assert.deepStrictEqual(actual, expected); -} - -/** - * Assert a plugin definition. - * @param {Object} actual The actual value. - * @param {Object} providedExpected The expected value. - * @returns {void} - */ -function assertPluginDefinition(actual, providedExpected) { - const expected = { - configs: {}, - environments: {}, - processors: {}, - rules: {}, - ...providedExpected - }; - - assert.deepStrictEqual(actual, expected); -} - -describe("ConfigArrayFactory", () => { - describe("'create(configData, options)' method should normalize the config data.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir - }); - - /** @type {ConfigArrayFactory} */ - let factory; - - beforeEach(() => { - factory = new ConfigArrayFactory(); - }); - - it("should return an empty config array if 'configData' is null.", () => { - assert.strictEqual(factory.create(null).length, 0); - }); - - it("should throw an error if the config data had invalid properties,", () => { - assert.throws(() => { - factory.create({ files: true }); - }, /Unexpected top-level property "files"/u); - }); - - it("should call '_normalizeConfigData(configData, ctx)' with given arguments.", () => { - const configData = {}; - const basePath = tempDir; - const filePath = __filename; - const name = "example"; - const normalizeConfigData = spy(factory, "_normalizeConfigData"); - - factory.create(configData, { basePath, filePath, name }); - - assert.strictEqual(normalizeConfigData.callCount, 1); - assert.deepStrictEqual(normalizeConfigData.args[0], [ - configData, - createContext({ cwd: tempDir }, void 0, name, filePath, basePath) - ]); - }); - - it("should return a config array that contains the yielded elements from '_normalizeConfigData(configData, ctx)'.", () => { - const elements = [{}, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.create({}); - - assert.strictEqual(configArray.length, 2); - assert.strictEqual(configArray[0], elements[0]); - assert.strictEqual(configArray[1], elements[1]); - }); - }); - - describe("'loadFile(filePath, options)' method should load a config file.", () => { - const basicFiles = { - "js/.eslintrc.js": "exports.settings = { name: 'js/.eslintrc.js' }", - "cjs/.eslintrc.cjs": "exports.settings = { name: 'cjs/.eslintrc.cjs' }", - "json/.eslintrc.json": "{ \"settings\": { \"name\": \"json/.eslintrc.json\" } }", - "legacy-json/.eslintrc": "{ \"settings\": { \"name\": \"legacy-json/.eslintrc\" } }", - "legacy-yml/.eslintrc": "settings:\n name: legacy-yml/.eslintrc", - "package-json/package.json": "{ \"eslintConfig\": { \"settings\": { \"name\": \"package-json/package.json\" } } }", - "yml/.eslintrc.yml": "settings:\n name: yml/.eslintrc.yml", - "yaml/.eslintrc.yaml": "settings:\n name: yaml/.eslintrc.yaml" - }; - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - ...basicFiles, - "invalid-property.json": "{ \"files\": \"*.js\" }", - "package-json-no-config/package.json": "{ \"name\": \"foo\" }" - } - }); - - /** @type {ConfigArrayFactory} */ - let factory; - - beforeEach(() => { - factory = new ConfigArrayFactory(); - }); - - it("should throw an error if 'filePath' is null.", () => { - assert.throws(() => factory.loadFile(null)); - }); - - it("should throw an error if 'filePath' doesn't exist.", () => { - assert.throws(() => { - factory.loadFile("non-exist"); - }, /Cannot read config file:.*non-exist/su); - }); - - it("should throw an error if 'filePath' was 'package.json' and it doesn't have 'eslintConfig' field.", () => { - assert.throws(() => { - factory.loadFile("package-json-no-config/package.json"); - }, /Cannot read config file:.*package.json/su); - }); - - it("should throw an error if the config data had invalid properties,", () => { - assert.throws(() => { - factory.loadFile("invalid-property.json"); - }, /Unexpected top-level property "files"/u); - }); - - for (const filePath of Object.keys(basicFiles)) { - it(`should load '${filePath}' then return a config array what contains that file content.`, () => { // eslint-disable-line no-loop-func - const configArray = factory.loadFile(filePath); - - assert.strictEqual(configArray.length, 1); - assertConfigArrayElement(configArray[0], { - filePath: path.resolve(tempDir, filePath), - name: path.relative(tempDir, path.resolve(tempDir, filePath)), - settings: { name: filePath } - }); - }); - } - - it("should call '_normalizeConfigData(configData, ctx)' with the loaded config data and given options.", () => { - const basePath = tempDir; - const filePath = "js/.eslintrc.js"; - const name = "example"; - const normalizeConfigData = spy(factory, "_normalizeConfigData"); - - factory.loadFile(filePath, { basePath, name }); - - assert.strictEqual(normalizeConfigData.callCount, 1); - assert.deepStrictEqual(normalizeConfigData.args[0], [ - { settings: { name: filePath } }, - createContext({ cwd: tempDir }, void 0, name, filePath, basePath) - ]); - }); - - it("should return a config array that contains the yielded elements from '_normalizeConfigData(configData, ctx)'.", () => { - const elements = [{}, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.loadFile("js/.eslintrc.js"); - - assert.strictEqual(configArray.length, 2); - assert.strictEqual(configArray[0], elements[0]); - assert.strictEqual(configArray[1], elements[1]); - }); - }); - - describe("'loadInDirectory(directoryPath, options)' method should load the config file of a directory.", () => { - const basicFiles = { - "js/.eslintrc.js": "exports.settings = { name: 'js/.eslintrc.js' }", - "cjs/.eslintrc.cjs": "exports.settings = { name: 'cjs/.eslintrc.cjs' }", - "json/.eslintrc.json": "{ \"settings\": { \"name\": \"json/.eslintrc.json\" } }", - "legacy-json/.eslintrc": "{ \"settings\": { \"name\": \"legacy-json/.eslintrc\" } }", - "legacy-yml/.eslintrc": "settings:\n name: legacy-yml/.eslintrc", - "package-json/package.json": "{ \"eslintConfig\": { \"settings\": { \"name\": \"package-json/package.json\" } } }", - "yml/.eslintrc.yml": "settings:\n name: yml/.eslintrc.yml", - "yaml/.eslintrc.yaml": "settings:\n name: yaml/.eslintrc.yaml" - }; - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - ...basicFiles, - "invalid-property/.eslintrc.json": "{ \"files\": \"*.js\" }", - "package-json-no-config/package.json": "{ \"name\": \"foo\" }" - } - }); - - /** @type {ConfigArrayFactory} */ - let factory; - - beforeEach(() => { - factory = new ConfigArrayFactory(); - }); - - it("should throw an error if 'directoryPath' is null.", () => { - assert.throws(() => factory.loadInDirectory(null)); - }); - - it("should return an empty config array if the config file of 'directoryPath' doesn't exist.", () => { - assert.strictEqual(factory.loadInDirectory("non-exist").length, 0); - }); - - it("should return an empty config array if the config file of 'directoryPath' was package.json and it didn't have 'eslintConfig' field.", () => { - assert.strictEqual(factory.loadInDirectory("package-json-no-config").length, 0); - }); - - it("should throw an error if the config data had invalid properties,", () => { - assert.throws(() => { - factory.loadInDirectory("invalid-property"); - }, /Unexpected top-level property "files"/u); - }); - - for (const filePath of Object.keys(basicFiles)) { - const directoryPath = filePath.split("/")[0]; - - it(`should load '${directoryPath}' then return a config array what contains the config file of that directory.`, () => { // eslint-disable-line no-loop-func - const configArray = factory.loadInDirectory(directoryPath); - - assert.strictEqual(configArray.length, 1); - assertConfigArrayElement(configArray[0], { - filePath: path.resolve(tempDir, filePath), - name: path.relative(tempDir, path.resolve(tempDir, filePath)), - settings: { name: filePath } - }); - }); - } - - it("should call '_normalizeConfigData(configData, ctx)' with the loaded config data and given options.", () => { - const basePath = tempDir; - const directoryPath = "js"; - const name = "example"; - const normalizeConfigData = spy(factory, "_normalizeConfigData"); - - factory.loadInDirectory(directoryPath, { basePath, name }); - - assert.strictEqual(normalizeConfigData.callCount, 1); - assert.deepStrictEqual(normalizeConfigData.args[0], [ - { settings: { name: `${directoryPath}/.eslintrc.js` } }, - createContext({ cwd: tempDir }, void 0, name, path.join(directoryPath, ".eslintrc.js"), basePath) - ]); - }); - - it("should return a config array that contains the yielded elements from '_normalizeConfigData(configData, ctx)'.", () => { - const elements = [{}, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.loadInDirectory("js"); - - assert.strictEqual(configArray.length, 2); - assert.strictEqual(configArray[0], elements[0]); - assert.strictEqual(configArray[1], elements[1]); - }); - }); - - /* - * All of `create`, `loadFile`, and `loadInDirectory` call this method. - * So this section tests the common part of the three. - */ - describe("'_normalizeConfigData(configData, ctx)' method should normalize the config data.", () => { - - /** @type {ConfigArrayFactory} */ - let factory = null; - - /** - * Call `_normalizeConfigData` method with given arguments. - * @param {ConfigData} configData The config data to normalize. - * @param {Object} [options] The options. - * @param {string} [options.filePath] The path to the config file of the config data. - * @param {string} [options.name] The name of the config file of the config data. - * @returns {ConfigArray} The created config array. - */ - function create(configData, { filePath, name } = {}) { - const ctx = createContext({ cwd: tempDir }, void 0, name, filePath, void 0); - - return new ConfigArray(...factory._normalizeConfigData(configData, ctx)); // eslint-disable-line no-underscore-dangle - } - - describe("misc", () => { - before(() => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir - }); - - factory = new ConfigArrayFactory(); - }); - - describe("if the config data was empty, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create({}); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the default values in the element.", () => { - assertConfigArrayElement(configArray[0], {}); - }); - }); - - describe("if the config data had 'env' property, the returned value", () => { - const env = { node: true }; - let configArray; - - beforeEach(() => { - configArray = create({ env }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'env' value in the element.", () => { - assertConfigArrayElement(configArray[0], { env }); - }); - }); - - describe("if the config data had 'globals' property, the returned value", () => { - const globals = { window: "readonly" }; - let configArray; - - beforeEach(() => { - configArray = create({ globals }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'globals' value in the element.", () => { - assertConfigArrayElement(configArray[0], { globals }); - }); - }); - - describe("if the config data had 'parser' property, the returned value", () => { - const parser = "espree"; - let configArray; - - beforeEach(() => { - configArray = create({ parser }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'parser' value in the element.", () => { - assert.strictEqual(configArray[0].parser.id, parser); - }); - }); - - describe("if the config data had 'parserOptions' property, the returned value", () => { - const parserOptions = { ecmaVersion: 2015 }; - let configArray; - - beforeEach(() => { - configArray = create({ parserOptions }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'parserOptions' value in the element.", () => { - assertConfigArrayElement(configArray[0], { parserOptions }); - }); - }); - - describe("if the config data had 'plugins' property, the returned value", () => { - const plugins = []; - let configArray; - - beforeEach(() => { - configArray = create({ plugins }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'plugins' value in the element.", () => { - assertConfigArrayElement(configArray[0], { plugins: {} }); - }); - }); - - describe("if the config data had 'root' property, the returned value", () => { - const root = true; - let configArray; - - beforeEach(() => { - configArray = create({ root }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'root' value in the element.", () => { - assertConfigArrayElement(configArray[0], { root }); - }); - }); - - describe("if the config data had 'rules' property, the returned value", () => { - const rules = { eqeqeq: "error" }; - let configArray; - - beforeEach(() => { - configArray = create({ rules }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'rules' value in the element.", () => { - assertConfigArrayElement(configArray[0], { rules }); - }); - }); - - describe("if the config data had 'settings' property, the returned value", () => { - const settings = { foo: 777 }; - let configArray; - - beforeEach(() => { - configArray = create({ settings }); - }); - - it("should have an element.", () => { - assert.strictEqual(configArray.length, 1); - }); - - it("should have the 'settings' value in the element.", () => { - assertConfigArrayElement(configArray[0], { settings }); - }); - }); - }); - - describe("'parser' details", () => { - before(() => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "node_modules/xxx-parser/index.js": "exports.name = 'xxx-parser';", - "subdir/node_modules/xxx-parser/index.js": "exports.name = 'subdir/xxx-parser';", - "parser.js": "exports.name = './parser.js';" - } - }); - - factory = new ConfigArrayFactory(); - }); - - describe("if the 'parser' property was a valid package, the first config array element", () => { - let element; - - beforeEach(() => { - element = create({ parser: "xxx-parser" })[0]; - }); - - it("should have the package ID at 'parser.id' property.", () => { - assert.strictEqual(element.parser.id, "xxx-parser"); - }); - - it("should have the package object at 'parser.definition' property.", () => { - assert.deepStrictEqual(element.parser.definition, { name: "xxx-parser" }); - }); - - it("should have the path to the package at 'parser.filePath' property.", () => { - assert.strictEqual(element.parser.filePath, path.join(tempDir, "node_modules/xxx-parser/index.js")); - }); - }); - - describe("if the 'parser' property was an invalid package, the first config array element", () => { - let element; - - beforeEach(() => { - element = create({ parser: "invalid-parser" })[0]; - }); - - it("should have the package ID at 'parser.id' property.", () => { - assert.strictEqual(element.parser.id, "invalid-parser"); - }); - - it("should have the loading error at 'parser.error' property.", () => { - assert.match(element.parser.error.message, /Cannot find module 'invalid-parser'/u); - }); - }); - - describe("if the 'parser' property was a valid relative path, the first config array element", () => { - let element; - - beforeEach(() => { - element = create({ parser: "./parser" })[0]; - }); - - it("should have the given path at 'parser.id' property.", () => { - assert.strictEqual(element.parser.id, "./parser"); - }); - - it("should have the file's object at 'parser.definition' property.", () => { - assert.deepStrictEqual(element.parser.definition, { name: "./parser.js" }); - }); - - it("should have the absolute path to the file at 'parser.filePath' property.", () => { - assert.strictEqual(element.parser.filePath, path.join(tempDir, "./parser.js")); - }); - }); - - describe("if the 'parser' property was an invalid relative path, the first config array element", () => { - let element; - - beforeEach(() => { - element = create({ parser: "./invalid-parser" })[0]; - }); - - it("should have the given path at 'parser.id' property.", () => { - assert.strictEqual(element.parser.id, "./invalid-parser"); - }); - - it("should have the loading error at 'parser.error' property.", () => { - assert.match(element.parser.error.message, /Cannot find module '.\/invalid-parser'/u); - }); - }); - - describe("if 'parser' property was given and 'filePath' option was given, the parser", () => { - let element; - - beforeEach(() => { - element = create( - { parser: "xxx-parser" }, - { filePath: path.join(tempDir, "subdir/.eslintrc") } - )[0]; - }); - - it("should be resolved relative to the 'filePath' option.", () => { - assert.strictEqual( - element.parser.filePath, - - // rather than "xxx-parser" at the project root. - path.join(tempDir, "subdir/node_modules/xxx-parser/index.js") - ); - }); - }); - }); - - describe("'plugins' details", () => { - before(() => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "node_modules/eslint-plugin-ext/index.js": "exports.processors = { '.abc': {}, '.xyz': {}, other: {} };", - "node_modules/eslint-plugin-subdir/index.js": "", - "node_modules/eslint-plugin-xxx/index.js": "exports.configs = { name: 'eslint-plugin-xxx' };", - "subdir/node_modules/eslint-plugin-subdir/index.js": "", - "parser.js": "" - } - }); - - factory = new ConfigArrayFactory(); - }); - - it("should throw an error if a 'plugins' value is a file path.", () => { - assert.throws(() => { - create({ plugins: ["./path/to/plugin"] }); - }, /Plugins array cannot includes file paths/u); - }); - - describe("if the 'plugins' property was a valid package, the first config array element", () => { - let element; - - beforeEach(() => { - element = create({ plugins: ["xxx"] })[0]; - }); - - it("should have 'plugins[id]' property.", () => { - assert.notStrictEqual(element.plugins.xxx, void 0); - }); - - it("should have the package ID at 'plugins[id].id' property.", () => { - assert.strictEqual(element.plugins.xxx.id, "xxx"); - }); - - it("should have the package object at 'plugins[id].definition' property.", () => { - assertPluginDefinition( - element.plugins.xxx.definition, - { configs: { name: "eslint-plugin-xxx" } } - ); - }); - - it("should have the path to the package at 'plugins[id].filePath' property.", () => { - assert.strictEqual(element.plugins.xxx.filePath, path.join(tempDir, "node_modules/eslint-plugin-xxx/index.js")); - }); - }); - - describe("if the 'plugins' property was an invalid package, the first config array element", () => { - let element; - - beforeEach(() => { - element = create({ plugins: ["invalid"] })[0]; - }); - - it("should have 'plugins[id]' property.", () => { - assert.notStrictEqual(element.plugins.invalid, void 0); - }); - - it("should have the package ID at 'plugins[id].id' property.", () => { - assert.strictEqual(element.plugins.invalid.id, "invalid"); - }); - - it("should have the loading error at 'plugins[id].error' property.", () => { - assert.match(element.plugins.invalid.error.message, /Cannot find module 'eslint-plugin-invalid'/u); - }); - }); - - describe("even if 'plugins' property was given and 'filePath' option was given,", () => { - it("should load the plugin from 'subdir'.", () => { - const configArray = create( - { plugins: ["subdir"] }, - { filePath: path.resolve(tempDir, "subdir/a.js") } - ); - - assert.strictEqual( - configArray[0].plugins.subdir.filePath, - path.resolve(tempDir, "subdir/node_modules/eslint-plugin-subdir/index.js") - ); - }); - }); - - describe("if 'plugins' property was given and the plugin has two file extension processors, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create({ plugins: ["ext"] }); - }); - - it("should have three elements.", () => { - assert.strictEqual(configArray.length, 3); - }); - - describe("the first element", () => { - let element; - - beforeEach(() => { - element = configArray[0]; - }); - - it("should be named '#processors[\"ext/.abc\"]'.", () => { - assert.strictEqual(element.name, "#processors[\"ext/.abc\"]"); - }); - - it("should not have 'plugins' property.", () => { - assert.strictEqual(element.plugins, void 0); - }); - - it("should have 'processor' property.", () => { - assert.strictEqual(element.processor, "ext/.abc"); - }); - - it("should have 'criteria' property which matches '.abc'.", () => { - assert.strictEqual(element.criteria.test(path.join(tempDir, "1234.abc")), true); - assert.strictEqual(element.criteria.test(path.join(tempDir, "1234.xyz")), false); - }); - }); - - describe("the second element", () => { - let element; - - beforeEach(() => { - element = configArray[1]; - }); - - it("should be named '#processors[\"ext/.xyz\"]'.", () => { - assert.strictEqual(element.name, "#processors[\"ext/.xyz\"]"); - }); - - it("should not have 'plugins' property.", () => { - assert.strictEqual(element.plugins, void 0); - }); - - it("should have 'processor' property.", () => { - assert.strictEqual(element.processor, "ext/.xyz"); - }); - - it("should have 'criteria' property which matches '.xyz'.", () => { - assert.strictEqual(element.criteria.test(path.join(tempDir, "1234.abc")), false); - assert.strictEqual(element.criteria.test(path.join(tempDir, "1234.xyz")), true); - }); - }); - - describe("the third element", () => { - let element; - - beforeEach(() => { - element = configArray[2]; - }); - - it("should have 'plugins' property.", () => { - assert.strictEqual(element.plugins.ext.id, "ext"); - }); - - it("should not have 'processor' property.", () => { - assert.strictEqual(element.processor, void 0); - }); - }); - }); - }); - - describe("'extends' details", () => { - before(() => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "node_modules/eslint-config-foo/index.js": "exports.env = { browser: true }", - "node_modules/eslint-config-one/index.js": "module.exports = { extends: 'two', env: { browser: true } }", - "node_modules/eslint-config-two/index.js": "module.exports = { env: { node: true } }", - "node_modules/eslint-config-override/index.js": ` - module.exports = { - rules: { regular: 1 }, - overrides: [ - { files: '*.xxx', rules: { override: 1 } }, - { files: '*.yyy', rules: { override: 2 } } - ] - } - `, - "node_modules/eslint-plugin-foo/index.js": "exports.configs = { bar: { env: { es6: true } } }", - "node_modules/eslint-plugin-invalid-config/index.js": "exports.configs = { foo: {} }", - "node_modules/eslint-plugin-error/index.js": "throw new Error('xxx error')", - "base.js": "module.exports = { rules: { semi: [2, 'always'] } };" - } - }); - - factory = new ConfigArrayFactory(); - }); - - it("should throw an error when extends config module is not found", () => { - assert.throws(() => { - create({ - extends: "not-exist", - rules: { eqeqeq: 2 } - }); - }, /Failed to load config "not-exist" to extend from./u); - }); - - it("should throw an error when an eslint config is not found", () => { - assert.throws(() => { - create({ - extends: "eslint:foo", - rules: { eqeqeq: 2 } - }); - }, /Failed to load config "eslint:foo" to extend from./u); - }); - - it("should throw an error when a plugin threw while loading.", () => { - assert.throws(() => { - create({ - extends: "plugin:error/foo", - rules: { eqeqeq: 2 } - }); - }, /xxx error/u); - }); - - it("should throw an error when a plugin extend is a file path.", () => { - assert.throws(() => { - create({ - extends: "plugin:./path/to/foo", - rules: { eqeqeq: 2 } - }); - }, /'extends' cannot use a file path for plugins/u); - }); - - it("should throw an error when an eslint config is not found", () => { - assert.throws(() => { - create({ - extends: "eslint:foo", - rules: { eqeqeq: 2 } - }); - }, /Failed to load config "eslint:foo" to extend from./u); - }); - - describe("if 'extends' property was 'eslint:all', the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create( - { extends: "eslint:all", rules: { eqeqeq: 1 } }, - { name: ".eslintrc" } - ); - }); - - it("should have two elements.", () => { - assert.strictEqual(configArray.length, 2); - }); - - it("should have the config data of 'eslint:all' at the first element.", () => { - assertConfigArrayElement(configArray[0], { - name: ".eslintrc » eslint:all", - filePath: require.resolve("../../../conf/eslint-all.js"), - ...require("../../../conf/eslint-all.js") - }); - }); - - it("should have the given config data at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: ".eslintrc", - rules: { eqeqeq: 1 } - }); - }); - }); - - describe("if 'extends' property was 'eslint:recommended', the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create( - { extends: "eslint:recommended", rules: { eqeqeq: 1 } }, - { name: ".eslintrc" } - ); - }); - - it("should have two elements.", () => { - assert.strictEqual(configArray.length, 2); - }); - - it("should have the config data of 'eslint:recommended' at the first element.", () => { - assertConfigArrayElement(configArray[0], { - name: ".eslintrc » eslint:recommended", - filePath: require.resolve("../../../conf/eslint-recommended.js"), - ...require("../../../conf/eslint-recommended.js") - }); - }); - - it("should have the given config data at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: ".eslintrc", - rules: { eqeqeq: 1 } - }); - }); - }); - - describe("if 'extends' property was 'foo', the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create( - { extends: "foo", rules: { eqeqeq: 1 } }, - { name: ".eslintrc" } - ); - }); - - it("should have two elements.", () => { - assert.strictEqual(configArray.length, 2); - }); - - it("should have the config data of 'eslint-config-foo' at the first element.", () => { - assertConfigArrayElement(configArray[0], { - name: ".eslintrc » eslint-config-foo", - filePath: path.join(tempDir, "node_modules/eslint-config-foo/index.js"), - env: { browser: true } - }); - }); - - it("should have the given config data at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: ".eslintrc", - rules: { eqeqeq: 1 } - }); - }); - }); - - describe("if 'extends' property was 'plugin:foo/bar', the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create( - { extends: "plugin:foo/bar", rules: { eqeqeq: 1 } }, - { name: ".eslintrc" } - ); - }); - - it("should have two elements.", () => { - assert.strictEqual(configArray.length, 2); - }); - - it("should have the config data of 'plugin:foo/bar' at the first element.", () => { - assertConfigArrayElement(configArray[0], { - name: ".eslintrc » plugin:foo/bar", - filePath: path.join(tempDir, "node_modules/eslint-plugin-foo/index.js"), - env: { es6: true } - }); - }); - - it("should have the given config data at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: ".eslintrc", - rules: { eqeqeq: 1 } - }); - }); - }); - - describe("if 'extends' property was './base', the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create( - { extends: "./base", rules: { eqeqeq: 1 } }, - { name: ".eslintrc" } - ); - }); - - it("should have two elements.", () => { - assert.strictEqual(configArray.length, 2); - }); - - it("should have the config data of './base' at the first element.", () => { - assertConfigArrayElement(configArray[0], { - name: ".eslintrc » ./base", - filePath: path.join(tempDir, "base.js"), - rules: { semi: [2, "always"] } - }); - }); - - it("should have the given config data at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: ".eslintrc", - rules: { eqeqeq: 1 } - }); - }); - }); - - describe("if 'extends' property was 'one' and the 'one' extends 'two', the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create( - { extends: "one", rules: { eqeqeq: 1 } }, - { name: ".eslintrc" } - ); - }); - - it("should have three elements.", () => { - assert.strictEqual(configArray.length, 3); - }); - - it("should have the config data of 'eslint-config-two' at the first element.", () => { - assertConfigArrayElement(configArray[0], { - name: ".eslintrc » eslint-config-one » eslint-config-two", - filePath: path.join(tempDir, "node_modules/eslint-config-two/index.js"), - env: { node: true } - }); - }); - - it("should have the config data of 'eslint-config-one' at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: ".eslintrc » eslint-config-one", - filePath: path.join(tempDir, "node_modules/eslint-config-one/index.js"), - env: { browser: true } - }); - }); - - it("should have the given config data at the third element.", () => { - assertConfigArrayElement(configArray[2], { - name: ".eslintrc", - rules: { eqeqeq: 1 } - }); - }); - }); - - describe("if 'extends' property was 'override' and the 'override' has 'overrides' property, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create( - { extends: "override", rules: { eqeqeq: 1 } }, - { name: ".eslintrc" } - ); - }); - - it("should have four elements.", () => { - assert.strictEqual(configArray.length, 4); - }); - - it("should have the config data of 'eslint-config-override' at the first element.", () => { - assertConfigArrayElement(configArray[0], { - name: ".eslintrc » eslint-config-override", - filePath: path.join(tempDir, "node_modules/eslint-config-override/index.js"), - rules: { regular: 1 } - }); - }); - - it("should have the 'overrides[0]' config data of 'eslint-config-override' at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: ".eslintrc » eslint-config-override#overrides[0]", - filePath: path.join(tempDir, "node_modules/eslint-config-override/index.js"), - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { override: 1 } - }); - }); - - it("should have the 'overrides[1]' config data of 'eslint-config-override' at the third element.", () => { - assertConfigArrayElement(configArray[2], { - name: ".eslintrc » eslint-config-override#overrides[1]", - filePath: path.join(tempDir, "node_modules/eslint-config-override/index.js"), - criteria: OverrideTester.create(["*.yyy"], [], tempDir), - rules: { override: 2 } - }); - }); - - it("should have the given config data at the fourth element.", () => { - assertConfigArrayElement(configArray[3], { - name: ".eslintrc", - rules: { eqeqeq: 1 } - }); - }); - }); - }); - - describe("'overrides' details", () => { - before(() => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "node_modules/eslint-config-foo/index.js": ` - module.exports = { - rules: { eqeqeq: "error" } - } - `, - "node_modules/eslint-config-has-overrides/index.js": ` - module.exports = { - rules: { eqeqeq: "error" }, - overrides: [ - { - files: ["**/foo/**/*.js"], - rules: { eqeqeq: "off" } - } - ] - } - `, - "node_modules/eslint-config-root/index.js": ` - module.exports = { - root: true, - rules: { eqeqeq: "error" } - } - ` - } - }); - - factory = new ConfigArrayFactory(); - }); - - describe("if 'overrides' property was given, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create({ - rules: { regular: 1 }, - overrides: [ - { files: "*.xxx", rules: { override: 1 } }, - { files: "*.yyy", rules: { override: 2 } } - ] - }); - }); - - it("should have three elements.", () => { - assert.strictEqual(configArray.length, 3); - }); - - it("should have the given config data at the first element.", () => { - assertConfigArrayElement(configArray[0], { - rules: { regular: 1 } - }); - }); - - it("should have the config data of 'overrides[0]' at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: "#overrides[0]", - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { override: 1 } - }); - }); - - it("should have the config data of 'overrides[1]' at the third element.", () => { - assertConfigArrayElement(configArray[2], { - name: "#overrides[1]", - criteria: OverrideTester.create(["*.yyy"], [], tempDir), - rules: { override: 2 } - }); - }); - }); - - describe("if a config in 'overrides' property had 'extends' property, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create({ - rules: { regular: 1 }, - overrides: [ - { - files: "*.xxx", - extends: "foo", - rules: { override: 1 } - } - ] - }); - }); - - it("should have three elements.", () => { - assert.strictEqual(configArray.length, 3); - }); - - it("should have the given config data at the first element.", () => { - assertConfigArrayElement(configArray[0], { - rules: { regular: 1 } - }); - }); - - it("should have the config data of 'overrides[0] » eslint-config-foo' at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: "#overrides[0] » eslint-config-foo", - filePath: path.join(tempDir, "node_modules/eslint-config-foo/index.js"), - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { eqeqeq: "error" } - }); - }); - - it("should have the config data of 'overrides[0]' at the third element.", () => { - assertConfigArrayElement(configArray[2], { - name: "#overrides[0]", - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { override: 1 } - }); - }); - }); - - describe("if a config in 'overrides' property had 'extends' property and the shareable config has 'overrides' property, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create({ - rules: { regular: 1 }, - overrides: [ - { - files: "*.xxx", - extends: "has-overrides", - rules: { override: 1 } - } - ] - }); - }); - - it("should have four elements.", () => { - assert.strictEqual(configArray.length, 4); - }); - - it("should have the given config data at the first element.", () => { - assertConfigArrayElement(configArray[0], { - rules: { regular: 1 } - }); - }); - - it("should have the config data of 'overrides[0] » eslint-config-has-overrides' at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: "#overrides[0] » eslint-config-has-overrides", - filePath: path.join(tempDir, "node_modules/eslint-config-has-overrides/index.js"), - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { eqeqeq: "error" } - }); - }); - - it("should have the config data of 'overrides[0] » eslint-config-has-overrides#overrides[0]' at the third element.", () => { - assertConfigArrayElement(configArray[2], { - name: "#overrides[0] » eslint-config-has-overrides#overrides[0]", - filePath: path.join(tempDir, "node_modules/eslint-config-has-overrides/index.js"), - criteria: OverrideTester.and( - OverrideTester.create(["*.xxx"], [], tempDir), - OverrideTester.create(["**/foo/**/*.js"], [], tempDir) - ), - rules: { eqeqeq: "off" } - }); - }); - - it("should have the config data of 'overrides[0]' at the fourth element.", () => { - assertConfigArrayElement(configArray[3], { - name: "#overrides[0]", - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { override: 1 } - }); - }); - }); - - describe("if a config in 'overrides' property had 'overrides' property, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create({ - rules: { regular: 1 }, - overrides: [ - { - files: "*.xxx", - rules: { override: 1 }, - overrides: [ - { - files: "*.yyy", - rules: { override: 2 } - } - ] - } - ] - }); - }); - - it("should have three elements.", () => { - assert.strictEqual(configArray.length, 3); - }); - - it("should have the given config data at the first element.", () => { - assertConfigArrayElement(configArray[0], { - rules: { regular: 1 } - }); - }); - - it("should have the config data of 'overrides[0]' at the second element.", () => { - assertConfigArrayElement(configArray[1], { - name: "#overrides[0]", - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { override: 1 } - }); - }); - - it("should have the config data of 'overrides[0].overrides[0]' at the third element.", () => { - assertConfigArrayElement(configArray[2], { - name: "#overrides[0]#overrides[0]", - criteria: OverrideTester.and( - OverrideTester.create(["*.xxx"], [], tempDir), - OverrideTester.create(["*.yyy"], [], tempDir) - ), - rules: { override: 2 } - }); - }); - }); - - describe("if a config in 'overrides' property had 'root' property, the returned value", () => { - let configArray; - - beforeEach(() => { - configArray = create({ - rules: { regular: 1 }, - overrides: [ - { - files: "*.xxx", - extends: "root", - rules: { override: 1 } - } - ] - }); - }); - - it("should have three elements.", () => { - assert.strictEqual(configArray.length, 3); - }); - - it("should have the given config data at the first element.", () => { - assertConfigArrayElement(configArray[0], { - rules: { regular: 1 } - }); - }); - - it("should have the config data of 'overrides[0] » eslint-config-root' at the second element; it doesn't have 'root' property.", () => { - assertConfigArrayElement(configArray[1], { - name: "#overrides[0] » eslint-config-root", - filePath: path.join(tempDir, "node_modules/eslint-config-root/index.js"), - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { eqeqeq: "error" } - }); - }); - - it("should have the config data of 'overrides[0]' at the third element.", () => { - assertConfigArrayElement(configArray[2], { - name: "#overrides[0]", - criteria: OverrideTester.create(["*.xxx"], [], tempDir), - rules: { override: 1 } - }); - }); - }); - }); - - describe("additional plugin pool", () => { - beforeEach(() => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir - }); - - factory = new ConfigArrayFactory({ - additionalPluginPool: new Map([ - ["abc", { configs: { name: "abc" } }], - ["eslint-plugin-def", { configs: { name: "def" } }] - ]) - }); - }); - - it("should use the matched plugin in the additional plugin pool; short to short", () => { - const configArray = create({ plugins: ["abc"] }); - - assert.strictEqual(configArray[0].plugins.abc.id, "abc"); - assertPluginDefinition( - configArray[0].plugins.abc.definition, - { configs: { name: "abc" } } - ); - }); - - it("should use the matched plugin in the additional plugin pool; long to short", () => { - const configArray = create({ plugins: ["eslint-plugin-abc"] }); - - assert.strictEqual(configArray[0].plugins.abc.id, "abc"); - assertPluginDefinition( - configArray[0].plugins.abc.definition, - { configs: { name: "abc" } } - ); - }); - - it("should use the matched plugin in the additional plugin pool; short to long", () => { - const configArray = create({ plugins: ["def"] }); - - assert.strictEqual(configArray[0].plugins.def.id, "def"); - assertPluginDefinition( - configArray[0].plugins.def.definition, - { configs: { name: "def" } } - ); - }); - - it("should use the matched plugin in the additional plugin pool; long to long", () => { - const configArray = create({ plugins: ["eslint-plugin-def"] }); - - assert.strictEqual(configArray[0].plugins.def.id, "def"); - assertPluginDefinition( - configArray[0].plugins.def.definition, - { configs: { name: "def" } } - ); - }); - }); - }); - - // This group moved from 'tests/lib/config/config-file.js' when refactoring to keep the cumulated test cases. - describe("'extends' property should handle the content of extended configs properly.", () => { - const files = { - "node_modules/eslint-config-foo/index.js": "exports.env = { browser: true }", - "node_modules/eslint-config-one/index.js": "module.exports = { extends: 'two', env: { browser: true } }", - "node_modules/eslint-config-two/index.js": "module.exports = { env: { node: true } }", - "node_modules/eslint-plugin-invalid-parser/index.js": "exports.configs = { foo: { parser: 'nonexistent-parser' } }", - "node_modules/eslint-plugin-invalid-config/index.js": "exports.configs = { foo: {} }", - "js/.eslintrc.js": "module.exports = { rules: { semi: [2, 'always'] } };", - "cjs/.eslintrc.cjs": "module.exports = { rules: { semi: [2, 'always'] } };", - "json/.eslintrc.json": "{ \"rules\": { \"quotes\": [2, \"double\"] } }", - "package-json/package.json": "{ \"eslintConfig\": { \"env\": { \"es6\": true } } }", - "yaml/.eslintrc.yaml": "env:\n browser: true" - }; - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ files }); - const factory = new ConfigArrayFactory(); - - /** - * Apply `extends` property. - * @param {Object} configData The config that has `extends` property. - * @param {string} [filePath] The path to the config data. - * @returns {Object} The applied config data. - */ - function applyExtends(configData, filePath = "whatever") { - return factory - .create(configData, { filePath }) - .extractConfig(filePath) - .toCompatibleObjectAsConfigFileContent(); - } - - it("should apply extension 'foo' when specified from root directory config", () => { - const config = applyExtends({ - extends: "foo", - rules: { eqeqeq: 2 } - }); - - assertConfig(config, { - env: { browser: true }, - rules: { eqeqeq: [2] } - }); - }); - - it("should apply all rules when extends config includes 'eslint:all'", () => { - const config = applyExtends({ - extends: "eslint:all" - }); - - assert.strictEqual(config.rules.eqeqeq[0], "error"); - assert.strictEqual(config.rules.curly[0], "error"); - }); - - it("should throw an error when extends config module is not found", () => { - assert.throws(() => { - applyExtends({ - extends: "not-exist", - rules: { eqeqeq: 2 } - }); - }, /Failed to load config "not-exist" to extend from./u); - }); - - it("should throw an error when an eslint config is not found", () => { - assert.throws(() => { - applyExtends({ - extends: "eslint:foo", - rules: { eqeqeq: 2 } - }); - }, /Failed to load config "eslint:foo" to extend from./u); - }); - - it("should throw an error when a parser in a plugin config is not found", () => { - assert.throws(() => { - applyExtends({ - extends: "plugin:invalid-parser/foo", - rules: { eqeqeq: 2 } - }); - }, /Failed to load parser 'nonexistent-parser' declared in 'whatever » plugin:invalid-parser\/foo'/u); - }); - - it("should fall back to default parser when a parser called 'espree' is not found", () => { - const config = applyExtends({ parser: "espree" }); - - assertConfig(config, { - parser: require.resolve("espree") - }); - }); - - it("should throw an error when a plugin config is not found", () => { - assert.throws(() => { - applyExtends({ - extends: "plugin:invalid-config/bar", - rules: { eqeqeq: 2 } - }); - }, /Failed to load config "plugin:invalid-config\/bar" to extend from./u); - }); - - it("should throw an error with a message template when a plugin config specifier is missing config name", () => { - try { - applyExtends({ - extends: "plugin:some-plugin", - rules: { eqeqeq: 2 } - }); - } catch (err) { - assert.strictEqual(err.messageTemplate, "plugin-invalid"); - assert.deepStrictEqual(err.messageData, { - configName: "plugin:some-plugin", - importerName: path.join(process.cwd(), "whatever") - }); - return; - } - assert.fail("Expected to throw an error"); - }); - - it("should throw an error with a message template when a plugin referenced for a plugin config is not found", () => { - try { - applyExtends({ - extends: "plugin:nonexistent-plugin/baz", - rules: { eqeqeq: 2 } - }); - } catch (err) { - assert.strictEqual(err.messageTemplate, "plugin-missing"); - assert.deepStrictEqual(err.messageData, { - pluginName: "eslint-plugin-nonexistent-plugin", - resolvePluginsRelativeTo: process.cwd(), - importerName: "whatever" - }); - return; - } - assert.fail("Expected to throw an error"); - }); - - it("should throw an error with a message template when a plugin in the plugins list is not found", () => { - try { - applyExtends({ - plugins: ["nonexistent-plugin"] - }); - } catch (err) { - assert.strictEqual(err.messageTemplate, "plugin-missing"); - assert.deepStrictEqual(err.messageData, { - pluginName: "eslint-plugin-nonexistent-plugin", - resolvePluginsRelativeTo: process.cwd(), - importerName: "whatever" - }); - return; - } - assert.fail("Expected to throw an error"); - }); - - it("should apply extensions recursively when specified from package", () => { - const config = applyExtends({ - extends: "one", - rules: { eqeqeq: 2 } - }); - - assertConfig(config, { - env: { browser: true, node: true }, - rules: { eqeqeq: [2] } - }); - }); - - it("should apply extensions when specified from a JavaScript file", () => { - const config = applyExtends({ - extends: ".eslintrc.js", - rules: { eqeqeq: 2 } - }, "js/foo.js"); - - assertConfig(config, { - rules: { - semi: [2, "always"], - eqeqeq: [2] - } - }); - }); - - it("should apply extensions when specified from a YAML file", () => { - const config = applyExtends({ - extends: ".eslintrc.yaml", - rules: { eqeqeq: 2 } - }, "yaml/foo.js"); - - assertConfig(config, { - env: { browser: true }, - rules: { - eqeqeq: [2] - } - }); - }); - - it("should apply extensions when specified from a JSON file", () => { - const config = applyExtends({ - extends: ".eslintrc.json", - rules: { eqeqeq: 2 } - }, "json/foo.js"); - - assertConfig(config, { - rules: { - eqeqeq: [2], - quotes: [2, "double"] - } - }); - }); - - it("should apply extensions when specified from a package.json file in a sibling directory", () => { - const config = applyExtends({ - extends: "../package-json/package.json", - rules: { eqeqeq: 2 } - }, "json/foo.js"); - - assertConfig(config, { - env: { es6: true }, - rules: { - eqeqeq: [2] - } - }); - }); - }); - - // This group moved from 'tests/lib/config/config-file.js' when refactoring to keep the cumulated test cases. - describe("loading config files should work properly.", () => { - - /** - * Load a given config file. - * @param {ConfigArrayFactory} factory The factory to load. - * @param {string} filePath The path to a config file. - * @returns {Object} The applied config data. - */ - function load(factory, filePath) { - return factory - .loadFile(filePath) - .extractConfig(filePath) - .toCompatibleObjectAsConfigFileContent(); - } - - it("should throw error if file doesn't exist", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem(); - const factory = new ConfigArrayFactory(); - - assert.throws(() => { - load(factory, "legacy/nofile.js"); - }); - - assert.throws(() => { - load(factory, "legacy/package.json"); - }); - }); - - it("should load information from a legacy file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "legacy/.eslintrc": "{ rules: { eqeqeq: 2 } }" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "legacy/.eslintrc"); - - assertConfig(config, { - rules: { - eqeqeq: [2] - } - }); - }); - - it("should load information from a JavaScript file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "js/.eslintrc.js": "module.exports = { rules: { semi: [2, 'always'] } };" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "js/.eslintrc.js"); - - assertConfig(config, { - rules: { - semi: [2, "always"] - } - }); - }); - - it("should load information from a JavaScript file with a .cjs extension", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "cjs/.eslintrc.cjs": "module.exports = { rules: { semi: [2, 'always'] } };" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "cjs/.eslintrc.cjs"); - - assertConfig(config, { - rules: { - semi: [2, "always"] - } - }); - }); - - it("should throw error when loading invalid JavaScript file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "js/.eslintrc.broken.js": "module.exports = { rules: { semi: [2, 'always'] }" - } - }); - const factory = new ConfigArrayFactory(); - - assert.throws(() => { - load(factory, "js/.eslintrc.broken.js"); - }, /Cannot read config file/u); - }); - - it("should interpret parser module name when present in a JavaScript file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "node_modules/foo/index.js": "", - "js/node_modules/foo/index.js": "", - "js/.eslintrc.parser.js": `module.exports = { - parser: 'foo', - rules: { semi: [2, 'always'] } - };` - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "js/.eslintrc.parser.js"); - - assertConfig(config, { - parser: path.resolve("js/node_modules/foo/index.js"), - rules: { - semi: [2, "always"] - } - }); - }); - - it("should interpret parser path when present in a JavaScript file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "js/.eslintrc.parser2.js": `module.exports = { - parser: './not-a-config.js', - rules: { semi: [2, 'always'] } - };`, - "js/not-a-config.js": "" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "js/.eslintrc.parser2.js"); - - assertConfig(config, { - parser: path.resolve("js/not-a-config.js"), - rules: { - semi: [2, "always"] - } - }); - }); - - it("should interpret parser module name or path when parser is set to default parser in a JavaScript file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "js/.eslintrc.parser3.js": `module.exports = { - parser: 'espree', - rules: { semi: [2, 'always'] } - };` - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "js/.eslintrc.parser3.js"); - - assertConfig(config, { - parser: require.resolve("espree"), - rules: { - semi: [2, "always"] - } - }); - }); - - it("should load information from a JSON file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "json/.eslintrc.json": "{ \"rules\": { \"quotes\": [2, \"double\"] } }" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "json/.eslintrc.json"); - - assertConfig(config, { - rules: { - quotes: [2, "double"] - } - }); - }); - - it("should load fresh information from a JSON file", () => { - const { fs, ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem(); - const factory = new ConfigArrayFactory(); - const initialConfig = { - rules: { - quotes: [2, "double"] - } - }; - const updatedConfig = { - rules: { - quotes: [0] - } - }; - let config; - - fs.writeFileSync("fresh-test.json", JSON.stringify(initialConfig)); - config = load(factory, "fresh-test.json"); - assertConfig(config, initialConfig); - - fs.writeFileSync("fresh-test.json", JSON.stringify(updatedConfig)); - config = load(factory, "fresh-test.json"); - assertConfig(config, updatedConfig); - }); - - it("should load information from a package.json file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "package-json/package.json": "{ \"eslintConfig\": { \"env\": { \"es6\": true } } }" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "package-json/package.json"); - - assertConfig(config, { - env: { es6: true } - }); - }); - - it("should throw error when loading invalid package.json file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "broken-package-json/package.json": "{ \"eslintConfig\": { \"env\": { \"es6\": true } }" - } - }); - const factory = new ConfigArrayFactory(); - - assert.throws(() => { - try { - load(factory, "broken-package-json/package.json"); - } catch (error) { - assert.strictEqual(error.messageTemplate, "failed-to-read-json"); - throw error; - } - }, /Cannot read config file/u); - }); - - it("should load fresh information from a package.json file", () => { - const { fs, ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem(); - const factory = new ConfigArrayFactory(); - const initialConfig = { - eslintConfig: { - rules: { - quotes: [2, "double"] - } - } - }; - const updatedConfig = { - eslintConfig: { - rules: { - quotes: [0] - } - } - }; - let config; - - fs.writeFileSync("package.json", JSON.stringify(initialConfig)); - config = load(factory, "package.json"); - assertConfig(config, initialConfig.eslintConfig); - - fs.writeFileSync("package.json", JSON.stringify(updatedConfig)); - config = load(factory, "package.json"); - assertConfig(config, updatedConfig.eslintConfig); - }); - - it("should load fresh information from a .eslintrc.js file", () => { - const { fs, ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem(); - const factory = new ConfigArrayFactory(); - const initialConfig = { - rules: { - quotes: [2, "double"] - } - }; - const updatedConfig = { - rules: { - quotes: [0] - } - }; - let config; - - fs.writeFileSync(".eslintrc.js", `module.exports = ${JSON.stringify(initialConfig)}`); - config = load(factory, ".eslintrc.js"); - assertConfig(config, initialConfig); - - fs.writeFileSync(".eslintrc.js", `module.exports = ${JSON.stringify(updatedConfig)}`); - config = load(factory, ".eslintrc.js"); - assertConfig(config, updatedConfig); - }); - - it("should load information from a YAML file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "yaml/.eslintrc.yaml": "env:\n browser: true" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "yaml/.eslintrc.yaml"); - - assertConfig(config, { - env: { browser: true } - }); - }); - - it("should load information from an empty YAML file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "yaml/.eslintrc.empty.yaml": "{}" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "yaml/.eslintrc.empty.yaml"); - - assertConfig(config, {}); - }); - - it("should load information from a YML file", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "yml/.eslintrc.yml": "env:\n node: true" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "yml/.eslintrc.yml"); - - assertConfig(config, { - env: { node: true } - }); - }); - - it("should load information from a YML file and apply extensions", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "extends/.eslintrc.yml": "extends: ../package-json/package.json\nrules:\n booya: 2", - "package-json/package.json": "{ \"eslintConfig\": { \"env\": { \"es6\": true } } }" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "extends/.eslintrc.yml"); - - assertConfig(config, { - env: { es6: true }, - rules: { booya: [2] } - }); - }); - - it("should load information from `extends` chain.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "extends-chain": { - "node_modules/eslint-config-a": { - "node_modules/eslint-config-b": { - "node_modules/eslint-config-c": { - "index.js": "module.exports = { rules: { c: 2 } };" - }, - "index.js": "module.exports = { extends: 'c', rules: { b: 2 } };" - }, - "index.js": "module.exports = { extends: 'b', rules: { a: 2 } };" - }, - ".eslintrc.json": "{ \"extends\": \"a\" }" - } - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "extends-chain/.eslintrc.json"); - - assertConfig(config, { - rules: { - a: [2], // from node_modules/eslint-config-a - b: [2], // from node_modules/eslint-config-a/node_modules/eslint-config-b - c: [2] // from node_modules/eslint-config-a/node_modules/eslint-config-b/node_modules/eslint-config-c - } - }); - }); - - it("should load information from `extends` chain with relative path.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "extends-chain-2": { - "node_modules/eslint-config-a/index.js": "module.exports = { extends: './relative.js', rules: { a: 2 } };", - "node_modules/eslint-config-a/relative.js": "module.exports = { rules: { relative: 2 } };", - ".eslintrc.json": "{ \"extends\": \"a\" }" - } - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "extends-chain-2/.eslintrc.json"); - - assertConfig(config, { - rules: { - a: [2], // from node_modules/eslint-config-a/index.js - relative: [2] // from node_modules/eslint-config-a/relative.js - } - }); - }); - - it("should load information from `extends` chain in .eslintrc with relative path.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "extends-chain-2": { - "node_modules/eslint-config-a/index.js": "module.exports = { extends: './relative.js', rules: { a: 2 } };", - "node_modules/eslint-config-a/relative.js": "module.exports = { rules: { relative: 2 } };", - "relative.eslintrc.json": "{ \"extends\": \"./node_modules/eslint-config-a/index.js\" }" - } - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "extends-chain-2/relative.eslintrc.json"); - - assertConfig(config, { - rules: { - a: [2], // from node_modules/eslint-config-a/index.js - relative: [2] // from node_modules/eslint-config-a/relative.js - } - }); - }); - - it("should load information from `parser` in .eslintrc with relative path.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "extends-chain-2": { - "parser.eslintrc.json": "{ \"parser\": \"./parser.js\" }", - "parser.js": "" - } - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "extends-chain-2/parser.eslintrc.json"); - - assertConfig(config, { - parser: path.resolve("extends-chain-2/parser.js") - }); - }); - - describe("Plugins", () => { - it("should load information from a YML file and load plugins", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "node_modules/eslint-plugin-test/index.js": ` - module.exports = { - environments: { - bar: { globals: { bar: true } } - } - } - `, - "plugins/.eslintrc.yml": ` - plugins: - - test - rules: - test/foo: 2 - env: - test/bar: true - ` - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "plugins/.eslintrc.yml"); - - assertConfig(config, { - env: { "test/bar": true }, - plugins: ["test"], - rules: { - "test/foo": [2] - } - }); - }); - - it("should load two separate configs from a plugin", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "node_modules/eslint-plugin-test/index.js": ` - module.exports = { - configs: { - foo: { rules: { semi: 2, quotes: 1 } }, - bar: { rules: { quotes: 2, yoda: 2 } } - } - } - `, - "plugins/.eslintrc.yml": ` - extends: - - plugin:test/foo - - plugin:test/bar - ` - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "plugins/.eslintrc.yml"); - - assertConfig(config, { - rules: { - semi: [2], - quotes: [2], - yoda: [2] - } - }); - }); - }); - - describe("even if config files have Unicode BOM,", () => { - it("should read the JSON config file correctly.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "bom/.eslintrc.json": "\uFEFF{ \"rules\": { \"semi\": \"error\" } }" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "bom/.eslintrc.json"); - - assertConfig(config, { - rules: { - semi: ["error"] - } - }); - }); - - it("should read the YAML config file correctly.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "bom/.eslintrc.yaml": "\uFEFFrules:\n semi: error" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "bom/.eslintrc.yaml"); - - assertConfig(config, { - rules: { - semi: ["error"] - } - }); - }); - - it("should read the config in package.json correctly.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "bom/package.json": "\uFEFF{ \"eslintConfig\": { \"rules\": { \"semi\": \"error\" } } }" - } - }); - const factory = new ConfigArrayFactory(); - const config = load(factory, "bom/package.json"); - - assertConfig(config, { - rules: { - semi: ["error"] - } - }); - }); - }); - - it("throws an error including the config file name if the config file is invalid", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - files: { - "invalid/invalid-top-level-property.yml": "invalidProperty: 3" - } - }); - const factory = new ConfigArrayFactory(); - - try { - load(factory, "invalid/invalid-top-level-property.yml"); - } catch (err) { - assert.include(err.message, `ESLint configuration in ${`invalid${path.sep}invalid-top-level-property.yml`} is invalid`); - return; - } - assert.fail(); - }); - }); - - // This group moved from 'tests/lib/config/config-file.js' when refactoring to keep the cumulated test cases. - describe("'extends' property should resolve the location of configs properly.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "node_modules/eslint-config-foo/index.js": "", - "node_modules/eslint-config-foo/bar.js": "", - "node_modules/eslint-config-eslint-configfoo/index.js": "", - "node_modules/@foo/eslint-config/index.js": "", - "node_modules/@foo/eslint-config-bar/index.js": "", - "node_modules/eslint-plugin-foo/index.js": "exports.configs = { bar: {} }", - "node_modules/@foo/eslint-plugin/index.js": "exports.configs = { bar: {} }", - "node_modules/@foo/eslint-plugin-bar/index.js": "exports.configs = { baz: {} }", - "foo/bar/.eslintrc": "", - ".eslintrc": "" - } - }); - const factory = new ConfigArrayFactory(); - - /** - * Resolve `extends` module. - * @param {string} request The module name to resolve. - * @param {string} [relativeTo] The importer path to resolve. - * @returns {string} The resolved path. - */ - function resolve(request, relativeTo) { - return factory.create( - { extends: request }, - { filePath: relativeTo } - )[0]; - } - - describe("Relative to CWD", () => { - for (const { input, expected } of [ - { input: ".eslintrc", expected: path.resolve(tempDir, ".eslintrc") }, - { input: "eslint-config-foo", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/index.js") }, - { input: "eslint-config-foo/bar", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/bar.js") }, - { input: "foo", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/index.js") }, - { input: "foo/bar", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/bar.js") }, - { input: "eslint-configfoo", expected: path.resolve(tempDir, "node_modules/eslint-config-eslint-configfoo/index.js") }, - { input: "@foo/eslint-config", expected: path.resolve(tempDir, "node_modules/@foo/eslint-config/index.js") }, - { input: "@foo", expected: path.resolve(tempDir, "node_modules/@foo/eslint-config/index.js") }, - { input: "@foo/bar", expected: path.resolve(tempDir, "node_modules/@foo/eslint-config-bar/index.js") }, - { input: "plugin:foo/bar", expected: path.resolve(tempDir, "node_modules/eslint-plugin-foo/index.js") }, - { input: "plugin:@foo/bar", expected: path.resolve(tempDir, "node_modules/@foo/eslint-plugin/index.js") }, - { input: "plugin:@foo/bar/baz", expected: path.resolve(tempDir, "node_modules/@foo/eslint-plugin-bar/index.js") } - ]) { - it(`should return ${expected} when passed ${input}`, () => { - const result = resolve(input); - - assert.strictEqual(result.filePath, expected); - }); - } - }); - - describe("Relative to config file", () => { - const relativePath = path.resolve(tempDir, "./foo/bar/.eslintrc"); - - for (const { input, expected } of [ - { input: ".eslintrc", expected: path.join(path.dirname(relativePath), ".eslintrc") }, - { input: "eslint-config-foo", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/index.js") }, - { input: "eslint-config-foo/bar", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/bar.js") }, - { input: "foo", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/index.js") }, - { input: "foo/bar", expected: path.resolve(tempDir, "node_modules/eslint-config-foo/bar.js") }, - { input: "eslint-configfoo", expected: path.resolve(tempDir, "node_modules/eslint-config-eslint-configfoo/index.js") }, - { input: "@foo/eslint-config", expected: path.resolve(tempDir, "node_modules/@foo/eslint-config/index.js") }, - { input: "@foo", expected: path.resolve(tempDir, "node_modules/@foo/eslint-config/index.js") }, - { input: "@foo/bar", expected: path.resolve(tempDir, "node_modules/@foo/eslint-config-bar/index.js") }, - { input: "plugin:foo/bar", expected: path.resolve(tempDir, "node_modules/eslint-plugin-foo/index.js") }, - { input: "plugin:@foo/bar", expected: path.resolve(tempDir, "node_modules/@foo/eslint-plugin/index.js") }, - { input: "plugin:@foo/bar/baz", expected: path.resolve(tempDir, "node_modules/@foo/eslint-plugin-bar/index.js") } - ]) { - it(`should return ${expected} when passed ${input}`, () => { - const result = resolve(input, relativePath); - - assert.strictEqual(result.filePath, expected); - }); - } - }); - }); - - // This group moved from 'tests/lib/config/plugins.js' when refactoring to keep the cumulated test cases. - describe("'plugins' property should load a correct plugin.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "node_modules/@scope/eslint-plugin-example/index.js": "exports.configs = { name: '@scope/eslint-plugin-example' };", - "node_modules/eslint-plugin-example/index.js": "exports.configs = { name: 'eslint-plugin-example' };", - "node_modules/eslint-plugin-throws-on-load/index.js": "throw new Error('error thrown while loading this module')" - } - }); - const factory = new ConfigArrayFactory(); - - /** - * Load a plugin. - * @param {string} request A request to load a plugin. - * @param {ConfigArrayFactory} [configArrayFactory] The factory to use - * @returns {Map} The loaded plugins. - */ - function load(request, configArrayFactory = factory) { - const config = configArrayFactory.create({ plugins: [request] }); - - return new Map( - Object - .entries(config[0].plugins) - .map(([id, entry]) => { - if (entry.error) { - throw entry.error; - } - return [id, entry.definition]; - }) - ); - } - - it("should load a plugin when referenced by short name", () => { - const loadedPlugins = load("example"); - - assertPluginDefinition( - loadedPlugins.get("example"), - { configs: { name: "eslint-plugin-example" } } - ); - }); - - it("should load a plugin when referenced by short name, even when using a custom loadPluginsRelativeTo value", () => { - const { ConfigArrayFactory: FactoryWithPluginsInSubdir } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "subdir/node_modules/eslint-plugin-example/index.js": "exports.configs = { name: 'eslint-plugin-example' };" - } - }); - - const factoryWithCustomPluginPath = new FactoryWithPluginsInSubdir({ resolvePluginsRelativeTo: "subdir" }); - - const loadedPlugins = load("example", factoryWithCustomPluginPath); - - assertPluginDefinition( - loadedPlugins.get("example"), - { configs: { name: "eslint-plugin-example" } } - ); - }); - - it("should load a plugin when referenced by long name", () => { - const loadedPlugins = load("eslint-plugin-example"); - - assertPluginDefinition( - loadedPlugins.get("example"), - { configs: { name: "eslint-plugin-example" } } - ); - }); - - it("should throw an error when a plugin has whitespace", () => { - assert.throws(() => { - load("whitespace "); - }, /Whitespace found in plugin name 'whitespace '/u); - assert.throws(() => { - load("whitespace\t"); - }, /Whitespace found in plugin name/u); - assert.throws(() => { - load("whitespace\n"); - }, /Whitespace found in plugin name/u); - assert.throws(() => { - load("whitespace\r"); - }, /Whitespace found in plugin name/u); - }); - - it("should throw an error when a plugin doesn't exist", () => { - assert.throws(() => { - load("nonexistentplugin"); - }, /Failed to load plugin/u); - }); - - it("should rethrow an error that a plugin throws on load", () => { - assert.throws(() => { - load("throws-on-load"); - }, /error thrown while loading this module/u); - }); - - it("should load a scoped plugin when referenced by short name", () => { - const loadedPlugins = load("@scope/example"); - - assertPluginDefinition( - loadedPlugins.get("@scope/example"), - { configs: { name: "@scope/eslint-plugin-example" } } - ); - }); - - it("should load a scoped plugin when referenced by long name", () => { - const loadedPlugins = load("@scope/eslint-plugin-example"); - - assertPluginDefinition( - loadedPlugins.get("@scope/example"), - { configs: { name: "@scope/eslint-plugin-example" } } - ); - }); - - describe("when referencing a scope plugin and omitting @scope/", () => { - it("should load a scoped plugin when referenced by short name, but should not get the plugin if '@scope/' is omitted", () => { - const loadedPlugins = load("@scope/example"); - - assert.strictEqual(loadedPlugins.get("example"), void 0); - }); - - it("should load a scoped plugin when referenced by long name, but should not get the plugin if '@scope/' is omitted", () => { - const loadedPlugins = load("@scope/eslint-plugin-example"); - - assert.strictEqual(loadedPlugins.get("example"), void 0); - }); - }); - }); - - // This group moved from 'tests/lib/config/plugins.js' when refactoring to keep the cumulated test cases. - describe("'plugins' property should load some correct plugins.", () => { - const { ConfigArrayFactory } = defineConfigArrayFactoryWithInMemoryFileSystem({ - cwd: () => tempDir, - files: { - "node_modules/eslint-plugin-example1/index.js": "exports.configs = { name: 'eslint-plugin-example1' };", - "node_modules/eslint-plugin-example2/index.js": "exports.configs = { name: 'eslint-plugin-example2' };" - } - }); - const factory = new ConfigArrayFactory(); - - /** - * Load a plugin. - * @param {string[]} request A request to load a plugin. - * @returns {Map} The loaded plugins. - */ - function loadAll(request) { - const config = factory.create({ plugins: request }); - - return new Map( - Object - .entries(config[0].plugins) - .map(([id, entry]) => { - if (entry.error) { - throw entry.error; - } - return [id, entry.definition]; - }) - ); - } - - it("should load plugins when passed multiple plugins", () => { - const loadedPlugins = loadAll(["example1", "example2"]); - - assertPluginDefinition( - loadedPlugins.get("example1"), - { configs: { name: "eslint-plugin-example1" } } - ); - assertPluginDefinition( - loadedPlugins.get("example2"), - { configs: { name: "eslint-plugin-example2" } } - ); - }); - }); -}); diff --git a/tests/lib/cli-engine/config-array/config-array.js b/tests/lib/cli-engine/config-array/config-array.js deleted file mode 100644 index 14788ee3080..00000000000 --- a/tests/lib/cli-engine/config-array/config-array.js +++ /dev/null @@ -1,751 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview Tests for ConfigArray class. - * @author Toru Nagashima - */ -"use strict"; - -const path = require("path"); -const { assert } = require("chai"); -const { ConfigArray, OverrideTester, getUsedExtractedConfigs } = require("../../../../lib/cli-engine/config-array"); - -describe("ConfigArray", () => { - it("should be a sub class of Array.", () => { - assert(new ConfigArray() instanceof Array); - }); - - describe("'constructor(...elements)' should adopt the elements as array elements.", () => { - const patterns = [ - { elements: [] }, - { elements: [{ value: 1 }] }, - { elements: [{ value: 2 }, { value: 3 }] }, - { elements: [{ value: 4 }, { value: 5 }, { value: 6 }] } - ]; - - for (const { elements } of patterns) { - describe(`if it gave ${JSON.stringify(elements)} then`, () => { - let configArray; - - beforeEach(() => { - configArray = new ConfigArray(...elements); - }); - - it(`should have ${elements.length} as the length.`, () => { - assert.strictEqual(configArray.length, elements.length); - }); - - for (let i = 0; i < elements.length; ++i) { - it(`should have ${JSON.stringify(elements[i])} at configArray[${i}].`, () => { // eslint-disable-line no-loop-func - assert.strictEqual(configArray[i], elements[i]); - }); - } - }); - } - }); - - describe("'isRoot()' method should be the value of the last element which has 'root' property.", () => { - const patterns = [ - { elements: [], expected: false }, - { elements: [{}], expected: false }, - { elements: [{}, {}], expected: false }, - { elements: [{ root: false }], expected: false }, - { elements: [{ root: true }], expected: true }, - { elements: [{ root: true }, { root: false }], expected: false }, - { elements: [{ root: false }, { root: true }], expected: true }, - { elements: [{ root: false }, { root: true }, { rules: {} }], expected: true }, // ignore undefined. - { elements: [{ root: true }, { root: 1 }], expected: true } // ignore non-boolean value - ]; - - for (const { elements, expected } of patterns) { - it(`should be ${expected} if the elements are ${JSON.stringify(elements)}.`, () => { - assert.strictEqual(new ConfigArray(...elements).isRoot(), expected); - }); - } - }); - - describe("'pluginEnvironments' property should be the environments of all plugins.", () => { - const env = { - "aaa/xxx": {}, - "bbb/xxx": {} - }; - let configArray; - - beforeEach(() => { - configArray = new ConfigArray( - { - plugins: { - aaa: { - definition: { - environments: { - xxx: env["aaa/xxx"] - } - } - } - } - }, - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - plugins: { - bbb: { - definition: { - environments: { - xxx: env["bbb/xxx"] - } - } - } - } - } - ); - }); - - it("should return null for built-in env", () => { - assert.strictEqual(configArray.pluginEnvironments.get("node"), void 0); - }); - - it("should return 'aaa/xxx' if it exists.", () => { - assert.strictEqual(configArray.pluginEnvironments.get("aaa/xxx"), env["aaa/xxx"]); - }); - - it("should return 'bbb/xxx' if it exists.", () => { - assert.strictEqual(configArray.pluginEnvironments.get("bbb/xxx"), env["bbb/xxx"]); - }); - - it("should throw an error if it tried to mutate.", () => { - assert.throws(() => { - configArray.pluginEnvironments.set("ccc/xxx", {}); - }); - }); - }); - - describe("'pluginProcessors' property should be the processors of all plugins.", () => { - const processors = { - "aaa/.xxx": {}, - "bbb/.xxx": {} - }; - let configArray; - - beforeEach(() => { - configArray = new ConfigArray( - { - plugins: { - aaa: { - definition: { - processors: { - ".xxx": processors["aaa/.xxx"] - } - } - } - } - }, - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - plugins: { - bbb: { - definition: { - processors: { - ".xxx": processors["bbb/.xxx"] - } - } - } - } - } - ); - }); - - it("should return 'aaa/.xxx' if it exists.", () => { - assert.strictEqual(configArray.pluginProcessors.get("aaa/.xxx"), processors["aaa/.xxx"]); - }); - - it("should return 'bbb/.xxx' if it exists.", () => { - assert.strictEqual(configArray.pluginProcessors.get("bbb/.xxx"), processors["bbb/.xxx"]); - }); - - it("should throw an error if it tried to mutate.", () => { - assert.throws(() => { - configArray.pluginProcessors.set("ccc/.xxx", {}); - }); - }); - }); - - describe("'pluginRules' property should be the rules of all plugins.", () => { - const rules = { - "aaa/xxx": {}, - "bbb/xxx": {} - }; - let configArray; - - beforeEach(() => { - configArray = new ConfigArray( - { - plugins: { - aaa: { - definition: { - rules: { - xxx: rules["aaa/xxx"] - } - } - } - } - }, - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - plugins: { - bbb: { - definition: { - rules: { - xxx: rules["bbb/xxx"] - } - } - } - } - } - ); - }); - - it("should return null for built-in rules", () => { - assert.strictEqual(configArray.pluginRules.get("eqeqeq"), void 0); - }); - - it("should return 'aaa/xxx' if it exists.", () => { - assert.strictEqual(configArray.pluginRules.get("aaa/xxx"), rules["aaa/xxx"]); - }); - - it("should return 'bbb/xxx' if it exists.", () => { - assert.strictEqual(configArray.pluginRules.get("bbb/xxx"), rules["bbb/xxx"]); - }); - - it("should throw an error if it tried to mutate.", () => { - assert.throws(() => { - configArray.pluginRules.set("ccc/xxx", {}); - }); - }); - }); - - describe("'extractConfig(filePath)' method should retrieve the merged config for a given file.", () => { - it("should throw an error if a 'parser' has the loading error.", () => { - assert.throws(() => { - new ConfigArray( - { - parser: { error: new Error("Failed to load a parser.") } - } - ).extractConfig(__filename); - }, "Failed to load a parser."); - }); - - it("should not throw if the errored 'parser' was not used; overwriten", () => { - const parser = { id: "a parser" }; - const config = new ConfigArray( - { - parser: { error: new Error("Failed to load a parser.") } - }, - { - parser - } - ).extractConfig(__filename); - - assert.strictEqual(config.parser, parser); - }); - - it("should not throw if the errored 'parser' was not used; not matched", () => { - const config = new ConfigArray( - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - parser: { error: new Error("Failed to load a parser.") } - } - ).extractConfig(__filename); - - assert.strictEqual(config.parser, null); - }); - - it("should throw an error if a 'plugins' value has the loading error.", () => { - assert.throws(() => { - new ConfigArray( - { - plugins: { - foo: { error: new Error("Failed to load a plugin.") } - } - } - ).extractConfig(__filename); - }, "Failed to load a plugin."); - }); - - it("should not throw if the errored 'plugins' value was not used; not matched", () => { - const config = new ConfigArray( - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - plugins: { - foo: { error: new Error("Failed to load a plugin.") } - } - } - ).extractConfig(__filename); - - assert.deepStrictEqual(config.plugins, {}); - }); - - it("should not merge the elements which were not matched.", () => { - const config = new ConfigArray( - { - rules: { - "no-redeclare": "error" - } - }, - { - criteria: OverrideTester.create(["*.js"], [], process.cwd()), - rules: { - "no-undef": "error" - } - }, - { - criteria: OverrideTester.create(["*.js"], [path.basename(__filename)], process.cwd()), - rules: { - "no-use-before-define": "error" - } - }, - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - rules: { - "no-unused-vars": "error" - } - } - ).extractConfig(__filename); - - assert.deepStrictEqual(config.rules, { - "no-redeclare": ["error"], - "no-undef": ["error"] - }); - }); - - it("should return the same instance for every the same matching.", () => { - const configArray = new ConfigArray( - { - rules: { - "no-redeclare": "error" - } - }, - { - criteria: OverrideTester.create(["*.js"], [], process.cwd()), - rules: { - "no-undef": "error" - } - }, - { - criteria: OverrideTester.create(["*.js"], [path.basename(__filename)], process.cwd()), - rules: { - "no-use-before-define": "error" - } - }, - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - rules: { - "no-unused-vars": "error" - } - } - ); - - assert.strictEqual( - configArray.extractConfig(path.join(__dirname, "a.js")), - configArray.extractConfig(path.join(__dirname, "b.js")) - ); - }); - - /** - * Merge two config data. - * - * The test cases which depend on this function were moved from - * 'tests/lib/config/config-ops.js' when refactoring to keep the - * cumulated test cases. - * - * Previously, the merging logic of multiple config data had been - * implemented in `ConfigOps.merge()` function. But currently, it's - * implemented in `ConfigArray#extractConfig()` method. - * @param {Object} target A config data. - * @param {Object} source Another config data. - * @returns {Object} The merged config data. - */ - function merge(target, source) { - return new ConfigArray(target, source).extractConfig(__filename); - } - - it("should combine two objects when passed two objects with different top-level properties", () => { - const config = [ - { env: { browser: true } }, - { globals: { foo: "bar" } } - ]; - - const result = merge(config[0], config[1]); - - assert.strictEqual(result.globals.foo, "bar"); - assert.isTrue(result.env.browser); - }); - - it("should combine without blowing up on null values", () => { - const config = [ - { env: { browser: true } }, - { env: { node: null } } - ]; - - const result = merge(config[0], config[1]); - - assert.strictEqual(result.env.node, null); - assert.isTrue(result.env.browser); - }); - - it("should combine two objects with parser when passed two objects with different top-level properties", () => { - const config = [ - { env: { browser: true }, parser: "espree" }, - { globals: { foo: "bar" } } - ]; - - const result = merge(config[0], config[1]); - - assert.strictEqual(result.parser, "espree"); - }); - - it("should combine configs and override rules when passed configs with the same rules", () => { - const config = [ - { rules: { "no-mixed-requires": [0, false] } }, - { rules: { "no-mixed-requires": [1, true] } } - ]; - - const result = merge(config[0], config[1]); - - assert.isArray(result.rules["no-mixed-requires"]); - assert.strictEqual(result.rules["no-mixed-requires"][0], 1); - assert.strictEqual(result.rules["no-mixed-requires"][1], true); - }); - - it("should combine configs when passed configs with parserOptions", () => { - const config = [ - { parserOptions: { ecmaFeatures: { jsx: true } } }, - { parserOptions: { ecmaFeatures: { globalReturn: true } } } - ]; - - const result = merge(config[0], config[1]); - - assert.deepStrictEqual(result, { - configNameOfNoInlineConfig: "", - env: {}, - globals: {}, - ignores: void 0, - noInlineConfig: void 0, - parser: null, - parserOptions: { - ecmaFeatures: { - jsx: true, - globalReturn: true - } - }, - plugins: {}, - processor: null, - reportUnusedDisableDirectives: void 0, - rules: {}, - settings: {} - }); - - // double-check that originals were not changed - assert.deepStrictEqual(config[0], { parserOptions: { ecmaFeatures: { jsx: true } } }); - assert.deepStrictEqual(config[1], { parserOptions: { ecmaFeatures: { globalReturn: true } } }); - }); - - it("should override configs when passed configs with the same ecmaFeatures", () => { - const config = [ - { parserOptions: { ecmaFeatures: { globalReturn: false } } }, - { parserOptions: { ecmaFeatures: { globalReturn: true } } } - ]; - - const result = merge(config[0], config[1]); - - assert.deepStrictEqual(result, { - configNameOfNoInlineConfig: "", - env: {}, - globals: {}, - ignores: void 0, - noInlineConfig: void 0, - parser: null, - parserOptions: { - ecmaFeatures: { - globalReturn: true - } - }, - plugins: {}, - processor: null, - reportUnusedDisableDirectives: void 0, - rules: {}, - settings: {} - }); - }); - - it("should combine configs and override rules when merging two configs with arrays and int", () => { - - const config = [ - { rules: { "no-mixed-requires": [0, false] } }, - { rules: { "no-mixed-requires": 1 } } - ]; - - const result = merge(config[0], config[1]); - - assert.isArray(result.rules["no-mixed-requires"]); - assert.strictEqual(result.rules["no-mixed-requires"][0], 1); - assert.strictEqual(result.rules["no-mixed-requires"][1], false); - assert.deepStrictEqual(config[0], { rules: { "no-mixed-requires": [0, false] } }); - assert.deepStrictEqual(config[1], { rules: { "no-mixed-requires": 1 } }); - }); - - it("should combine configs and override rules options completely", () => { - - const config = [ - { rules: { "no-mixed-requires1": [1, { event: ["evt", "e"] }] } }, - { rules: { "no-mixed-requires1": [1, { err: ["error", "e"] }] } } - ]; - - const result = merge(config[0], config[1]); - - assert.isArray(result.rules["no-mixed-requires1"]); - assert.deepStrictEqual(result.rules["no-mixed-requires1"][1], { err: ["error", "e"] }); - assert.deepStrictEqual(config[0], { rules: { "no-mixed-requires1": [1, { event: ["evt", "e"] }] } }); - assert.deepStrictEqual(config[1], { rules: { "no-mixed-requires1": [1, { err: ["error", "e"] }] } }); - }); - - it("should combine configs and override rules options without array or object", () => { - - const config = [ - { rules: { "no-mixed-requires1": ["warn", "nconf", "underscore"] } }, - { rules: { "no-mixed-requires1": [2, "requirejs"] } } - ]; - - const result = merge(config[0], config[1]); - - assert.strictEqual(result.rules["no-mixed-requires1"][0], 2); - assert.strictEqual(result.rules["no-mixed-requires1"][1], "requirejs"); - assert.isUndefined(result.rules["no-mixed-requires1"][2]); - assert.deepStrictEqual(config[0], { rules: { "no-mixed-requires1": ["warn", "nconf", "underscore"] } }); - assert.deepStrictEqual(config[1], { rules: { "no-mixed-requires1": [2, "requirejs"] } }); - }); - - it("should combine configs and override rules options without array or object but special case", () => { - - const config = [ - { rules: { "no-mixed-requires1": [1, "nconf", "underscore"] } }, - { rules: { "no-mixed-requires1": "error" } } - ]; - - const result = merge(config[0], config[1]); - - assert.strictEqual(result.rules["no-mixed-requires1"][0], "error"); - assert.strictEqual(result.rules["no-mixed-requires1"][1], "nconf"); - assert.strictEqual(result.rules["no-mixed-requires1"][2], "underscore"); - assert.deepStrictEqual(config[0], { rules: { "no-mixed-requires1": [1, "nconf", "underscore"] } }); - assert.deepStrictEqual(config[1], { rules: { "no-mixed-requires1": "error" } }); - }); - - it("should combine configs correctly", () => { - - const config = [ - { - rules: { - "no-mixed-requires1": [1, { event: ["evt", "e"] }], - "valid-jsdoc": 1, - semi: 1, - quotes1: [2, { exception: ["hi"] }], - smile: [1, ["hi", "bye"]] - }, - parserOptions: { - ecmaFeatures: { jsx: true } - }, - env: { browser: true }, - globals: { foo: false } - }, - { - rules: { - "no-mixed-requires1": [1, { err: ["error", "e"] }], - "valid-jsdoc": 2, - test: 1, - smile: [1, ["xxx", "yyy"]] - }, - parserOptions: { - ecmaFeatures: { globalReturn: true } - }, - env: { browser: false }, - globals: { foo: true } - } - ]; - - const result = merge(config[0], config[1]); - - assert.deepStrictEqual(result, { - configNameOfNoInlineConfig: "", - parser: null, - parserOptions: { - ecmaFeatures: { - jsx: true, - globalReturn: true - } - }, - plugins: {}, - env: { - browser: false - }, - globals: { - foo: true - }, - rules: { - "no-mixed-requires1": [1, - { - err: [ - "error", - "e" - ] - } - ], - quotes1: [2, - { - exception: [ - "hi" - ] - } - ], - semi: [1], - smile: [1, ["xxx", "yyy"]], - test: [1], - "valid-jsdoc": [2] - }, - settings: {}, - processor: null, - noInlineConfig: void 0, - reportUnusedDisableDirectives: void 0, - ignores: void 0 - }); - assert.deepStrictEqual(config[0], { - rules: { - "no-mixed-requires1": [1, { event: ["evt", "e"] }], - "valid-jsdoc": 1, - semi: 1, - quotes1: [2, { exception: ["hi"] }], - smile: [1, ["hi", "bye"]] - }, - parserOptions: { - ecmaFeatures: { jsx: true } - }, - env: { browser: true }, - globals: { foo: false } - }); - assert.deepStrictEqual(config[1], { - rules: { - "no-mixed-requires1": [1, { err: ["error", "e"] }], - "valid-jsdoc": 2, - test: 1, - smile: [1, ["xxx", "yyy"]] - }, - parserOptions: { - ecmaFeatures: { globalReturn: true } - }, - env: { browser: false }, - globals: { foo: true } - }); - }); - - it("should copy deeply if there is not the destination's property", () => { - const a = {}; - const b = { settings: { bar: 1 } }; - - const result = merge(a, b); - - assert(a.settings === void 0); - assert(b.settings.bar === 1); - assert(result.settings.bar === 1); - - result.settings.bar = 2; - assert(b.settings.bar === 1); - assert(result.settings.bar === 2); - }); - }); - - describe("'getUsedExtractedConfigs(instance)' function should retrieve used extracted configs from the instance's internal cache.", () => { - let configArray; - - beforeEach(() => { - configArray = new ConfigArray( - { - rules: { - "no-redeclare": "error" - } - }, - { - criteria: OverrideTester.create(["*.js"], [], process.cwd()), - rules: { - "no-undef": "error" - } - }, - { - criteria: OverrideTester.create(["*.js"], [path.basename(__filename)], process.cwd()), - rules: { - "no-use-before-define": "error" - } - }, - { - criteria: OverrideTester.create(["*.ts"], [], process.cwd()), - rules: { - "no-unused-vars": "error" - } - } - ); - }); - - it("should return empty array before it called 'extractConfig(filePath)'.", () => { - assert.deepStrictEqual(getUsedExtractedConfigs(configArray), []); - }); - - for (const { filePaths } of [ - { filePaths: [__filename] }, - { filePaths: [__filename, `${__filename}.ts`] }, - { filePaths: [__filename, `${__filename}.ts`, path.join(__dirname, "foo.js")] } - ]) { - describe(`after it called 'extractConfig(filePath)' ${filePaths.length} time(s) with ${JSON.stringify(filePaths, null, 4)}, the returned array`, () => { // eslint-disable-line no-loop-func - let configs; - let usedConfigs; - - beforeEach(() => { - configs = filePaths.map(filePath => configArray.extractConfig(filePath)); - usedConfigs = getUsedExtractedConfigs(configArray); - }); - - it(`should have ${filePaths.length} as the length.`, () => { - assert.strictEqual(usedConfigs.length, configs.length); - }); - - for (let i = 0; i < filePaths.length; ++i) { - it(`should contain 'configs[${i}]'.`, () => { // eslint-disable-line no-loop-func - assert(usedConfigs.includes(configs[i])); - }); - } - }); - } - - it("should not contain duplicate values.", () => { - - // Call some times, including with the same arguments. - configArray.extractConfig(__filename); - configArray.extractConfig(`${__filename}.ts`); - configArray.extractConfig(path.join(__dirname, "foo.js")); - configArray.extractConfig(__filename); - configArray.extractConfig(path.join(__dirname, "foo.js")); - configArray.extractConfig(path.join(__dirname, "bar.js")); - configArray.extractConfig(path.join(__dirname, "baz.js")); - - const usedConfigs = getUsedExtractedConfigs(configArray); - - assert.strictEqual(new Set(usedConfigs).size, usedConfigs.length); - }); - }); -}); diff --git a/tests/lib/cli-engine/config-array/config-dependency.js b/tests/lib/cli-engine/config-array/config-dependency.js deleted file mode 100644 index ef97b309ec7..00000000000 --- a/tests/lib/cli-engine/config-array/config-dependency.js +++ /dev/null @@ -1,132 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview Tests for ConfigDependency class. - * @author Toru Nagashima - */ -"use strict"; - -const assert = require("assert"); -const { Console } = require("console"); -const { Writable } = require("stream"); -const { ConfigDependency } = require("../../../../lib/cli-engine/config-array/config-dependency"); - -describe("ConfigDependency", () => { - describe("'constructor(data)' should initialize properties.", () => { - - /** @type {ConfigDependency} */ - let dep; - - beforeEach(() => { - dep = new ConfigDependency({ - definition: { name: "definition?" }, - error: new Error("error?"), - filePath: "filePath?", - id: "id?", - importerName: "importerName?", - importerPath: "importerPath?" - }); - }); - - it("should set 'data.definition' to 'definition' property.", () => { - assert.deepStrictEqual(dep.definition, { name: "definition?" }); - }); - - it("should set 'data.error' to 'error' property.", () => { - assert.deepStrictEqual(dep.error.message, "error?"); - }); - - it("should set 'data.filePath' to 'filePath' property.", () => { - assert.deepStrictEqual(dep.filePath, "filePath?"); - }); - - it("should set 'data.id' to 'id' property.", () => { - assert.deepStrictEqual(dep.id, "id?"); - }); - - it("should set 'data.importerName' to 'importerName' property.", () => { - assert.deepStrictEqual(dep.importerName, "importerName?"); - }); - - it("should set 'data.importerPath' to 'importerPath' property.", () => { - assert.deepStrictEqual(dep.importerPath, "importerPath?"); - }); - }); - - describe("'JSON.stringify(...)' should return readable JSON; not include 'definition' property", () => { - it("should not print 'definition' property.", () => { - const dep = new ConfigDependency({ - definition: { name: "definition?" }, - error: new Error("error?"), - filePath: "filePath?", - id: "id?", - importerName: "importerName?", - importerPath: "importerPath?" - }); - - assert.deepStrictEqual( - JSON.parse(JSON.stringify(dep)), - { - error: { message: "error?" }, - filePath: "filePath?", - id: "id?", - importerName: "importerName?", - importerPath: "importerPath?" - } - ); - }); - }); - - describe("'console.log(...)' should print readable string; not include 'defininition' property", () => { - - // Record the written strings to `output` variable. - let output = ""; - const localConsole = new Console( - new class extends Writable { - write(chunk) { // eslint-disable-line class-methods-use-this - output += chunk; - } - }() - ); - - it("should not print 'definition' property.", () => { - const error = new Error("error?"); // reuse error object to use the same stacktrace. - const dep = new ConfigDependency({ - definition: { name: "definition?" }, - error, - filePath: "filePath?", - id: "id?", - importerName: "importerName?", - importerPath: "importerPath?" - }); - - // Make actual output. - output = ""; - localConsole.log(dep); - const actual = output; - - // Make expected output; no `definition` property. - output = ""; - localConsole.log({ - error, - filePath: "filePath?", - id: "id?", - importerName: "importerName?", - importerPath: "importerPath?" - }); - const expected = output; - - assert.strictEqual(actual, expected); - }); - }); -}); diff --git a/tests/lib/cli-engine/config-array/extracted-config.js b/tests/lib/cli-engine/config-array/extracted-config.js deleted file mode 100644 index afb3981193c..00000000000 --- a/tests/lib/cli-engine/config-array/extracted-config.js +++ /dev/null @@ -1,151 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview Tests for ExtractedConfig class. - * @author Toru Nagashima - */ -"use strict"; - -const assert = require("assert"); -const { ExtractedConfig } = require("../../../../lib/cli-engine/config-array/extracted-config"); - -describe("'ExtractedConfig' class", () => { - describe("'constructor()' should create an instance.", () => { - - /** @type {ExtractedConfig} */ - let config; - - beforeEach(() => { - config = new ExtractedConfig(); - }); - - it("should have 'env' property.", () => { - assert.deepStrictEqual(config.env, {}); - }); - - it("should have 'globals' property.", () => { - assert.deepStrictEqual(config.globals, {}); - }); - - it("should have 'parser' property.", () => { - assert.deepStrictEqual(config.parser, null); - }); - - it("should have 'parserOptions' property.", () => { - assert.deepStrictEqual(config.parserOptions, {}); - }); - - it("should have 'plugins' property.", () => { - assert.deepStrictEqual(config.plugins, {}); - }); - - it("should have 'processor' property.", () => { - assert.deepStrictEqual(config.processor, null); - }); - - it("should have 'rules' property.", () => { - assert.deepStrictEqual(config.rules, {}); - }); - - it("should have 'settings' property.", () => { - assert.deepStrictEqual(config.settings, {}); - }); - }); - - describe("'toCompatibleObjectAsConfigFileContent()' method should return a valid config data.", () => { - - /** @type {ExtractedConfig} */ - let config; - - beforeEach(() => { - config = new ExtractedConfig(); - }); - - it("should use 'env' property as is.", () => { - config.env = { a: true }; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.env, { a: true }); - }); - - it("should use 'globals' as is.", () => { - config.globals = { a: true }; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.globals, { a: true }); - }); - - it("should use 'parser.filePath' for 'parser' property.", () => { - config.parser = { - definition: {}, - error: null, - filePath: "/path/to/a/parser", - id: "parser", - importerName: "importer name", - importerPath: "importer path" - }; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.parser, "/path/to/a/parser"); - }); - - it("should use 'null' for 'parser' property if 'parser' property is 'null'.", () => { - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.parser, null); - }); - - it("should use 'parserOptions' property as is.", () => { - config.parserOptions = { a: true }; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.parserOptions, { a: true }); - }); - - it("should use the keys of 'plugins' property for 'plugins' property.", () => { - config.plugins = { a: {}, b: {} }; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.plugins, ["b", "a"]); - }); - - it("should not use 'processor' property.", () => { - config.processor = "foo/.md"; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.processor, void 0); - }); - - it("should use 'rules' property as is.", () => { - config.rules = { a: 1, b: 2 }; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.rules, { a: 1, b: 2 }); - }); - - it("should use 'settings' property as is.", () => { - config.settings = { a: 1 }; - - const data = config.toCompatibleObjectAsConfigFileContent(); - - assert.deepStrictEqual(data.settings, { a: 1 }); - }); - }); -}); diff --git a/tests/lib/cli-engine/config-array/ignore-pattern.js b/tests/lib/cli-engine/config-array/ignore-pattern.js deleted file mode 100644 index a30cc769fc6..00000000000 --- a/tests/lib/cli-engine/config-array/ignore-pattern.js +++ /dev/null @@ -1,191 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ - -/** - * @fileoverview Tests for IgnorePattern class. - * @author Toru Nagashima - */ -"use strict"; - -const assert = require("assert"); -const path = require("path"); -const sinon = require("sinon"); -const { IgnorePattern } = require("../../../../lib/cli-engine/config-array/ignore-pattern"); - -describe("IgnorePattern", () => { - describe("constructor(patterns, basePath)", () => { - it("should bind the first argument to 'patterns' property.", () => { - const p = new IgnorePattern(["a.js"], process.cwd()); - - assert.deepStrictEqual(p.patterns, ["a.js"]); - }); - - it("should bind the second argument to 'basePath' property.", () => { - const p = new IgnorePattern(["a.js"], process.cwd()); - - assert.strictEqual(p.basePath, process.cwd()); - }); - - it("should throw an error if the second argument was not an absolute path.", () => { - assert.throws(() => new IgnorePattern([], "a.js"), ""); - }); - }); - - describe("getPatternsRelativeTo(newBasePath)", () => { - it("should return 'patterns' as-is if the argument is the same as 'basePath'.", () => { - const basePath1 = path.join(process.cwd(), "foo/bar"); - const p = new IgnorePattern(["a.js", "/b.js", "!c.js", "!/d.js"], basePath1); - - assert.deepStrictEqual( - p.getPatternsRelativeTo(basePath1), - ["a.js", "/b.js", "!c.js", "!/d.js"] - ); - }); - - it("should return modified 'patterns' if the argument is different from 'basePath'.", () => { - const basePath1 = path.join(process.cwd(), "foo/bar"); - const basePath2 = process.cwd(); - const p = new IgnorePattern(["a.js", "/b.js", "!c.js", "!/d.js"], basePath1); - - assert.deepStrictEqual( - p.getPatternsRelativeTo(basePath2), - ["/foo/bar/**/a.js", "/foo/bar/b.js", "!/foo/bar/**/c.js", "!/foo/bar/d.js"] - ); - }); - }); - - describe("static createIgnore(ignorePatterns)", () => { - describe("with two patterns should return a function, and the function", () => { - - /** - * performs static createIgnre assertions against the cwd. - * @param {string} cwd cwd to be the base path for assertions - * @returns {void} - */ - function assertions(cwd) { - const basePath1 = path.join(cwd, "foo/bar"); - const basePath2 = path.join(cwd, "abc/"); - const ignores = IgnorePattern.createIgnore([ - new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath1), - new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath2) - ]); - const patterns = [ - ["a.js", false], - ["a.ts", false], - ["b.js", false], - ["b.ts", false], - ["c.js", false], - ["c.ts", false], - ["dir/a.js", false], - ["dir/a.ts", false], - ["dir/b.js", false], - ["dir/b.ts", false], - ["dir/c.js", false], - ["dir/c.ts", false], - ["foo/bar/a.js", false], - ["foo/bar/a.ts", false], - ["foo/bar/b.js", false], - ["foo/bar/b.ts", false], - ["foo/bar/c.js", true], - ["foo/bar/c.ts", true], - ["foo/bar/dir/a.js", false], - ["foo/bar/dir/a.ts", false], - ["foo/bar/dir/b.js", true], - ["foo/bar/dir/b.ts", false], - ["foo/bar/dir/c.js", true], - ["foo/bar/dir/c.ts", false], - ["abc/a.js", false], - ["abc/a.ts", false], - ["abc/b.js", false], - ["abc/b.ts", false], - ["abc/c.js", true], - ["abc/c.ts", true], - ["abc/dir/a.js", false], - ["abc/dir/a.ts", false], - ["abc/dir/b.js", true], - ["abc/dir/b.ts", false], - ["abc/dir/c.js", true], - ["abc/dir/c.ts", false] - ]; - - for (const [filename, expected] of patterns) { - it(`should return ${expected} if '${filename}' was given.`, () => { - assert.strictEqual(ignores(path.join(cwd, filename)), expected); - }); - } - - it("should return false if '.dot.js' and false was given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot.js"), false), true); - }); - - it("should return true if '.dot.js' and true were given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot.js"), true), false); - }); - - it("should return false if '.dot/foo.js' and false was given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), false), true); - }); - - it("should return true if '.dot/foo.js' and true were given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), true), false); - }); - } - - assertions(process.cwd()); - - /* - * This will catch regressions of Windows specific issue #12850 when run on CI nodes. - * This runs the full set of assertions for the function returned from IgnorePattern.createIgnore. - * When run on Windows CI nodes the .root drive i.e C:\ will be supplied - * forcing getCommonAncestors to resolve to the root of the drive thus catching any regrssion of 12850. - * When run on *nix CI nodes provides additional coverage on this OS too. - * assertions when run on Windows CI nodes and / on *nix OS - */ - assertions(path.parse(process.cwd()).root); - }); - }); - - describe("static createIgnore(ignorePatterns)", () => { - - /* - * This test will catch regressions of Windows specific issue #12850 when run on your local dev box - * irrespective of if you are running a Windows or *nix style OS. - * When running on *nix sinon is used to emulate Windows behaviors of path and platform APIs - * thus ensuring that the Windows specific fix is exercised and any regression is caught. - */ - it("with common ancestor of drive root on windows should not throw", () => { - try { - - /* - * When not on Windows return win32 values so local runs on *nix hit the same code path as on Windows - * thus enabling developers with *nix style OS to catch and debug any future regression of #12850 without - * requiring a Windows based OS. - */ - if (process.platform !== "win32") { - sinon.stub(process, "platform").value("win32"); - sinon.stub(path, "sep").value(path.win32.sep); - sinon.replace(path, "isAbsolute", path.win32.isAbsolute); - } - - const ignores = IgnorePattern.createIgnore([ - new IgnorePattern(["*.js"], "C:\\foo\\bar"), - new IgnorePattern(["*.js"], "C:\\abc\\") - ]); - - // calls to this should not throw when getCommonAncestor returns root of drive - ignores("C:\\abc\\contract.d.ts"); - } finally { - sinon.restore(); - } - }); - }); -}); diff --git a/tests/lib/cli-engine/config-array/override-tester.js b/tests/lib/cli-engine/config-array/override-tester.js deleted file mode 100644 index b367ff93fdb..00000000000 --- a/tests/lib/cli-engine/config-array/override-tester.js +++ /dev/null @@ -1,297 +0,0 @@ -/* - * STOP!!! DO NOT MODIFY. - * - * This file is part of the ongoing work to move the eslintrc-style config - * system into the @eslint/eslintrc package. This file needs to remain - * unchanged in order for this work to proceed. - * - * If you think you need to change this file, please contact @nzakas first. - * - * Thanks in advance for your cooperation. - */ -/** - * @fileoverview Tests for OverrideTester class. - * @author Toru Nagashima - */ -"use strict"; - -const assert = require("assert"); -const { Console } = require("console"); -const path = require("path"); -const { Writable } = require("stream"); -const { OverrideTester } = require("../../../../lib/cli-engine/config-array/override-tester"); - -describe("OverrideTester", () => { - describe("'create(files, excludedFiles, basePath)' should create a tester.", () => { - for (const { files, excludedFiles, basePath } of [ - { files: void 0, excludedFiles: void 0, basePath: process.cwd() }, - { files: [], excludedFiles: [], basePath: process.cwd() } - ]) { - it(`should return null if ${JSON.stringify({ files, excludedFiles, basePath })} was given.`, () => { - assert.strictEqual( - OverrideTester.create(files, excludedFiles, basePath), - null - ); - }); - } - - it("should return an 'OverrideTester' instance that has given parameters if strings were given.", () => { - const files = "*.js"; - const excludedFiles = "ignore/*"; - const basePath = process.cwd(); - const tester = OverrideTester.create(files, excludedFiles, basePath); - - assert.strictEqual(tester.patterns.length, 1); - assert.strictEqual(tester.patterns[0].includes.length, 1); - assert.strictEqual(tester.patterns[0].excludes.length, 1); - assert.strictEqual(tester.patterns[0].includes[0].pattern, files); - assert.strictEqual(tester.patterns[0].excludes[0].pattern, excludedFiles); - assert.strictEqual(tester.basePath, basePath); - }); - - it("should return an 'OverrideTester' instance that has given parameters if arrays were given.", () => { - const files = ["*.js"]; - const excludedFiles = ["ignore/*"]; - const basePath = process.cwd(); - const tester = OverrideTester.create(files, excludedFiles, basePath); - - assert.strictEqual(tester.patterns.length, 1); - assert.strictEqual(tester.patterns[0].includes.length, 1); - assert.strictEqual(tester.patterns[0].excludes.length, 1); - assert.strictEqual(tester.patterns[0].includes[0].pattern, files[0]); - assert.strictEqual(tester.patterns[0].excludes[0].pattern, excludedFiles[0]); - assert.strictEqual(tester.basePath, basePath); - }); - }); - - describe("'and(a, b)' should return either or create another tester what includes both.", () => { - it("should return null if both were null.", () => { - assert.strictEqual(OverrideTester.and(null, null), null); - }); - - it("should return a new tester with the the first one's properties if the second one was null.", () => { - const tester = OverrideTester.create("*.js", null, process.cwd()); - const result = OverrideTester.and(tester, null); - - assert.notStrictEqual(result, tester); - assert.strictEqual(result.patterns, tester.patterns); - assert.strictEqual(result.basePath, tester.basePath); - }); - - it("should return a new tester with the the second one's properties if the first one was null.", () => { - const tester = OverrideTester.create("*.js", null, process.cwd()); - const result = OverrideTester.and(null, tester); - - assert.notStrictEqual(result, tester); - assert.strictEqual(result.patterns, tester.patterns); - assert.strictEqual(result.basePath, tester.basePath); - }); - - it("should return another one what includes both patterns if both are testers.", () => { - const tester1 = OverrideTester.create("*.js"); - const tester2 = OverrideTester.create("*.ts"); - const tester3 = OverrideTester.and(tester1, tester2); - - assert.strictEqual(tester3.patterns.length, 2); - assert.strictEqual(tester3.patterns[0], tester1.patterns[0]); - assert.strictEqual(tester3.patterns[1], tester2.patterns[0]); - }); - }); - - describe("'test(filePath)' method", () => { - it("should throw an error if no arguments were given.", () => { - assert.throws(() => { - OverrideTester.create(["*.js"], [], process.cwd()).test(); - }, /'filePath' should be an absolute path, but got undefined/u); - }); - - it("should throw an error if a non-string value was given.", () => { - assert.throws(() => { - OverrideTester.create(["*.js"], [], process.cwd()).test(100); - }, /'filePath' should be an absolute path, but got 100/u); - }); - - it("should throw an error if a relative path was given.", () => { - assert.throws(() => { - OverrideTester.create(["*.js"], [], process.cwd()).test("foo/bar.js"); - }, /'filePath' should be an absolute path, but got foo\/bar\.js/u); - }); - - it("should return true only when both conditions are matched if the tester was created by 'and' factory function.", () => { - const tester = OverrideTester.and( - OverrideTester.create(["*.js"], [], process.cwd()), - OverrideTester.create(["test/**"], [], process.cwd()) - ); - - assert.strictEqual(tester.test(path.resolve("test/a.js")), true); - assert.strictEqual(tester.test(path.resolve("lib/a.js")), false); - assert.strictEqual(tester.test(path.resolve("test/a.ts")), false); - }); - - /** - * Test if a given file path matches to the given condition. - * - * The test cases which depend on this function were moved from - * 'tests/lib/config/config-ops.js' when refactoring to keep the - * cumulated test cases. - * - * Previously, the testing logic of `overrides` properties had been - * implemented in `ConfigOps.pathMatchesGlobs()` function. But - * currently, it's implemented in `OverrideTester` class. - * @param {string} filePath The file path to test patterns against - * @param {string|string[]} files One or more glob patterns - * @param {string|string[]} [excludedFiles] One or more glob patterns - * @returns {boolean} The result. - */ - function test(filePath, files, excludedFiles) { - const basePath = process.cwd(); - const tester = OverrideTester.create(files, excludedFiles, basePath); - - return tester.test(path.resolve(basePath, filePath)); - } - - /** - * Emits a test that confirms the specified file path matches the specified combination of patterns. - * @param {string} filePath The file path to test patterns against - * @param {string|string[]} patterns One or more glob patterns - * @param {string|string[]} [excludedPatterns] One or more glob patterns - * @returns {void} - */ - function match(filePath, patterns, excludedPatterns) { - it(`matches ${filePath} given '${patterns.join("','")}' includes and '${excludedPatterns.join("','")}' excludes`, () => { - const result = test(filePath, patterns, excludedPatterns); - - assert.strictEqual(result, true); - }); - } - - /** - * Emits a test that confirms the specified file path does not match the specified combination of patterns. - * @param {string} filePath The file path to test patterns against - * @param {string|string[]} patterns One or more glob patterns - * @param {string|string[]} [excludedPatterns] One or more glob patterns - * @returns {void} - */ - function noMatch(filePath, patterns, excludedPatterns) { - it(`does not match ${filePath} given '${patterns.join("','")}' includes and '${excludedPatterns.join("','")}' excludes`, () => { - const result = test(filePath, patterns, excludedPatterns); - - assert.strictEqual(result, false); - }); - } - - /** - * Emits a test that confirms the specified pattern throws an error. - * @param {string} filePath The file path to test the pattern against - * @param {string} pattern The glob pattern that should trigger the error condition - * @param {string} expectedMessage The expected error's message - * @returns {void} - */ - function error(filePath, pattern, expectedMessage) { - it(`emits an error given '${pattern}'`, () => { - let errorMessage; - - try { - test(filePath, pattern); - } catch (e) { - errorMessage = e.message; - } - - assert.strictEqual(errorMessage, expectedMessage); - }); - } - - // files in the project root - match("foo.js", ["foo.js"], []); - match("foo.js", ["*"], []); - match("foo.js", ["*.js"], []); - match("foo.js", ["**/*.js"], []); - match("bar.js", ["*.js"], ["foo.js"]); - match("foo.js", ["./foo.js"], []); - match("foo.js", ["./*"], []); - match("foo.js", ["./**"], []); - - noMatch("foo.js", ["*"], ["foo.js"]); - noMatch("foo.js", ["*.js"], ["foo.js"]); - noMatch("foo.js", ["**/*.js"], ["foo.js"]); - - // files in a subdirectory - match("subdir/foo.js", ["foo.js"], []); - match("subdir/foo.js", ["*"], []); - match("subdir/foo.js", ["*.js"], []); - match("subdir/foo.js", ["**/*.js"], []); - match("subdir/foo.js", ["subdir/*.js"], []); - match("subdir/foo.js", ["subdir/foo.js"], []); - match("subdir/foo.js", ["subdir/*"], []); - match("subdir/second/foo.js", ["subdir/**"], []); - match("subdir/foo.js", ["./**"], []); - match("subdir/foo.js", ["./subdir/**"], []); - match("subdir/foo.js", ["./subdir/*"], []); - - noMatch("subdir/foo.js", ["./foo.js"], []); - noMatch("subdir/foo.js", ["*"], ["subdir/**"]); - noMatch("subdir/very/deep/foo.js", ["*.js"], ["subdir/**"]); - noMatch("subdir/second/foo.js", ["subdir/*"], []); - noMatch("subdir/second/foo.js", ["subdir/**"], ["subdir/second/*"]); - - // error conditions - error("foo.js", ["/*.js"], "Invalid override pattern (expected relative path not containing '..'): /*.js"); - error("foo.js", ["/foo.js"], "Invalid override pattern (expected relative path not containing '..'): /foo.js"); - error("foo.js", ["../**"], "Invalid override pattern (expected relative path not containing '..'): ../**"); - }); - - describe("'JSON.stringify(...)' should return readable JSON; not include 'Minimatch' objects", () => { - it("should return an object that has three properties 'includes', 'excludes', and 'basePath' if that 'patterns' property include one object.", () => { - const files = "*.js"; - const excludedFiles = "test/*"; - const basePath = process.cwd(); - const tester = OverrideTester.create(files, excludedFiles, basePath); - - assert.strictEqual( - JSON.stringify(tester), - `{"includes":["${files}"],"excludes":["${excludedFiles}"],"basePath":${JSON.stringify(basePath)}}` - ); - }); - - it("should return an object that has two properties 'AND' and 'basePath' if that 'patterns' property include two or more objects.", () => { - const files1 = "*.js"; - const excludedFiles1 = "test/*"; - const files2 = "*.story.js"; - const excludedFiles2 = "src/*"; - const basePath = process.cwd(); - const tester = OverrideTester.and( - OverrideTester.create(files1, excludedFiles1, basePath), - OverrideTester.create(files2, excludedFiles2, basePath) - ); - - assert.deepStrictEqual( - JSON.parse(JSON.stringify(tester)), - { - AND: [ - { includes: [files1], excludes: [excludedFiles1] }, - { includes: [files2], excludes: [excludedFiles2] } - ], - basePath - } - ); - }); - }); - - describe("'console.log(...)' should print readable string; not include 'Minimatch' objects", () => { - const localConsole = new Console(new Writable()); - - it("should use 'toJSON()' method.", () => { - const tester = OverrideTester.create("*.js", "", process.cwd()); - let called = false; - - tester.toJSON = () => { - called = true; - return ""; - }; - - localConsole.log(tester); - - assert(called); - }); - }); -}); diff --git a/tests/lib/cli-engine/file-enumerator.js b/tests/lib/cli-engine/file-enumerator.js index 7f91b2a95f4..1561b42e105 100644 --- a/tests/lib/cli-engine/file-enumerator.js +++ b/tests/lib/cli-engine/file-enumerator.js @@ -10,30 +10,24 @@ const os = require("os"); const { assert } = require("chai"); const sh = require("shelljs"); const { CascadingConfigArrayFactory } = - require("../../../lib/cli-engine/cascading-config-array-factory"); -const { defineFileEnumeratorWithInMemoryFileSystem } = require("../../_utils"); + require("@eslint/eslintrc/lib/cascading-config-array-factory"); +const { createCustomTeardown } = require("../../_utils"); +const { FileEnumerator } = require("../../../lib/cli-engine/file-enumerator"); describe("FileEnumerator", () => { describe("'iterateFiles(patterns)' method should iterate files and configs.", () => { describe("with three directories ('lib', 'lib/nested', 'test') that contains 'one.js' and 'two.js'", () => { const root = path.join(os.tmpdir(), "eslint/file-enumerator"); const files = { - /* eslint-disable quote-props */ - "lib": { - "nested": { - "one.js": "", - "two.js": "", - "parser.js": "", - ".eslintrc.yml": "parser: './parser'" - }, - "one.js": "", - "two.js": "" - }, - "test": { - "one.js": "", - "two.js": "", - ".eslintrc.yml": "env: { mocha: true }" - }, + "lib/nested/one.js": "", + "lib/nested/two.js": "", + "lib/nested/parser.js": "", + "lib/nested/.eslintrc.yml": "parser: './parser'", + "lib/one.js": "", + "lib/two.js": "", + "test/one.js": "", + "test/two.js": "", + "test/.eslintrc.yml": "env: { mocha: true }", ".eslintignore": "/lib/nested/parser.js", ".eslintrc.json": JSON.stringify({ rules: { @@ -41,17 +35,19 @@ describe("FileEnumerator", () => { "no-unused-vars": "error" } }) - /* eslint-enable quote-props */ }; - const { FileEnumerator } = defineFileEnumeratorWithInMemoryFileSystem({ cwd: () => root, files }); + const { prepare, cleanup, getPath } = createCustomTeardown({ cwd: root, files }); /** @type {FileEnumerator} */ let enumerator; - beforeEach(() => { - enumerator = new FileEnumerator(); + beforeEach(async () => { + await prepare(); + enumerator = new FileEnumerator({ cwd: getPath() }); }); + afterEach(cleanup); + it("should ignore empty strings.", () => { Array.from(enumerator.iterateFiles(["lib/*.js", ""])); // don't throw "file not found" error. }); @@ -177,7 +173,6 @@ describe("FileEnumerator", () => { // This group moved from 'tests/lib/util/glob-utils.js' when refactoring to keep the cumulated test cases. describe("with 'tests/fixtures/glob-utils' files", () => { - const { FileEnumerator } = require("../../../lib/cli-engine/file-enumerator"); let fixtureDir; /** diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index d6abf5a8bda..f9ec1e7c57d 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -19,9 +19,9 @@ const fCache = require("file-entry-cache"); const sinon = require("sinon"); const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); const shell = require("shelljs"); -const { CascadingConfigArrayFactory } = require("../../../lib/cli-engine/cascading-config-array-factory"); +const { CascadingConfigArrayFactory } = require("@eslint/eslintrc/lib/cascading-config-array-factory"); const hash = require("../../../lib/cli-engine/hash"); -const { unIndent, defineESLintWithInMemoryFileSystem } = require("../../_utils"); +const { unIndent, createCustomTeardown } = require("../../_utils"); //------------------------------------------------------------------------------ // Tests @@ -2945,26 +2945,31 @@ describe("ESLint", () => { }); describe("a config file setting should have higher priority than a shareable config file's settings always; https://github.com/eslint/eslint/issues/11510", () => { + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: path.join(os.tmpdir(), "eslint/11510"), + files: { + "no-console-error-in-overrides.json": JSON.stringify({ + overrides: [{ + files: ["*.js"], + rules: { "no-console": "error" } + }] + }), + ".eslintrc.json": JSON.stringify({ + extends: "./no-console-error-in-overrides.json", + rules: { "no-console": "off" } + }), + "a.js": "console.log();" + } + }); + beforeEach(() => { - ({ ESLint } = defineESLintWithInMemoryFileSystem({ - cwd: () => path.join(os.tmpdir(), "eslint/11510"), - files: { - "no-console-error-in-overrides.json": JSON.stringify({ - overrides: [{ - files: ["*.js"], - rules: { "no-console": "error" } - }] - }), - ".eslintrc.json": JSON.stringify({ - extends: "./no-console-error-in-overrides.json", - rules: { "no-console": "off" } - }), - "a.js": "console.log();" - } - })); - eslint = new ESLint(); + eslint = new ESLint({ cwd: getPath() }); + return prepare(); }); + afterEach(cleanup); + it("should not report 'no-console' error.", async () => { const results = await eslint.lintFiles("a.js"); @@ -2974,11 +2979,11 @@ describe("ESLint", () => { }); describe("configs of plugin rules should be validated even if 'plugins' key doesn't exist; https://github.com/eslint/eslint/issues/11559", () => { - beforeEach(() => { - ({ ESLint } = defineESLintWithInMemoryFileSystem({ - cwd: () => path.join(os.tmpdir(), "eslint/11559"), - files: { - "node_modules/eslint-plugin-test/index.js": ` + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: path.join(os.tmpdir(), "eslint/11559"), + files: { + "node_modules/eslint-plugin-test/index.js": ` exports.configs = { recommended: { plugins: ["test"] } }; @@ -2989,20 +2994,26 @@ describe("ESLint", () => { } }; `, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": JSON.stringify({ - // Import via the recommended config. - extends: "plugin:test/recommended", + // Import via the recommended config. + extends: "plugin:test/recommended", - // Has invalid option. - rules: { "test/foo": ["error", "invalid-option"] } - }), - "a.js": "console.log();" - } - })); - eslint = new ESLint(); + // Has invalid option. + rules: { "test/foo": ["error", "invalid-option"] } + }), + "a.js": "console.log();" + } + }); + + beforeEach(() => { + eslint = new ESLint({ cwd: getPath() }); + return prepare(); }); + afterEach(cleanup); + + it("should throw fatal error.", async () => { await assert.rejects(async () => { await eslint.lintFiles("a.js"); @@ -3011,11 +3022,10 @@ describe("ESLint", () => { }); describe("'--fix-type' should not crash even if plugin rules exist; https://github.com/eslint/eslint/issues/11586", () => { - beforeEach(() => { - ({ ESLint } = defineESLintWithInMemoryFileSystem({ - cwd: () => path.join(os.tmpdir(), "eslint/11586"), - files: { - "node_modules/eslint-plugin-test/index.js": ` + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: path.join(os.tmpdir(), "cli-engine/11586"), + files: { + "node_modules/eslint-plugin-test/index.js": ` exports.rules = { "no-example": { meta: { type: "problem", fixable: "code" }, @@ -3035,16 +3045,26 @@ describe("ESLint", () => { } }; `, - ".eslintrc.json": JSON.stringify({ - plugins: ["test"], - rules: { "test/no-example": "error" } - }), - "a.js": "example;" - } - })); - eslint = new ESLint({ fix: true, fixTypes: ["problem"] }); + ".eslintrc.json": { + plugins: ["test"], + rules: { "test/no-example": "error" } + }, + "a.js": "example;" + } }); + beforeEach(() => { + eslint = new ESLint({ + cwd: getPath(), + fix: true, + fixTypes: ["problem"] + }); + + return prepare(); + }); + + afterEach(cleanup); + it("should not crash.", async () => { const results = await eslint.lintFiles("a.js"); @@ -3095,18 +3115,29 @@ describe("ESLint", () => { ` }; + let cleanup; + + beforeEach(() => { + cleanup = () => { }; + }); + + afterEach(() => cleanup()); + it("should lint only JavaScript blocks if '--ext' was not given.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" } - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root }); + }); + + cleanup = teardown.cleanup; + await teardown.prepare(); + eslint = new ESLint({ cwd: teardown.getPath() }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3116,17 +3147,20 @@ describe("ESLint", () => { }); it("should fix only JavaScript blocks if '--ext' was not given.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" } - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root, fix: true }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath(), fix: true }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3148,17 +3182,20 @@ describe("ESLint", () => { }); it("should lint HTML blocks as well with multiple processors if '--ext' option was given.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" } - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root, extensions: ["js", "html"] }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3170,17 +3207,20 @@ describe("ESLint", () => { }); it("should fix HTML blocks as well with multiple processors if '--ext' option was given.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" } - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root, extensions: ["js", "html"], fix: true }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"], fix: true }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3202,11 +3242,11 @@ describe("ESLint", () => { }); it("should use overridden processor; should report HTML blocks but not fix HTML blocks if the processor for '*.html' didn't support autofix.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" }, overrides: [ @@ -3215,10 +3255,13 @@ describe("ESLint", () => { processor: "html/non-fixable" // supportsAutofix: false } ] - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root, extensions: ["js", "html"], fix: true }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"], fix: true }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3243,11 +3286,11 @@ describe("ESLint", () => { }); it("should use the config '**/*.html/*.js' to lint JavaScript blocks in HTML.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" }, overrides: [ @@ -3268,10 +3311,13 @@ describe("ESLint", () => { } } ] - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root, extensions: ["js", "html"] }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3283,11 +3329,11 @@ describe("ESLint", () => { }); it("should use the same config as one which has 'processor' property in order to lint blocks in HTML if the processor was legacy style.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" }, overrides: [ @@ -3307,10 +3353,13 @@ describe("ESLint", () => { } } ] - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root, extensions: ["js", "html"] }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath(), extensions: ["js", "html"] }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3324,17 +3373,20 @@ describe("ESLint", () => { }); it("should throw an error if invalid processor was specified.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], processor: "markdown/unknown" - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); await assert.rejects(async () => { await eslint.lintFiles(["test.md"]); @@ -3342,11 +3394,11 @@ describe("ESLint", () => { }); it("should lint HTML blocks as well with multiple processors if 'overrides[].files' is present.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { ...commonFiles, - ".eslintrc.json": JSON.stringify({ + ".eslintrc.json": { plugins: ["markdown", "html"], rules: { semi: "error" }, overrides: [ @@ -3359,10 +3411,13 @@ describe("ESLint", () => { processor: "markdown/.md" } ] - }) + } } - }).ESLint; - eslint = new ESLint({ cwd: root }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); const results = await eslint.lintFiles(["test.md"]); assert.strictEqual(results.length, 1); @@ -3473,27 +3528,33 @@ describe("ESLint", () => { }); describe("with '--rulesdir' option", () => { - it("should use the configured rules which are defined by '--rulesdir' option.", async () => { - const rootPath = getFixturePath("cli-engine/with-rulesdir"); - const StubbedESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => rootPath, - files: { - "internal-rules/test.js": ` + + const rootPath = getFixturePath("cli-engine/with-rulesdir"); + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: rootPath, + files: { + "internal-rules/test.js": ` module.exports = context => ({ ExpressionStatement(node) { context.report({ node, message: "ok" }) } }) `, - ".eslintrc.json": JSON.stringify({ - root: true, - rules: { test: "error" } - }), - "test.js": "console.log('hello')" - } - }).ESLint; + ".eslintrc.json": { + root: true, + rules: { test: "error" } + }, + "test.js": "console.log('hello')" + } + }); + + beforeEach(prepare); + afterEach(cleanup); + - eslint = new StubbedESLint({ + it("should use the configured rules which are defined by '--rulesdir' option.", async () => { + eslint = new ESLint({ + cwd: getPath(), rulePaths: ["internal-rules"] }); const results = await eslint.lintFiles(["test.js"]); @@ -3507,9 +3568,18 @@ describe("ESLint", () => { describe("glob pattern '[ab].js'", () => { const root = getFixturePath("cli-engine/unmatched-glob"); + let cleanup; + + beforeEach(() => { + cleanup = () => { }; + }); + + afterEach(() => cleanup()); + it("should match '[ab].js' if existed.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + + const teardown = createCustomTeardown({ + cwd: root, files: { "a.js": "", "b.js": "", @@ -3517,8 +3587,12 @@ describe("ESLint", () => { "[ab].js": "", ".eslintrc.yml": "root: true" } - }).ESLint; - eslint = new ESLint(); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + + eslint = new ESLint({ cwd: teardown.getPath() }); const results = await eslint.lintFiles(["[ab].js"]); const filenames = results.map(r => path.basename(r.filePath)); @@ -3526,16 +3600,19 @@ describe("ESLint", () => { }); it("should match 'a.js' and 'b.js' if '[ab].js' didn't existed.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { "a.js": "", "b.js": "", "ab.js": "", ".eslintrc.yml": "root: true" } - }).ESLint; - eslint = new ESLint(); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); const results = await eslint.lintFiles(["[ab].js"]); const filenames = results.map(r => path.basename(r.filePath)); @@ -3546,15 +3623,27 @@ describe("ESLint", () => { describe("with 'noInlineConfig' setting", () => { const root = getFixturePath("cli-engine/noInlineConfig"); + let cleanup; + + beforeEach(() => { + cleanup = () => { }; + }); + + afterEach(() => cleanup()); + it("should warn directive comments if 'noInlineConfig' was given.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { "test.js": "/* globals foo */", ".eslintrc.yml": "noInlineConfig: true" } - }).ESLint; - eslint = new ESLint(); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); + const results = await eslint.lintFiles(["test.js"]); const messages = results[0].messages; @@ -3563,15 +3652,19 @@ describe("ESLint", () => { }); it("should show the config file what the 'noInlineConfig' came from.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { "node_modules/eslint-config-foo/index.js": "module.exports = {noInlineConfig: true}", "test.js": "/* globals foo */", ".eslintrc.yml": "extends: foo" } - }).ESLint; - eslint = new ESLint(); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); + const results = await eslint.lintFiles(["test.js"]); const messages = results[0].messages; @@ -3583,15 +3676,28 @@ describe("ESLint", () => { describe("with 'reportUnusedDisableDirectives' setting", () => { const root = getFixturePath("cli-engine/reportUnusedDisableDirectives"); + let cleanup; + + beforeEach(() => { + cleanup = () => { }; + }); + + afterEach(() => cleanup()); + it("should warn unused 'eslint-disable' comments if 'reportUnusedDisableDirectives' was given.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { "test.js": "/* eslint-disable eqeqeq */", ".eslintrc.yml": "reportUnusedDisableDirectives: true" } - }).ESLint; - eslint = new ESLint(); + }); + + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); + const results = await eslint.lintFiles(["test.js"]); const messages = results[0].messages; @@ -3602,14 +3708,22 @@ describe("ESLint", () => { describe("the runtime option overrides config files.", () => { it("should not warn unused 'eslint-disable' comments if 'reportUnusedDisableDirectives=off' was given in runtime.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { "test.js": "/* eslint-disable eqeqeq */", ".eslintrc.yml": "reportUnusedDisableDirectives: true" } - }).ESLint; - eslint = new ESLint({ reportUnusedDisableDirectives: "off" }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + + eslint = new ESLint({ + cwd: teardown.getPath(), + reportUnusedDisableDirectives: "off" + }); + const results = await eslint.lintFiles(["test.js"]); const messages = results[0].messages; @@ -3617,14 +3731,22 @@ describe("ESLint", () => { }); it("should warn unused 'eslint-disable' comments as error if 'reportUnusedDisableDirectives=error' was given in runtime.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { "test.js": "/* eslint-disable eqeqeq */", ".eslintrc.yml": "reportUnusedDisableDirectives: true" } - }).ESLint; - eslint = new ESLint({ reportUnusedDisableDirectives: "error" }); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + + eslint = new ESLint({ + cwd: teardown.getPath(), + reportUnusedDisableDirectives: "error" + }); + const results = await eslint.lintFiles(["test.js"]); const messages = results[0].messages; @@ -3637,25 +3759,28 @@ describe("ESLint", () => { describe("with 'overrides[*].extends' setting on deep locations", () => { const root = getFixturePath("cli-engine/deeply-overrides-i-extends"); + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + overrides: [{ files: ["*test*"], extends: "two" }] + })}`, + "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ + overrides: [{ files: ["*.js"], extends: "three" }] + })}`, + "node_modules/eslint-config-three/index.js": `module.exports = ${JSON.stringify({ + rules: { "no-console": "error" } + })}`, + "test.js": "console.log('hello')", + ".eslintrc.yml": "extends: one" + } + }); + + beforeEach(prepare); + afterEach(cleanup); it("should not throw.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - overrides: [{ files: ["*test*"], extends: "two" }] - })}`, - "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ - overrides: [{ files: ["*.js"], extends: "three" }] - })}`, - "node_modules/eslint-config-three/index.js": `module.exports = ${JSON.stringify({ - rules: { "no-console": "error" } - })}`, - "test.js": "console.log('hello')", - ".eslintrc.yml": "extends: one" - } - }).ESLint; - eslint = new ESLint(); + eslint = new ESLint({ cwd: getPath() }); const results = await eslint.lintFiles(["test.js"]); const messages = results[0].messages; @@ -3667,46 +3792,71 @@ describe("ESLint", () => { describe("don't ignore the entry directory.", () => { const root = getFixturePath("cli-engine/dont-ignore-entry-dir"); + let cleanup; + + beforeEach(() => { + cleanup = () => { }; + }); + + afterEach(async () => { + await cleanup(); + + const configFilePath = path.resolve(root, "../.eslintrc.json"); + + if (shell.test("-e", configFilePath)) { + shell.rm(configFilePath); + } + }); + it("'lintFiles(\".\")' should not load config files from outside of \".\".", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { "../.eslintrc.json": "BROKEN FILE", ".eslintrc.json": JSON.stringify({ root: true }), "index.js": "console.log(\"hello\")" } - }).ESLint; - eslint = new ESLint(); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); // Don't throw "failed to load config file" error. await eslint.lintFiles("."); }); it("'lintFiles(\".\")' should not ignore '.' even if 'ignorePatterns' contains it.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { - "../.eslintrc.json": JSON.stringify({ ignorePatterns: ["/dont-ignore-entry-dir"] }), - ".eslintrc.json": JSON.stringify({ root: true }), + "../.eslintrc.json": { ignorePatterns: ["/dont-ignore-entry-dir"] }, + ".eslintrc.json": { root: true }, "index.js": "console.log(\"hello\")" } - }).ESLint; - eslint = new ESLint(); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); // Don't throw "file not found" error. await eslint.lintFiles("."); }); it("'lintFiles(\"subdir\")' should not ignore './subdir' even if 'ignorePatterns' contains it.", async () => { - ESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, + const teardown = createCustomTeardown({ + cwd: root, files: { - ".eslintrc.json": JSON.stringify({ ignorePatterns: ["/subdir"] }), - "subdir/.eslintrc.json": JSON.stringify({ root: true }), + ".eslintrc.json": { ignorePatterns: ["/subdir"] }, + "subdir/.eslintrc.json": { root: true }, "subdir/index.js": "console.log(\"hello\")" } - }).ESLint; - eslint = new ESLint(); + }); + + await teardown.prepare(); + cleanup = teardown.cleanup; + eslint = new ESLint({ cwd: teardown.getPath() }); // Don't throw "file not found" error. await eslint.lintFiles("subdir"); @@ -4438,9 +4588,10 @@ describe("ESLint", () => { writeFile: sinon.spy(callLastArgument) }; const spy = fakeFS.writeFile; - const localESLint = proxyquire("../../../lib/eslint/eslint", { + const { ESLint: localESLint } = proxyquire("../../../lib/eslint/eslint", { fs: fakeFS - }).ESLint; + }); + const results = [ { filePath: path.resolve("foo.js"), @@ -4464,9 +4615,9 @@ describe("ESLint", () => { writeFile: sinon.spy(callLastArgument) }; const spy = fakeFS.writeFile; - const localESLint = proxyquire("../../../lib/eslint/eslint", { + const { ESLint: localESLint } = proxyquire("../../../lib/eslint/eslint", { fs: fakeFS - }).ESLint; + }); const results = [ { filePath: path.resolve("foo.js"), @@ -4638,41 +4789,39 @@ describe("ESLint", () => { describe("with ignorePatterns config", () => { const root = getFixturePath("cli-engine/ignore-patterns"); - /** @type {typeof ESLint} */ - let InMemoryESLint; - describe("ignorePatterns can add an ignore pattern ('foo.js').", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "foo.js" - }), - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": { + ignorePatterns: "foo.js" + }, + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); }); it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), false); assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), false); }); it("'lintFiles()' should not verify 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4685,39 +4834,40 @@ describe("ESLint", () => { }); describe("ignorePatterns can add ignore patterns ('foo.js', '/bar.js').", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: ["foo.js", "/bar.js"] - }), - "foo.js": "", - "bar.js": "", - "baz.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "", - "subdir/baz.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": { + ignorePatterns: ["foo.js", "/bar.js"] + }, + "foo.js": "", + "bar.js": "", + "baz.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/baz.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); }); it("'isPathIgnored()' should return 'true' for '/bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), true); assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), false); }); it("'lintFiles()' should not verify 'foo.js' and '/bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4731,41 +4881,43 @@ describe("ESLint", () => { }); describe("ignorePatterns can unignore '/node_modules/foo'.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "!/node_modules/foo" - }), - "node_modules/foo/index.js": "", - "node_modules/foo/.dot.js": "", - "node_modules/bar/index.js": "", - "foo.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": { + ignorePatterns: "!/node_modules/foo" + }, + "node_modules/foo/index.js": "", + "node_modules/foo/.dot.js": "", + "node_modules/bar/index.js": "", + "foo.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'false' for 'node_modules/foo/index.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("node_modules/foo/index.js"), false); }); it("'isPathIgnored()' should return 'true' for 'node_modules/foo/.dot.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("node_modules/foo/.dot.js"), true); }); it("'isPathIgnored()' should return 'true' for 'node_modules/bar/index.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("node_modules/bar/index.js"), true); }); it("'lintFiles()' should verify 'node_modules/foo/index.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4778,26 +4930,28 @@ describe("ESLint", () => { }); describe("ignorePatterns can unignore '.eslintrc.js'.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "!.eslintrc.js" - })}`, - "foo.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.eslintrc.js" + })}`, + "foo.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'false' for '.eslintrc.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored(".eslintrc.js"), false); }); it("'lintFiles()' should verify '.eslintrc.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4810,34 +4964,35 @@ describe("ESLint", () => { }); describe(".eslintignore can re-ignore files that are unignored by ignorePatterns.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "!.*" - })}`, - ".eslintignore": ".foo*", - ".foo.js": "", - ".bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.*" + })}`, + ".eslintignore": ".foo*", + ".foo.js": "", + ".bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for re-ignored '.foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored(".foo.js"), true); }); it("'isPathIgnored()' should return 'false' for unignored '.bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored(".bar.js"), false); }); it("'lintFiles()' should not verify re-ignored '.foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4850,34 +5005,35 @@ describe("ESLint", () => { }); describe(".eslintignore can unignore files that are ignored by ignorePatterns.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "*.js" - })}`, - ".eslintignore": "!foo.js", - "foo.js": "", - "bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintignore": "!foo.js", + "foo.js": "", + "bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), false); }); it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), true); }); it("'lintFiles()' should verify unignored 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4889,28 +5045,30 @@ describe("ESLint", () => { }); describe("ignorePatterns in the config file in a child directory affects to only in the directory.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "foo.js" - }), - "subdir/.eslintrc.json": JSON.stringify({ - ignorePatterns: "bar.js" - }), - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "", - "subdir/subsubdir/foo.js": "", - "subdir/subsubdir/bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "bar.js" + }), + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/subsubdir/foo.js": "", + "subdir/subsubdir/bar.js": "" + } }); + + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); @@ -4918,20 +5076,20 @@ describe("ESLint", () => { }); it("'isPathIgnored()' should return 'true' for 'bar.js' in 'subdir'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); assert.strictEqual(await engine.isPathIgnored("subdir/subsubdir/bar.js"), true); }); it("'isPathIgnored()' should return 'false' for 'bar.js' in the outside of 'subdir'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), false); }); it("'lintFiles()' should verify 'bar.js' in the outside of 'subdir'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4943,36 +5101,37 @@ describe("ESLint", () => { }); describe("ignorePatterns in the config file in a child directory can unignore the ignored files in the parent directory's config.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "foo.js" - }), - "subdir/.eslintrc.json": JSON.stringify({ - ignorePatterns: "!foo.js" - }), - "foo.js": "", - "subdir/foo.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "!foo.js" + }), + "foo.js": "", + "subdir/foo.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); }); it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); }); it("'lintFiles()' should verify 'foo.js' in the child directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -4984,37 +5143,38 @@ describe("ESLint", () => { }); describe(".eslintignore can unignore files that are ignored by ignorePatterns in the config file in the child directory.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({}), - "subdir/.eslintrc.json": JSON.stringify({ - ignorePatterns: "*.js" - }), - ".eslintignore": "!foo.js", - "foo.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({}), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "*.js" + }), + ".eslintignore": "!foo.js", + "foo.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), false); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); }); it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); }); it("'lintFiles()' should verify unignored 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5027,51 +5187,52 @@ describe("ESLint", () => { }); describe("if the config in a child directory has 'root:true', ignorePatterns in the config file in the parent directory should not be used.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "foo.js" - }), - "subdir/.eslintrc.json": JSON.stringify({ - root: true, - ignorePatterns: "bar.js" - }), - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "subdir/.eslintrc.json": JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + }), + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); }); it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), false); }); it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); }); it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); }); it("'lintFiles()' should verify 'bar.js' in the root directory and 'foo.js' in the child directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5084,45 +5245,46 @@ describe("ESLint", () => { }); describe("even if the config in a child directory has 'root:true', .eslintignore should be used.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({}), - "subdir/.eslintrc.json": JSON.stringify({ - root: true, - ignorePatterns: "bar.js" - }), - ".eslintignore": "foo.js", - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({}), + "subdir/.eslintrc.json": JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + }), + ".eslintignore": "foo.js", + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); }); it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), false); }); it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); }); it("'lintFiles()' should verify 'bar.js' in the root directory.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5134,36 +5296,37 @@ describe("ESLint", () => { }); describe("ignorePatterns in the shareable config should be used.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "foo.js" - })}`, - ".eslintrc.json": JSON.stringify({ - extends: "one" - }), - "foo.js": "", - "bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "one" + }), + "foo.js": "", + "bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); }); it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), false); }); it("'lintFiles()' should verify 'bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5175,36 +5338,38 @@ describe("ESLint", () => { }); describe("ignorePatterns in the shareable config should be relative to the entry config file.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "/foo.js" - })}`, - ".eslintrc.json": JSON.stringify({ - extends: "one" - }), - "foo.js": "", - "subdir/foo.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "/foo.js" + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "one" + }), + "foo.js": "", + "subdir/foo.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); }); it("'isPathIgnored()' should return 'false' for 'subdir/foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); }); it("'lintFiles()' should verify 'subdir/foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5216,37 +5381,38 @@ describe("ESLint", () => { }); describe("ignorePatterns in a config file can unignore the files which are ignored by ignorePatterns in the shareable config.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "*.js" - })}`, - ".eslintrc.json": JSON.stringify({ - extends: "one", - ignorePatterns: "!bar.js" - }), - "foo.js": "", - "bar.js": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "one", + ignorePatterns: "!bar.js" + }), + "foo.js": "", + "bar.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("foo.js"), true); }); it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); assert.strictEqual(await engine.isPathIgnored("bar.js"), false); }); it("'lintFiles()' should verify 'bar.js'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5258,26 +5424,28 @@ describe("ESLint", () => { }); describe("ignorePatterns in a config file should not be used if --no-ignore option was given.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "*.js" - }), - "foo.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "*.js" + }), + "foo.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'isPathIgnored()' should return 'false' for 'foo.js'.", async () => { - const engine = new InMemoryESLint({ ignore: false }); + const engine = new ESLint({ cwd: getPath(), ignore: false }); assert.strictEqual(await engine.isPathIgnored("foo.js"), false); }); it("'lintFiles()' should verify 'foo.js'.", async () => { - const engine = new InMemoryESLint({ ignore: false }); + const engine = new ESLint({ cwd: getPath(), ignore: false }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5289,26 +5457,28 @@ describe("ESLint", () => { }); describe("ignorePatterns in overrides section is not allowed.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - overrides: [ - { - files: "*.js", - ignorePatterns: "foo.js" - } - ] - })}`, - "foo.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + overrides: [ + { + files: "*.js", + ignorePatterns: "foo.js" + } + ] + })}`, + "foo.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("should throw a configuration error.", async () => { await assert.rejects(async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); await engine.lintFiles("*.js"); }, /Unexpected top-level property "overrides\[0\]\.ignorePatterns"/u); @@ -5318,37 +5488,38 @@ describe("ESLint", () => { describe("'overrides[].files' adds lint targets", () => { const root = getFixturePath("cli-engine/additional-lint-targets"); - let InMemoryESLint; + describe("if { files: 'foo/*.txt', excludedFiles: '**/ignore.txt' } is present,", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - overrides: [ - { - files: "foo/*.txt", - excludedFiles: "**/ignore.txt" - } - ] - }), - "foo/nested/test.txt": "", - "foo/test.js": "", - "foo/test.txt": "", - "foo/ignore.txt": "", - "bar/test.js": "", - "bar/test.txt": "", - "bar/ignore.txt": "", - "test.js": "", - "test.txt": "", - "ignore.txt": "" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/*.txt", + excludedFiles: "**/ignore.txt" + } + ] + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "foo/ignore.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "bar/ignore.txt": "", + "test.js": "", + "test.txt": "", + "ignore.txt": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with a directory path should contain 'foo/test.txt'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles(".")) .map(r => r.filePath) .sort(); @@ -5362,7 +5533,7 @@ describe("ESLint", () => { }); it("'lintFiles()' with a glob pattern '*.js' should not contain 'foo/test.txt'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles("**/*.js")) .map(r => r.filePath) .sort(); @@ -5376,30 +5547,32 @@ describe("ESLint", () => { }); describe("if { files: 'foo/**/*.txt' } is present,", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - overrides: [ - { - files: "foo/**/*.txt" - } - ] - }), - "foo/nested/test.txt": "", - "foo/test.js": "", - "foo/test.txt": "", - "bar/test.js": "", - "bar/test.txt": "", - "test.js": "", - "test.txt": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/**/*.txt" + } + ] + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles(".")) .map(r => r.filePath) .sort(); @@ -5415,30 +5588,32 @@ describe("ESLint", () => { }); describe("if { files: 'foo/**/*' } is present,", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - overrides: [ - { - files: "foo/**/*" - } - ] - }), - "foo/nested/test.txt": "", - "foo/test.js": "", - "foo/test.txt": "", - "bar/test.js": "", - "bar/test.txt": "", - "test.js": "", - "test.txt": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + ".eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/**/*" + } + ] + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with a directory path should NOT contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles(".")) .map(r => r.filePath) .sort(); @@ -5452,33 +5627,35 @@ describe("ESLint", () => { }); describe("if { files: 'foo/**/*.txt' } is present in a shareable config,", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-foo/index.js": `module.exports = ${JSON.stringify({ - overrides: [ - { - files: "foo/**/*.txt" - } - ] - })}`, - ".eslintrc.json": JSON.stringify({ - extends: "foo" - }), - "foo/nested/test.txt": "", - "foo/test.js": "", - "foo/test.txt": "", - "bar/test.js": "", - "bar/test.txt": "", - "test.js": "", - "test.txt": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/eslint-config-foo/index.js": `module.exports = ${JSON.stringify({ + overrides: [ + { + files: "foo/**/*.txt" + } + ] + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "foo" + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles(".")) .map(r => r.filePath) .sort(); @@ -5494,35 +5671,37 @@ describe("ESLint", () => { }); describe("if { files: 'foo/**/*.txt' } is present in a plugin config,", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": `exports.configs = ${JSON.stringify({ - bar: { - overrides: [ - { - files: "foo/**/*.txt" - } - ] - } - })}`, - ".eslintrc.json": JSON.stringify({ - extends: "plugin:foo/bar" - }), - "foo/nested/test.txt": "", - "foo/test.js": "", - "foo/test.txt": "", - "bar/test.js": "", - "bar/test.txt": "", - "test.js": "", - "test.txt": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/eslint-plugin-foo/index.js": `exports.configs = ${JSON.stringify({ + bar: { + overrides: [ + { + files: "foo/**/*.txt" + } + ] + } + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "plugin:foo/bar" + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { - const engine = new InMemoryESLint(); + const engine = new ESLint({ cwd: getPath() }); const filePaths = (await engine.lintFiles(".")) .map(r => r.filePath) .sort(); @@ -5541,34 +5720,32 @@ describe("ESLint", () => { describe("'ignorePatterns', 'overrides[].files', and 'overrides[].excludedFiles' of the configuration that the '--config' option provided should be resolved from CWD.", () => { const root = getFixturePath("cli-engine/config-and-overrides-files"); - /** @type {ESLint} */ - let InMemoryESLint; - describe("if { files: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/myconf/.eslintrc.json": JSON.stringify({ - overrides: [ - { - files: "foo/*.js", - rules: { - eqeqeq: "error" - } + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/myconf/.eslintrc.json": { + overrides: [ + { + files: "foo/*.js", + rules: { + eqeqeq: "error" } - ] - }), - "node_modules/myconf/foo/test.js": "a == b", - "foo/test.js": "a == b" - } - }).ESLint; + } + ] + }, + "node_modules/myconf/foo/test.js": "a == b", + "foo/test.js": "a == b" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with 'foo/test.js' should use the override entry.", async () => { - const engine = new InMemoryESLint({ + const engine = new ESLint({ overrideConfigFile: "node_modules/myconf/.eslintrc.json", - cwd: root, + cwd: getPath(), ignore: false, useEslintrc: false }); @@ -5578,7 +5755,7 @@ describe("ESLint", () => { assert.deepStrictEqual(results, [ { errorCount: 1, - filePath: path.join(root, "foo/test.js"), + filePath: path.join(getPath(), "foo/test.js"), fixableErrorCount: 0, fixableWarningCount: 0, messages: [ @@ -5602,7 +5779,7 @@ describe("ESLint", () => { }); it("'lintFiles()' with 'node_modules/myconf/foo/test.js' should NOT use the override entry.", async () => { - const engine = new InMemoryESLint({ + const engine = new ESLint({ overrideConfigFile: "node_modules/myconf/.eslintrc.json", cwd: root, ignore: false, @@ -5614,7 +5791,7 @@ describe("ESLint", () => { assert.deepStrictEqual(results, [ { errorCount: 0, - filePath: path.join(root, "node_modules/myconf/foo/test.js"), + filePath: path.join(getPath(), "node_modules/myconf/foo/test.js"), fixableErrorCount: 0, fixableWarningCount: 0, messages: [], @@ -5626,29 +5803,30 @@ describe("ESLint", () => { }); describe("if { files: '*', excludedFiles: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/myconf/.eslintrc.json": JSON.stringify({ - overrides: [ - { - files: "*", - excludedFiles: "foo/*.js", - rules: { - eqeqeq: "error" - } + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/myconf/.eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "*", + excludedFiles: "foo/*.js", + rules: { + eqeqeq: "error" } - ] - }), - "node_modules/myconf/foo/test.js": "a == b", - "foo/test.js": "a == b" - } - }).ESLint; + } + ] + }), + "node_modules/myconf/foo/test.js": "a == b", + "foo/test.js": "a == b" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with 'foo/test.js' should NOT use the override entry.", async () => { - const engine = new InMemoryESLint({ + const engine = new ESLint({ overrideConfigFile: "node_modules/myconf/.eslintrc.json", cwd: root, ignore: false, @@ -5660,7 +5838,7 @@ describe("ESLint", () => { assert.deepStrictEqual(results, [ { errorCount: 0, - filePath: path.join(root, "foo/test.js"), + filePath: path.join(getPath(), "foo/test.js"), fixableErrorCount: 0, fixableWarningCount: 0, messages: [], @@ -5671,7 +5849,7 @@ describe("ESLint", () => { }); it("'lintFiles()' with 'node_modules/myconf/foo/test.js' should use the override entry.", async () => { - const engine = new InMemoryESLint({ + const engine = new ESLint({ overrideConfigFile: "node_modules/myconf/.eslintrc.json", cwd: root, ignore: false, @@ -5683,7 +5861,7 @@ describe("ESLint", () => { assert.deepStrictEqual(results, [ { errorCount: 1, - filePath: path.join(root, "node_modules/myconf/foo/test.js"), + filePath: path.join(getPath(), "node_modules/myconf/foo/test.js"), fixableErrorCount: 0, fixableWarningCount: 0, messages: [ @@ -5708,26 +5886,27 @@ describe("ESLint", () => { }); describe("if { ignorePatterns: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/myconf/.eslintrc.json": JSON.stringify({ - ignorePatterns: ["!/node_modules/myconf", "foo/*.js"], - rules: { - eqeqeq: "error" - } - }), - "node_modules/myconf/foo/test.js": "a == b", - "foo/test.js": "a == b" - } - }).ESLint; + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: root, + files: { + "node_modules/myconf/.eslintrc.json": JSON.stringify({ + ignorePatterns: ["!/node_modules/myconf", "foo/*.js"], + rules: { + eqeqeq: "error" + } + }), + "node_modules/myconf/foo/test.js": "a == b", + "foo/test.js": "a == b" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' with '**/*.js' should iterate 'node_modules/myconf/foo/test.js' but not 'foo/test.js'.", async () => { - const engine = new InMemoryESLint({ + const engine = new ESLint({ overrideConfigFile: "node_modules/myconf/.eslintrc.json", - cwd: root, + cwd: getPath(), useEslintrc: false }); const files = (await engine.lintFiles("**/*.js")) @@ -5743,14 +5922,7 @@ describe("ESLint", () => { describe("plugin conflicts", () => { let uid = 0; - let root = ""; - - beforeEach(() => { - root = getFixturePath(`eslint/plugin-conflicts-${++uid}`); - }); - - /** @type {typeof ESLint} */ - let InMemoryESLint; + const root = getFixturePath("cli-engine/plugin-conflicts-"); /** * Verify thrown errors. @@ -5772,110 +5944,118 @@ describe("ESLint", () => { } describe("between a config file and linear extendees.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - extends: ["two"], - plugins: ["foo"] - })}`, - "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", - "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ - plugins: ["foo"] - })}`, - ".eslintrc.json": JSON.stringify({ - extends: ["one"], - plugins: ["foo"] - }), - "test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + extends: ["two"], + plugins: ["foo"] + })}`, + "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ + plugins: ["foo"] + })}`, + ".eslintrc.json": JSON.stringify({ + extends: ["one"], + plugins: ["foo"] + }), + "test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file.)", async () => { - const engine = new InMemoryESLint({ cwd: root }); + const engine = new ESLint({ cwd: getPath() }); await engine.lintFiles("test.js"); }); }); describe("between a config file and same-depth extendees.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - plugins: ["foo"] - })}`, - "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", - "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ - plugins: ["foo"] - })}`, - ".eslintrc.json": JSON.stringify({ - extends: ["one", "two"], - plugins: ["foo"] - }), - "test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + plugins: ["foo"] + })}`, + "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ + plugins: ["foo"] + })}`, + ".eslintrc.json": JSON.stringify({ + extends: ["one", "two"], + plugins: ["foo"] + }), + "test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file.)", async () => { - const engine = new InMemoryESLint({ cwd: root }); + const engine = new ESLint({ cwd: getPath() }); await engine.lintFiles("test.js"); }); }); describe("between two config files in different directories, with single node_modules.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - ".eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files, but node_modules directory is unique.)", async () => { - const engine = new InMemoryESLint({ cwd: root }); + const engine = new ESLint({ cwd: getPath() }); await engine.lintFiles("subdir/test.js"); }); }); describe("between two config files in different directories, with multiple node_modules.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - ".eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/node_modules/eslint-plugin-foo/index.js": "", - "subdir/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files.)", async () => { - const engine = new InMemoryESLint({ cwd: root }); + const engine = new ESLint({ cwd: getPath() }); await assertThrows( () => engine.lintFiles("subdir/test.js"), @@ -5886,11 +6066,11 @@ describe("ESLint", () => { pluginId: "foo", plugins: [ { - filePath: path.join(root, "subdir/node_modules/eslint-plugin-foo/index.js"), + filePath: path.join(getPath(), "subdir/node_modules/eslint-plugin-foo/index.js"), importerName: `subdir${path.sep}.eslintrc.json` }, { - filePath: path.join(root, "node_modules/eslint-plugin-foo/index.js"), + filePath: path.join(getPath(), "node_modules/eslint-plugin-foo/index.js"), importerName: ".eslintrc.json" } ] @@ -5901,25 +6081,27 @@ describe("ESLint", () => { }); describe("between '--config' option and a regular config file, with single node_modules.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - "node_modules/mine/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - ".eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/mine/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files, but node_modules directory is unique.)", async () => { - const engine = new InMemoryESLint({ - cwd: root, + const engine = new ESLint({ + cwd: getPath(), overrideConfigFile: "node_modules/mine/.eslintrc.json" }); @@ -5928,26 +6110,28 @@ describe("ESLint", () => { }); describe("between '--config' option and a regular config file, with multiple node_modules.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - "node_modules/mine/node_modules/eslint-plugin-foo/index.js": "", - "node_modules/mine/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - ".eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/mine/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/mine/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files.)", async () => { - const engine = new InMemoryESLint({ - cwd: root, + const engine = new ESLint({ + cwd: getPath(), overrideConfigFile: "node_modules/mine/.eslintrc.json" }); @@ -5960,11 +6144,11 @@ describe("ESLint", () => { pluginId: "foo", plugins: [ { - filePath: path.join(root, "node_modules/mine/node_modules/eslint-plugin-foo/index.js"), + filePath: path.join(getPath(), "node_modules/mine/node_modules/eslint-plugin-foo/index.js"), importerName: "--config" }, { - filePath: path.join(root, "node_modules/eslint-plugin-foo/index.js"), + filePath: path.join(getPath(), "node_modules/eslint-plugin-foo/index.js"), importerName: ".eslintrc.json" } ] @@ -5975,22 +6159,25 @@ describe("ESLint", () => { }); describe("between '--plugin' option and a regular config file, with single node_modules.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - "subdir/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } }); + + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from both CWD and the base directory of the entry config file, but node_modules directory is unique.)", async () => { - const engine = new InMemoryESLint({ - cwd: root, + const engine = new ESLint({ + cwd: getPath(), overrideConfig: { plugins: ["foo"] } }); @@ -5999,23 +6186,25 @@ describe("ESLint", () => { }); describe("between '--plugin' option and a regular config file, with multiple node_modules.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - "subdir/node_modules/eslint-plugin-foo/index.js": "", - "subdir/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "subdir/node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from both CWD and the base directory of the entry config file.)", async () => { - const engine = new InMemoryESLint({ - cwd: root, + const engine = new ESLint({ + cwd: getPath(), overrideConfig: { plugins: ["foo"] } }); @@ -6028,11 +6217,11 @@ describe("ESLint", () => { pluginId: "foo", plugins: [ { - filePath: path.join(root, "node_modules/eslint-plugin-foo/index.js"), + filePath: path.join(getPath(), "node_modules/eslint-plugin-foo/index.js"), importerName: "CLIOptions" }, { - filePath: path.join(root, "subdir/node_modules/eslint-plugin-foo/index.js"), + filePath: path.join(getPath(), "subdir/node_modules/eslint-plugin-foo/index.js"), importerName: `subdir${path.sep}.eslintrc.json` } ] @@ -6043,27 +6232,29 @@ describe("ESLint", () => { }); describe("'--resolve-plugins-relative-to' option overrides the location that ESLint load plugins from.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-plugin-foo/index.js": "", - ".eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/node_modules/eslint-plugin-foo/index.js": "", - "subdir/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "subdir/test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from '--resolve-plugins-relative-to'.)", async () => { - const engine = new InMemoryESLint({ - cwd: root, - resolvePluginsRelativeTo: root + const engine = new ESLint({ + cwd: getPath(), + resolvePluginsRelativeTo: getPath() }); await engine.lintFiles("subdir/test.js"); @@ -6071,26 +6262,28 @@ describe("ESLint", () => { }); describe("between two config files with different target files.", () => { - beforeEach(() => { - InMemoryESLint = defineESLintWithInMemoryFileSystem({ - cwd: () => root, - files: { - "one/node_modules/eslint-plugin-foo/index.js": "", - "one/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "one/test.js": "", - "two/node_modules/eslint-plugin-foo/index.js": "", - "two/.eslintrc.json": JSON.stringify({ - plugins: ["foo"] - }), - "two/test.js": "" - } - }).ESLint; + + const { prepare, cleanup, getPath } = createCustomTeardown({ + cwd: `${root}${++uid}`, + files: { + "one/node_modules/eslint-plugin-foo/index.js": "", + "one/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "one/test.js": "", + "two/node_modules/eslint-plugin-foo/index.js": "", + "two/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "two/test.js": "" + } }); + beforeEach(prepare); + afterEach(cleanup); + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file for each target file. Not related to each other.)", async () => { - const engine = new InMemoryESLint({ cwd: root }); + const engine = new ESLint({ cwd: getPath() }); const results = await engine.lintFiles("*/test.js"); assert.strictEqual(results.length, 2);