From 48b122f450b14dd27afef4c8115c69fca4f02be1 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Mon, 23 Mar 2020 12:26:26 +0900 Subject: [PATCH] Breaking: change relative paths with --config (refs eslint/rfcs#37) (#12887) --- docs/user-guide/configuring.md | 8 +- .../cascading-config-array-factory.js | 4 +- lib/cli-engine/config-array-factory.js | 439 +++++++++--------- lib/cli-engine/config-array/config-array.js | 1 + lib/shared/types.js | 4 +- tests/lib/cli-engine/cli-engine.js | 218 ++++++++- tests/lib/cli-engine/config-array-factory.js | 139 ++---- 7 files changed, 468 insertions(+), 345 deletions(-) diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md index 13f723fa380..414d0e02524 100644 --- a/docs/user-guide/configuring.md +++ b/docs/user-guide/configuring.md @@ -968,6 +968,8 @@ project-root The config in `app/.eslintrc.json` defines the glob pattern `**/*Spec.js`. This pattern is relative to the base directory of `app/.eslintrc.json`. So, this pattern would match `app/lib/fooSpec.js` and `app/components/barSpec.js` but **NOT** `server/serverSpec.js`. If you defined the same pattern in the `.eslintrc.json` file within in the `project-root` folder, it would match all three of the `*Spec` files. +If a config is provided via the `--config` CLI option, the glob patterns in the config are relative to the current working directory rather than the base directory of the given config. For example, if `--config configs/.eslintrc.json` is present, the glob patterns in the config are relative to `.` rather than `./configs`. + ### Example configuration In your `.eslintrc.json`: @@ -1032,6 +1034,10 @@ You can tell ESLint to ignore specific files and directories by `ignorePatterns` * You cannot write `ignorePatterns` property under `overrides` property. * `.eslintignore` can override `ignorePatterns` property of config files. +If a glob pattern starts with `/`, the pattern is relative to the base directory of the config file. For example, `/foo.js` in `lib/.eslintrc.json` matches to `lib/foo.js` but not `lib/subdir/foo.js`. + +If a config is provided via the `--config` CLI option, the ignore patterns that start with `/` in the config are relative to the current working directory rather than the base directory of the given config. For example, if `--config configs/.eslintrc.json` is present, the ignore patterns in the config are relative to `.` rather than `./configs`. + ### `.eslintignore` You can tell ESLint to ignore specific files and directories by creating an `.eslintignore` file in your project's root directory. The `.eslintignore` file is a plain text file where each line is a glob pattern indicating which paths should be omitted from linting. For example, the following will omit all JavaScript files: @@ -1045,7 +1051,7 @@ When ESLint is run, it looks in the current working directory to find an `.eslin Globs are matched using [node-ignore](https://github.com/kaelzhang/node-ignore), so a number of features are available: * Lines beginning with `#` are treated as comments and do not affect ignore patterns. -* Paths are relative to `.eslintignore` location or the current working directory. This is also true of paths passed in via the `--ignore-pattern` [command](./command-line-interface.md#--ignore-pattern). +* Paths are relative to the current working directory. This is also true of paths passed in via the `--ignore-pattern` [command](./command-line-interface.md#--ignore-pattern). * Lines preceded by `!` are negated patterns that re-include a pattern that was ignored by an earlier pattern. * Ignore patterns behave according to the `.gitignore` [specification](https://git-scm.com/docs/gitignore). diff --git a/lib/cli-engine/cascading-config-array-factory.js b/lib/cli-engine/cascading-config-array-factory.js index f91dea4c448..4c362d9000a 100644 --- a/lib/cli-engine/cascading-config-array-factory.js +++ b/lib/cli-engine/cascading-config-array-factory.js @@ -140,6 +140,7 @@ function createBaseConfigArray({ function createCLIConfigArray({ cliConfigData, configArrayFactory, + cwd, ignorePath, specificConfigPath }) { @@ -158,7 +159,7 @@ function createCLIConfigArray({ cliConfigArray.unshift( ...configArrayFactory.loadFile( specificConfigPath, - { name: "--config" } + { name: "--config", basePath: cwd } ) ); } @@ -220,6 +221,7 @@ class CascadingConfigArrayFactory { cliConfigArray: createCLIConfigArray({ cliConfigData, configArrayFactory, + cwd, ignorePath, specificConfigPath }), diff --git a/lib/cli-engine/config-array-factory.js b/lib/cli-engine/config-array-factory.js index 997a7e15318..c5816bf445e 100644 --- a/lib/cli-engine/config-array-factory.js +++ b/lib/cli-engine/config-array-factory.js @@ -93,6 +93,14 @@ const configFilenames = [ * @property {string} 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 {"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(); @@ -328,21 +336,35 @@ function writeDebugLogForLoading(request, relativeTo, filePath) { } /** - * Concatenate two config data. - * @param {IterableIterator|null} elements The config elements. - * @param {ConfigArray|null} parentConfigArray The parent config array. - * @returns {ConfigArray} The concatenated config array. + * Create a new context with default values. + * @param {string | undefined} cwd The current working directory. + * @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 createConfigArray(elements, parentConfigArray) { - if (!elements) { - return parentConfigArray || new ConfigArray(); - } - const configArray = new ConfigArray(...elements); - - if (parentConfigArray && !configArray.isRoot()) { - configArray.unshift(...parentConfigArray); - } - return configArray; +function createContext( + cwd, + providedType, + providedName, + providedFilePath, + providedMatchBasePath +) { + const filePath = providedFilePath + ? path.resolve(cwd, providedFilePath) + : ""; + const matchBasePath = + providedMatchBasePath || + (filePath && path.dirname(filePath)) || + cwd; + const name = + providedName || + (filePath && path.relative(cwd, filePath)) || + ""; + const type = providedType || "config"; + + return { filePath, matchBasePath, name, type }; } /** @@ -379,61 +401,91 @@ class ConfigArrayFactory { cwd = process.cwd(), resolvePluginsRelativeTo = cwd } = {}) { - internalSlotsMap.set(this, { additionalPluginPool, cwd, resolvePluginsRelativeTo: path.resolve(cwd, resolvePluginsRelativeTo) }); + internalSlotsMap.set(this, { + additionalPluginPool, + cwd, + 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. - * @param {ConfigArray} [options.parent] The parent config array. * @returns {ConfigArray} Loaded config. */ - create(configData, { filePath, name, parent } = {}) { - return createConfigArray( - configData - ? this._normalizeConfigData(configData, filePath, name) - : null, - parent - ); + create(configData, { basePath, filePath, name } = {}) { + if (!configData) { + return new ConfigArray(); + } + + const { cwd } = internalSlotsMap.get(this); + const ctx = createContext(cwd, "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. - * @param {ConfigArray} [options.parent] The parent config array. * @returns {ConfigArray} Loaded config. */ - loadFile(filePath, { name, parent } = {}) { + loadFile(filePath, { basePath, name } = {}) { const { cwd } = internalSlotsMap.get(this); - const absolutePath = path.resolve(cwd, filePath); + const ctx = createContext(cwd, "config", name, filePath, basePath); - return createConfigArray( - this._loadConfigData(absolutePath, name), - parent - ); + 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. - * @param {ConfigArray} [options.parent] The parent config array. * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. */ - loadInDirectory(directoryPath, { name, parent } = {}) { + loadInDirectory(directoryPath, { basePath, name } = {}) { const { cwd } = internalSlotsMap.get(this); - const absolutePath = path.resolve(cwd, directoryPath); - return createConfigArray( - this._loadConfigDataInDirectory(absolutePath, name), - parent - ); + for (const filename of configFilenames) { + const ctx = createContext( + cwd, + "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(); } /** @@ -467,11 +519,17 @@ class ConfigArrayFactory { loadESLintIgnore(filePath) { const { cwd } = internalSlotsMap.get(this); const absolutePath = path.resolve(cwd, filePath); - const name = path.relative(cwd, absolutePath); const ignorePatterns = loadESLintIgnoreFile(absolutePath); + const ctx = createContext( + cwd, + "ignore", + void 0, + absolutePath, + cwd + ); - return createConfigArray( - this._normalizeESLintIgnoreData(ignorePatterns, absolutePath, name) + return new ConfigArray( + ...this._normalizeESLintIgnoreData(ignorePatterns, ctx) ); } @@ -494,12 +552,16 @@ class ConfigArrayFactory { if (!Array.isArray(data.eslintIgnore)) { throw new Error("Package.json eslintIgnore property requires an array of paths"); } - return createConfigArray( - this._normalizeESLintIgnoreData( - data.eslintIgnore, - packageJsonPath, - "eslintIgnore in package.json" - ) + const ctx = createContext( + cwd, + "ignore", + "eslintIgnore in package.json", + packageJsonPath, + cwd + ); + + return new ConfigArray( + ...this._normalizeESLintIgnoreData(data.eslintIgnore, ctx) ); } } @@ -509,65 +571,25 @@ class ConfigArrayFactory { /** * Load a given config file. - * @param {string} filePath The path to a config file. - * @param {string} name The config name. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} Loaded config. * @private */ - _loadConfigData(filePath, name) { - return this._normalizeConfigData( - loadConfigFile(filePath), - filePath, - name - ); - } - - /** - * Load the config file in a given directory if exists. - * @param {string} directoryPath The path to a directory. - * @param {string} name The config name. - * @returns {IterableIterator | null} Loaded config. `null` if any config doesn't exist. - * @private - */ - _loadConfigDataInDirectory(directoryPath, name) { - for (const filename of configFilenames) { - const filePath = path.join(directoryPath, filename); - - if (fs.existsSync(filePath)) { - let configData; - - try { - configData = loadConfigFile(filePath); - } catch (error) { - if (!error || error.code !== "ESLINT_CONFIG_FIELD_NOT_FOUND") { - throw error; - } - } - - if (configData) { - debug(`Config file found: ${filePath}`); - return this._normalizeConfigData(configData, filePath, name); - } - } - } - - debug(`Config file not found on ${directoryPath}`); - return null; + _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 {string|undefined} filePath The file path of this config. - * @param {string|undefined} name The name of this config. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ - *_normalizeESLintIgnoreData(ignorePatterns, filePath, name) { + *_normalizeESLintIgnoreData(ignorePatterns, ctx) { const elements = this._normalizeObjectConfigData( - { type: "ignore", ignorePatterns }, - filePath, - name + { ignorePatterns }, + ctx ); // Set `ignorePattern.loose` flag for backward compatibility. @@ -582,53 +604,38 @@ class ConfigArrayFactory { /** * Normalize a given config to an array. * @param {ConfigData} configData The config data to normalize. - * @param {string|undefined} providedFilePath The file path of this config. - * @param {string|undefined} providedName The name of this config. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ - _normalizeConfigData(configData, providedFilePath, providedName) { - const { cwd } = internalSlotsMap.get(this); - const filePath = providedFilePath - ? path.resolve(cwd, providedFilePath) - : ""; - const name = providedName || (filePath && path.relative(cwd, filePath)); - - validateConfigSchema(configData, name || filePath); - - return this._normalizeObjectConfigData(configData, filePath, name); + _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 {string} filePath The file path of this config. - * @param {string} name The name of this config. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ - *_normalizeObjectConfigData(configData, filePath, name) { - const { cwd } = internalSlotsMap.get(this); + *_normalizeObjectConfigData(configData, ctx) { const { files, excludedFiles, ...configBody } = configData; - const basePath = filePath ? path.dirname(filePath) : cwd; - const criteria = OverrideTester.create(files, excludedFiles, basePath); - const elements = - this._normalizeObjectConfigDataBody(configBody, filePath, name); + 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) { - // Adopt the base path of the entry file (the outermost base path). - if (element.criteria) { - element.criteria.basePath = basePath; - } - if (element.ignorePattern) { - element.ignorePattern.basePath = basePath; - } - /* - * Merge the criteria; this is for only file extension processors in - * `overrides` section for now. + * Merge the criteria. + * This is for the `overrides` entries that came from the + * configurations of `overrides[].extends`. */ element.criteria = OverrideTester.and(criteria, element.criteria); @@ -647,8 +654,7 @@ class ConfigArrayFactory { /** * Normalize a given config to an array. * @param {ConfigData} configData The config data to normalize. - * @param {string} filePath The file path of this config. - * @param {string} name The name of this config. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ @@ -667,41 +673,37 @@ class ConfigArrayFactory { root, rules, settings, - type = "config", overrides: overrideList = [] }, - filePath, - name + ctx ) { const extendList = Array.isArray(extend) ? extend : [extend]; const ignorePattern = ignorePatterns && new IgnorePattern( Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns], - filePath ? path.dirname(filePath) : internalSlotsMap.get(this).cwd + ctx.matchBasePath ); // Flatten `extends`. for (const extendName of extendList.filter(Boolean)) { - yield* this._loadExtends(extendName, filePath, name); + yield* this._loadExtends(extendName, ctx); } // Load parser & plugins. - const parser = - parserName && this._loadParser(parserName, filePath, name); - const plugins = - pluginList && this._loadPlugins(pluginList, filePath, name); + 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, filePath, name); + yield* this._takeFileExtensionProcessors(plugins, ctx); } // Yield the config data except `extends` and `overrides`. yield { // Debug information. - type, - name, - filePath, + type: ctx.type, + name: ctx.name, + filePath: ctx.filePath, // Config data. criteria: null, @@ -723,8 +725,7 @@ class ConfigArrayFactory { for (let i = 0; i < overrideList.length; ++i) { yield* this._normalizeObjectConfigData( overrideList[i], - filePath, - `${name}#overrides[${i}]` + { ...ctx, name: `${ctx.name}#overrides[${i}]` } ); } } @@ -732,34 +733,22 @@ class ConfigArrayFactory { /** * Load configs of an element in `extends`. * @param {string} extendName The name of a base config. - * @param {string} importerPath The file path which has the `extends` property. - * @param {string} importerName The name of the config which has the `extends` property. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ - _loadExtends(extendName, importerPath, importerName) { - debug("Loading {extends:%j} relative to %s", extendName, importerPath); + _loadExtends(extendName, ctx) { + debug("Loading {extends:%j} relative to %s", extendName, ctx.filePath); try { if (extendName.startsWith("eslint:")) { - return this._loadExtendedBuiltInConfig( - extendName, - importerName - ); + return this._loadExtendedBuiltInConfig(extendName, ctx); } if (extendName.startsWith("plugin:")) { - return this._loadExtendedPluginConfig( - extendName, - importerPath, - importerName - ); + return this._loadExtendedPluginConfig(extendName, ctx); } - return this._loadExtendedShareableConfig( - extendName, - importerPath, - importerName - ); + return this._loadExtendedShareableConfig(extendName, ctx); } catch (error) { - error.message += `\nReferenced from: ${importerPath || importerName}`; + error.message += `\nReferenced from: ${ctx.filePath || ctx.name}`; throw error; } } @@ -767,32 +756,37 @@ class ConfigArrayFactory { /** * Load configs of an element in `extends`. * @param {string} extendName The name of a base config. - * @param {string} importerName The name of the config which has the `extends` property. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ - _loadExtendedBuiltInConfig(extendName, importerName) { - const name = `${importerName} » ${extendName}`; - + _loadExtendedBuiltInConfig(extendName, ctx) { if (extendName === "eslint:recommended") { - return this._loadConfigData(eslintRecommendedPath, name); + return this._loadConfigData({ + ...ctx, + filePath: eslintRecommendedPath, + name: `${ctx.name} » ${extendName}` + }); } if (extendName === "eslint:all") { - return this._loadConfigData(eslintAllPath, name); + return this._loadConfigData({ + ...ctx, + filePath: eslintAllPath, + name: `${ctx.name} » ${extendName}` + }); } - throw configMissingError(extendName, importerName); + throw configMissingError(extendName, ctx.name); } /** * Load configs of an element in `extends`. * @param {string} extendName The name of a base config. - * @param {string} importerPath The file path which has the `extends` property. - * @param {string} importerName The name of the config which has the `extends` property. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ - _loadExtendedPluginConfig(extendName, importerPath, importerName) { + _loadExtendedPluginConfig(extendName, ctx) { const slashIndex = extendName.lastIndexOf("/"); const pluginName = extendName.slice("plugin:".length, slashIndex); const configName = extendName.slice(slashIndex + 1); @@ -801,33 +795,32 @@ class ConfigArrayFactory { throw new Error("'extends' cannot use a file path for plugins."); } - const plugin = this._loadPlugin(pluginName, importerPath, importerName); + const plugin = this._loadPlugin(pluginName, ctx); const configData = plugin.definition && plugin.definition.configs[configName]; if (configData) { - return this._normalizeConfigData( - configData, - plugin.filePath, - `${importerName} » plugin:${plugin.id}/${configName}` - ); + return this._normalizeConfigData(configData, { + ...ctx, + filePath: plugin.filePath, + name: `${ctx.name} » plugin:${plugin.id}/${configName}` + }); } - throw plugin.error || configMissingError(extendName, importerPath); + throw plugin.error || configMissingError(extendName, ctx.filePath); } /** * Load configs of an element in `extends`. * @param {string} extendName The name of a base config. - * @param {string} importerPath The file path which has the `extends` property. - * @param {string} importerName The name of the config which has the `extends` property. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The normalized config. * @private */ - _loadExtendedShareableConfig(extendName, importerPath, importerName) { + _loadExtendedShareableConfig(extendName, ctx) { const { cwd } = internalSlotsMap.get(this); - const relativeTo = importerPath || path.join(cwd, "__placeholder__.js"); + const relativeTo = ctx.filePath || path.join(cwd, "__placeholder__.js"); let request; if (isFilePath(extendName)) { @@ -848,29 +841,32 @@ class ConfigArrayFactory { } catch (error) { /* istanbul ignore else */ if (error && error.code === "MODULE_NOT_FOUND") { - throw configMissingError(extendName, importerPath); + throw configMissingError(extendName, ctx.filePath); } throw error; } writeDebugLogForLoading(request, relativeTo, filePath); - return this._loadConfigData(filePath, `${importerName} » ${request}`); + return this._loadConfigData({ + ...ctx, + filePath, + name: `${ctx.name} » ${request}` + }); } /** * Load given plugins. * @param {string[]} names The plugin names to load. - * @param {string} importerPath The path to a config file that imports it. This is just a debug info. - * @param {string} importerName The name of a config file that imports it. This is just a debug info. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {Record} The loaded parser. * @private */ - _loadPlugins(names, importerPath, importerName) { + _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, importerPath, importerName); + const plugin = this._loadPlugin(name, ctx); map[plugin.id] = plugin; @@ -881,15 +877,14 @@ class ConfigArrayFactory { /** * Load a given parser. * @param {string} nameOrPath The package name or the path to a parser file. - * @param {string} importerPath The path to a config file that imports it. - * @param {string} importerName The name of a config file that imports it. This is just a debug info. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {DependentParser} The loaded parser. */ - _loadParser(nameOrPath, importerPath, importerName) { - debug("Loading parser %j from %s", nameOrPath, importerPath); + _loadParser(nameOrPath, ctx) { + debug("Loading parser %j from %s", nameOrPath, ctx.filePath); const { cwd } = internalSlotsMap.get(this); - const relativeTo = importerPath || path.join(cwd, "__placeholder__.js"); + const relativeTo = ctx.filePath || path.join(cwd, "__placeholder__.js"); try { const filePath = ModuleResolver.resolve(nameOrPath, relativeTo); @@ -900,8 +895,8 @@ class ConfigArrayFactory { definition: require(filePath), filePath, id: nameOrPath, - importerName, - importerPath + importerName: ctx.name, + importerPath: ctx.filePath }); } catch (error) { @@ -912,19 +907,19 @@ class ConfigArrayFactory { definition: require("espree"), filePath: require.resolve("espree"), id: nameOrPath, - importerName, - importerPath + importerName: ctx.name, + importerPath: ctx.filePath }); } - debug("Failed to load parser '%s' declared in '%s'.", nameOrPath, importerName); - error.message = `Failed to load parser '${nameOrPath}' declared in '${importerName}': ${error.message}`; + 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, - importerPath + importerName: ctx.name, + importerPath: ctx.filePath }); } } @@ -932,13 +927,12 @@ class ConfigArrayFactory { /** * Load a given plugin. * @param {string} name The plugin name to load. - * @param {string} importerPath The path to a config file that imports it. This is just a debug info. - * @param {string} importerName The name of a config file that imports it. This is just a debug info. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {DependentPlugin} The loaded plugin. * @private */ - _loadPlugin(name, importerPath, importerName) { - debug("Loading plugin %j from %s", name, importerPath); + _loadPlugin(name, ctx) { + debug("Loading plugin %j from %s", name, ctx.filePath); const { additionalPluginPool, resolvePluginsRelativeTo } = internalSlotsMap.get(this); const request = naming.normalizePackageName(name, "eslint-plugin"); @@ -957,8 +951,8 @@ class ConfigArrayFactory { return new ConfigDependency({ error, id, - importerName, - importerPath + importerName: ctx.name, + importerPath: ctx.filePath }); } @@ -970,10 +964,10 @@ class ConfigArrayFactory { if (plugin) { return new ConfigDependency({ definition: normalizePlugin(plugin), - filePath: importerPath, + filePath: ctx.filePath, id, - importerName, - importerPath + importerName: ctx.name, + importerPath: ctx.filePath }); } @@ -990,7 +984,7 @@ class ConfigArrayFactory { error.messageData = { pluginName: request, resolvePluginsRelativeTo, - importerName + importerName: ctx.name }; } } @@ -1008,33 +1002,32 @@ class ConfigArrayFactory { definition: normalizePlugin(pluginDefinition), filePath, id, - importerName, - importerPath + importerName: ctx.name, + importerPath: ctx.filePath }); } catch (loadError) { error = loadError; } } - debug("Failed to load plugin '%s' declared in '%s'.", name, importerName); - error.message = `Failed to load plugin '${name}' declared in '${importerName}': ${error.message}`; + 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, - importerPath + importerName: ctx.name, + importerPath: ctx.filePath }); } /** * Take file expression processors as config array elements. * @param {Record} plugins The plugin definitions. - * @param {string} filePath The file path of this config. - * @param {string} name The name of this config. + * @param {ConfigArrayFactoryLoadingContext} ctx The loading context. * @returns {IterableIterator} The config array elements of file expression processors. * @private */ - *_takeFileExtensionProcessors(plugins, filePath, name) { + *_takeFileExtensionProcessors(plugins, ctx) { for (const pluginId of Object.keys(plugins)) { const processors = plugins[pluginId] && @@ -1049,12 +1042,14 @@ class ConfigArrayFactory { if (processorId.startsWith(".")) { yield* this._normalizeObjectConfigData( { - type: "implicit-processor", files: [`*${processorId}`], processor: `${pluginId}/${processorId}` }, - filePath, - `${name}#processors["${pluginId}/${processorId}"]` + { + ...ctx, + type: "implicit-processor", + name: `${ctx.name}#processors["${pluginId}/${processorId}"]` + } ); } } @@ -1062,4 +1057,4 @@ class ConfigArrayFactory { } } -module.exports = { ConfigArrayFactory }; +module.exports = { ConfigArrayFactory, createContext }; diff --git a/lib/cli-engine/config-array/config-array.js b/lib/cli-engine/config-array/config-array.js index b3f7c1e7350..1d10fe1b3f9 100644 --- a/lib/cli-engine/config-array/config-array.js +++ b/lib/cli-engine/config-array/config-array.js @@ -65,6 +65,7 @@ const { IgnorePattern } = require("./ignore-pattern"); * @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. */ /** diff --git a/lib/shared/types.js b/lib/shared/types.js index f3d1a7f29f0..bf37327fa24 100644 --- a/lib/shared/types.js +++ b/lib/shared/types.js @@ -37,7 +37,7 @@ module.exports = {}; * @property {ParserOptions} [parserOptions] The parser options. * @property {string[]} [plugins] The plugin specifiers. * @property {string} [processor] The processor specifier. - * @property {boolean|undefined} reportUnusedDisableDirectives The flag to report unused `eslint-disable` comments. + * @property {boolean} [reportUnusedDisableDirectives] The flag to report unused `eslint-disable` comments. * @property {boolean} [root] The root flag. * @property {Record} [rules] The rule settings. * @property {Object} [settings] The shared settings. @@ -56,7 +56,7 @@ module.exports = {}; * @property {ParserOptions} [parserOptions] The parser options. * @property {string[]} [plugins] The plugin specifiers. * @property {string} [processor] The processor specifier. - * @property {boolean|undefined} reportUnusedDisableDirectives The flag to report unused `eslint-disable` comments. + * @property {boolean} [reportUnusedDisableDirectives] The flag to report unused `eslint-disable` comments. * @property {Record} [rules] The rule settings. * @property {Object} [settings] The shared settings. */ diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index cb1e7cc4377..ba631f38344 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -4090,37 +4090,37 @@ describe("CLIEngine", () => { assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); }); - it("should resolve relative paths from the ignorePath, not cwd", () => { + it("should resolve relative paths from CWD", () => { const cwd = getFixturePath("ignored-paths", "subdir"); const ignorePath = getFixturePath("ignored-paths", ".eslintignoreForDifferentCwd"); const engine = new CLIEngine({ ignorePath, cwd }); - assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); - assert(engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); }); - it("should resolve relative paths from the ignorePath when it's in a child directory", () => { + it("should resolve relative paths from CWD when it's in a child directory", () => { const cwd = getFixturePath("ignored-paths"); const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); const engine = new CLIEngine({ ignorePath, cwd }); - assert(engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); - assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo.js"))); assert(engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/foo.js"))); assert(engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/bar.js"))); }); - it("should resolve relative paths from the ignorePath when it contains negated globs", () => { + it("should resolve relative paths from CWD when it contains negated globs", () => { const cwd = getFixturePath("ignored-paths"); const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); const engine = new CLIEngine({ ignorePath, cwd }); assert(engine.isPathIgnored("subdir/blah.txt")); assert(engine.isPathIgnored("blah.txt")); - assert(!engine.isPathIgnored("subdir/bar.txt")); - assert(engine.isPathIgnored("bar.txt")); + assert(engine.isPathIgnored("subdir/bar.txt")); + assert(!engine.isPathIgnored("bar.txt")); assert(!engine.isPathIgnored("subdir/baz.txt")); assert(!engine.isPathIgnored("baz.txt")); }); @@ -5704,4 +5704,204 @@ describe("CLIEngine", () => { }); }); }); + + 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 {CLIEngine} */ + let InMemoryCLIEngine; + + describe("if { files: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/myconf/.eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/*.js", + rules: { + eqeqeq: "error" + } + } + ] + }), + "node_modules/myconf/foo/test.js": "a == b", + "foo/test.js": "a == b" + } + }).CLIEngine; + }); + + it("'executeOnFiles()' with 'foo/test.js' should use the override entry.", () => { + const engine = new InMemoryCLIEngine({ + configFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const { results } = engine.executeOnFiles("foo/test.js"); + + // Expected to be an 'eqeqeq' error because the file matches to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 1, + filePath: path.join(root, "foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [ + { + column: 3, + endColumn: 5, + endLine: 1, + line: 1, + message: "Expected '===' and instead saw '=='.", + messageId: "unexpected", + nodeType: "BinaryExpression", + ruleId: "eqeqeq", + severity: 2 + } + ], + source: "a == b", + warningCount: 0 + } + ]); + }); + + it("'executeOnFiles()' with 'node_modules/myconf/foo/test.js' should NOT use the override entry.", () => { + const engine = new InMemoryCLIEngine({ + configFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const { results } = engine.executeOnFiles("node_modules/myconf/foo/test.js"); + + // Expected to be no errors because the file doesn't match to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 0, + filePath: path.join(root, "node_modules/myconf/foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [], + warningCount: 0 + } + ]); + }); + }); + + describe("if { files: '*', excludedFiles: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + 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" + } + }).CLIEngine; + }); + + it("'executeOnFiles()' with 'foo/test.js' should NOT use the override entry.", () => { + const engine = new InMemoryCLIEngine({ + configFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const { results } = engine.executeOnFiles("foo/test.js"); + + // Expected to be no errors because the file matches to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 0, + filePath: path.join(root, "foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [], + warningCount: 0 + } + ]); + }); + + it("'executeOnFiles()' with 'node_modules/myconf/foo/test.js' should use the override entry.", () => { + const engine = new InMemoryCLIEngine({ + configFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const { results } = engine.executeOnFiles("node_modules/myconf/foo/test.js"); + + // Expected to be an 'eqeqeq' error because the file doesn't match to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 1, + filePath: path.join(root, "node_modules/myconf/foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [ + { + column: 3, + endColumn: 5, + endLine: 1, + line: 1, + message: "Expected '===' and instead saw '=='.", + messageId: "unexpected", + nodeType: "BinaryExpression", + ruleId: "eqeqeq", + severity: 2 + } + ], + source: "a == b", + warningCount: 0 + } + ]); + }); + }); + + describe("if { ignorePatterns: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + 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" + } + }).CLIEngine; + }); + + it("'executeOnFiles()' with '**/*.js' should iterate 'node_modules/myconf/foo/test.js' but not 'foo/test.js'.", () => { + const engine = new InMemoryCLIEngine({ + configFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + useEslintrc: false + }); + const files = engine.executeOnFiles("**/*.js") + .results + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(files, [ + path.join(root, "node_modules/myconf/foo/test.js") + ]); + }); + }); + }); }); diff --git a/tests/lib/cli-engine/config-array-factory.js b/tests/lib/cli-engine/config-array-factory.js index 2e1d230a574..e3e7759de21 100644 --- a/tests/lib/cli-engine/config-array-factory.js +++ b/tests/lib/cli-engine/config-array-factory.js @@ -10,6 +10,7 @@ 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"); @@ -112,23 +113,23 @@ describe("ConfigArrayFactory", () => { }, /Unexpected top-level property "files"/u); }); - it("should call '_normalizeConfigData(configData, options)' with given arguments except 'options.parent'.", () => { + it("should call '_normalizeConfigData(configData, ctx)' with given arguments.", () => { const configData = {}; + const basePath = tempDir; const filePath = __filename; const name = "example"; - const parent = new ConfigArray(); const normalizeConfigData = spy(factory, "_normalizeConfigData"); - factory.create(configData, { filePath, name, parent }); + factory.create(configData, { basePath, filePath, name }); assert.strictEqual(normalizeConfigData.callCount, 1); - assert.strictEqual(normalizeConfigData.args[0].length, 3); - assert.strictEqual(normalizeConfigData.args[0][0], configData); - assert.strictEqual(normalizeConfigData.args[0][1], filePath); - assert.strictEqual(normalizeConfigData.args[0][2], name); + assert.deepStrictEqual(normalizeConfigData.args[0], [ + configData, + createContext(tempDir, void 0, name, filePath, basePath) + ]); }); - it("should return a config array that contains the yielded elements from '_normalizeConfigData(configData, options)'.", () => { + 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 @@ -139,34 +140,6 @@ describe("ConfigArrayFactory", () => { assert.strictEqual(configArray[0], elements[0]); assert.strictEqual(configArray[1], elements[1]); }); - - it("should concatenate the elements of `options.parent` and the yielded elements from '_normalizeConfigData(configData, options)'.", () => { - const parent = new ConfigArray({}, {}); - const elements = [{}, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.create({}, { parent }); - - assert.strictEqual(configArray.length, 4); - assert.strictEqual(configArray[0], parent[0]); - assert.strictEqual(configArray[1], parent[1]); - assert.strictEqual(configArray[2], elements[0]); - assert.strictEqual(configArray[3], elements[1]); - }); - - it("should not concatenate the elements of `options.parent` if the yielded elements from '_normalizeConfigData(configData, options)' has 'root:true'.", () => { - const parent = new ConfigArray({}, {}); - const elements = [{ root: true }, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.create({}, { parent }); - - 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.", () => { @@ -231,22 +204,22 @@ describe("ConfigArrayFactory", () => { }); } - it("should call '_normalizeConfigData(configData, options)' with the loaded config data and given options except 'options.parent'.", () => { + 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 parent = new ConfigArray(); const normalizeConfigData = spy(factory, "_normalizeConfigData"); - factory.loadFile(filePath, { name, parent }); + factory.loadFile(filePath, { basePath, name }); assert.strictEqual(normalizeConfigData.callCount, 1); - assert.strictEqual(normalizeConfigData.args[0].length, 3); - assert.deepStrictEqual(normalizeConfigData.args[0][0], { settings: { name: filePath } }); - assert.strictEqual(normalizeConfigData.args[0][1], path.resolve(tempDir, filePath)); - assert.strictEqual(normalizeConfigData.args[0][2], name); + assert.deepStrictEqual(normalizeConfigData.args[0], [ + { settings: { name: filePath } }, + createContext(tempDir, void 0, name, filePath, basePath) + ]); }); - it("should return a config array that contains the yielded elements from '_normalizeConfigData(configData, options)'.", () => { + 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 @@ -257,34 +230,6 @@ describe("ConfigArrayFactory", () => { assert.strictEqual(configArray[0], elements[0]); assert.strictEqual(configArray[1], elements[1]); }); - - it("should concatenate the elements of `options.parent` and the yielded elements from '_normalizeConfigData(configData, options)'.", () => { - const parent = new ConfigArray({}, {}); - const elements = [{}, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.loadFile("js/.eslintrc.js", { parent }); - - assert.strictEqual(configArray.length, 4); - assert.strictEqual(configArray[0], parent[0]); - assert.strictEqual(configArray[1], parent[1]); - assert.strictEqual(configArray[2], elements[0]); - assert.strictEqual(configArray[3], elements[1]); - }); - - it("should not concatenate the elements of `options.parent` if the yielded elements from '_normalizeConfigData(configData, options)' has 'root:true'.", () => { - const parent = new ConfigArray({}, {}); - const elements = [{ root: true }, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.loadFile("js/.eslintrc.js", { parent }); - - 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.", () => { @@ -347,22 +292,22 @@ describe("ConfigArrayFactory", () => { }); } - it("should call '_normalizeConfigData(configData, options)' with the loaded config data and given options except 'options.parent'.", () => { + it("should call '_normalizeConfigData(configData, ctx)' with the loaded config data and given options.", () => { + const basePath = tempDir; const directoryPath = "js"; const name = "example"; - const parent = new ConfigArray(); const normalizeConfigData = spy(factory, "_normalizeConfigData"); - factory.loadInDirectory(directoryPath, { name, parent }); + factory.loadInDirectory(directoryPath, { basePath, name }); assert.strictEqual(normalizeConfigData.callCount, 1); - assert.strictEqual(normalizeConfigData.args[0].length, 3); - assert.deepStrictEqual(normalizeConfigData.args[0][0], { settings: { name: `${directoryPath}/.eslintrc.js` } }); - assert.strictEqual(normalizeConfigData.args[0][1], path.resolve(tempDir, directoryPath, ".eslintrc.js")); - assert.strictEqual(normalizeConfigData.args[0][2], name); + assert.deepStrictEqual(normalizeConfigData.args[0], [ + { settings: { name: `${directoryPath}/.eslintrc.js` } }, + createContext(tempDir, void 0, name, path.join(directoryPath, ".eslintrc.js"), basePath) + ]); }); - it("should return a config array that contains the yielded elements from '_normalizeConfigData(configData, options)'.", () => { + 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 @@ -373,41 +318,13 @@ describe("ConfigArrayFactory", () => { assert.strictEqual(configArray[0], elements[0]); assert.strictEqual(configArray[1], elements[1]); }); - - it("should concatenate the elements of `options.parent` and the yielded elements from '_normalizeConfigData(configData, options)'.", () => { - const parent = new ConfigArray({}, {}); - const elements = [{}, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.loadInDirectory("js", { parent }); - - assert.strictEqual(configArray.length, 4); - assert.strictEqual(configArray[0], parent[0]); - assert.strictEqual(configArray[1], parent[1]); - assert.strictEqual(configArray[2], elements[0]); - assert.strictEqual(configArray[3], elements[1]); - }); - - it("should not concatenate the elements of `options.parent` if the yielded elements from '_normalizeConfigData(configData, options)' has 'root:true'.", () => { - const parent = new ConfigArray({}, {}); - const elements = [{ root: true }, {}]; - - factory._normalizeConfigData = () => elements; // eslint-disable-line no-underscore-dangle - - const configArray = factory.loadInDirectory("js", { parent }); - - 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, options)' method should normalize the config data.", () => { + describe("'_normalizeConfigData(configData, ctx)' method should normalize the config data.", () => { /** @type {ConfigArrayFactory} */ let factory = null; @@ -421,7 +338,9 @@ describe("ConfigArrayFactory", () => { * @returns {ConfigArray} The created config array. */ function create(configData, { filePath, name } = {}) { - return new ConfigArray(...factory._normalizeConfigData(configData, filePath, name)); // eslint-disable-line no-underscore-dangle + const ctx = createContext(tempDir, void 0, name, filePath, void 0); + + return new ConfigArray(...factory._normalizeConfigData(configData, ctx)); // eslint-disable-line no-underscore-dangle } describe("misc", () => {