From e7581dd83302708ecf5c963e963075febe1a0885 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Dec 2019 21:12:06 +0900 Subject: [PATCH 01/12] Breaking: runtime-deprecation on '~/.eslintrc' (refs eslint/rfcs#32) --- .../cascading-config-array-factory.js | 49 ++++++++++++---- lib/shared/config-validator.js | 29 +--------- lib/shared/deprecation-warnings.js | 56 +++++++++++++++++++ 3 files changed, 95 insertions(+), 39 deletions(-) create mode 100644 lib/shared/deprecation-warnings.js diff --git a/lib/cli-engine/cascading-config-array-factory.js b/lib/cli-engine/cascading-config-array-factory.js index 52703a873bb..4a5ab73ff09 100644 --- a/lib/cli-engine/cascading-config-array-factory.js +++ b/lib/cli-engine/cascading-config-array-factory.js @@ -26,6 +26,7 @@ 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"); @@ -290,10 +291,11 @@ class CascadingConfigArrayFactory { /** * 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) { + _loadConfigInAncestors(directoryPath, configsExistInSubdirs = false) { const { baseConfigArray, configArrayFactory, @@ -315,14 +317,6 @@ class CascadingConfigArrayFactory { } 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."); - return this._cacheConfig(directoryPath, baseConfigArray); - } - // Load the config on this directory. try { configArray = configArrayFactory.loadInDirectory(directoryPath); @@ -335,6 +329,22 @@ class CascadingConfigArrayFactory { throw error; } + const homePath = os.homedir(); + + // Consider this is root. + if (directoryPath === homePath && cwd !== homePath) { + debug("Stop traversing because of considered root."); + if (configsExistInSubdirs && configArray.length > 0) { + const lastElement = configArray[configArray.length - 1]; + + emitDeprecationWarning( + lastElement.filePath, + "ESLINT_PERSONAL_CONFOG_SUPPRESS" + ); + } + return this._cacheConfig(directoryPath, baseConfigArray); + } + if (configArray.length > 0 && configArray.isRoot()) { debug("Stop traversing because of 'root:true'."); configArray.unshift(...baseConfigArray); @@ -344,7 +354,10 @@ class CascadingConfigArrayFactory { // Load from the ancestors and merge it. const parentPath = path.dirname(directoryPath); const parentConfigArray = parentPath && parentPath !== directoryPath - ? this._loadConfigInAncestors(parentPath) + ? this._loadConfigInAncestors( + parentPath, + configsExistInSubdirs || configArray.length > 0 + ) : baseConfigArray; if (configArray.length > 0) { @@ -402,10 +415,22 @@ class CascadingConfigArrayFactory { ) { debug("Loading the config file of the home directory."); - finalConfigArray = configArrayFactory.loadInDirectory( + const personalConfigArray = configArrayFactory.loadInDirectory( os.homedir(), - { name: "PersonalConfig", parent: finalConfigArray } + { name: "PersonalConfig" } ); + + if (personalConfigArray.length > 0) { + const lastElement = + personalConfigArray[personalConfigArray.length - 1]; + + emitDeprecationWarning( + lastElement.filePath, + "ESLINT_PERSONAL_CONFOG_LOAD" + ); + finalConfigArray = + finalConfigArray.concat(personalConfigArray); + } } // Apply CLI options. diff --git a/lib/shared/config-validator.js b/lib/shared/config-validator.js index aca6e1fb274..70eaf0a9670 100644 --- a/lib/shared/config-validator.js +++ b/lib/shared/config-validator.js @@ -10,13 +10,12 @@ //------------------------------------------------------------------------------ const - path = require("path"), util = require("util"), - lodash = require("lodash"), configSchema = require("../../conf/config-schema"), BuiltInEnvironments = require("../../conf/environments"), BuiltInRules = require("../rules"), - ConfigOps = require("./config-ops"); + ConfigOps = require("./config-ops"), + { emitDeprecationWarning } = require("./deprecation-warnings"); const ajv = require("./ajv")(); const ruleValidators = new WeakMap(); @@ -26,11 +25,6 @@ const noop = Function.prototype; // Private //------------------------------------------------------------------------------ let validateSchema; - -// Defitions for deprecation warnings. -const deprecationWarningMessages = { - ESLINT_LEGACY_ECMAFEATURES: "The 'ecmaFeatures' config file property is deprecated, and has no effect." -}; const severityMap = { error: 2, warn: 1, @@ -254,25 +248,6 @@ function formatErrors(errors) { }).map(message => `\t- ${message}.\n`).join(""); } -/** - * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted - * for each unique file path, but repeated invocations with the same file path have no effect. - * No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active. - * @param {string} source The name of the configuration source to report the warning for. - * @param {string} errorCode The warning message to show. - * @returns {void} - */ -const emitDeprecationWarning = lodash.memoize((source, errorCode) => { - const rel = path.relative(process.cwd(), source); - const message = deprecationWarningMessages[errorCode]; - - process.emitWarning( - `${message} (found in "${rel}")`, - "DeprecationWarning", - errorCode - ); -}); - /** * Validates the top level properties of the config object. * @param {Object} config The config object to validate. diff --git a/lib/shared/deprecation-warnings.js b/lib/shared/deprecation-warnings.js new file mode 100644 index 00000000000..fce52549bd1 --- /dev/null +++ b/lib/shared/deprecation-warnings.js @@ -0,0 +1,56 @@ +/** + * @fileoverview Provide the function that emits deprecation warnings. + * @author Toru Nagashima + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const path = require("path"); +const lodash = require("lodash"); + +//------------------------------------------------------------------------------ +// Private +//------------------------------------------------------------------------------ + +// Defitions for deprecation warnings. +const deprecationWarningMessages = { + ESLINT_LEGACY_ECMAFEATURES: + "The 'ecmaFeatures' config file property is deprecated, and has no effect.", + ESLINT_PERSONAL_CONFOG_LOAD: + "The '~/.eslintrc.*' config file has been deprecated. " + + "Please use config files for each project or '--config' option.", + ESLINT_PERSONAL_CONFOG_SUPPRESS: + "The '~/.eslintrc.*' config file has been deprecated. " + + "Please remove it or add 'root:true' into the config file of your " + + "projects in order to avoid loading '~/.eslintrc.*' accidentally." +}; + +/** + * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted + * for each unique file path, but repeated invocations with the same file path have no effect. + * No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active. + * @param {string} source The name of the configuration source to report the warning for. + * @param {string} errorCode The warning message to show. + * @returns {void} + */ +const emitDeprecationWarning = lodash.memoize((source, errorCode) => { + const rel = path.relative(process.cwd(), source); + const message = deprecationWarningMessages[errorCode]; + + process.emitWarning( + `${message} (found in "${rel}")`, + "DeprecationWarning", + errorCode + ); +}, (...args) => JSON.stringify(args)); + +//------------------------------------------------------------------------------ +// Public Interface +//------------------------------------------------------------------------------ + +module.exports = { + emitDeprecationWarning +}; From b5bedc8df1717516b586efdd00c7493b8dead92a Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:12:10 +0900 Subject: [PATCH 02/12] fix to not load personal config for the suppress warning --- .../cascading-config-array-factory.js | 34 ++++++++++--------- lib/cli-engine/config-array-factory.js | 23 +++++++++++++ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/lib/cli-engine/cascading-config-array-factory.js b/lib/cli-engine/cascading-config-array-factory.js index 4a5ab73ff09..552d87f62c2 100644 --- a/lib/cli-engine/cascading-config-array-factory.js +++ b/lib/cli-engine/cascading-config-array-factory.js @@ -317,6 +317,24 @@ class CascadingConfigArrayFactory { } 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_CONFOG_SUPPRESS" + ); + } + } + return this._cacheConfig(directoryPath, baseConfigArray); + } + // Load the config on this directory. try { configArray = configArrayFactory.loadInDirectory(directoryPath); @@ -329,22 +347,6 @@ class CascadingConfigArrayFactory { throw error; } - const homePath = os.homedir(); - - // Consider this is root. - if (directoryPath === homePath && cwd !== homePath) { - debug("Stop traversing because of considered root."); - if (configsExistInSubdirs && configArray.length > 0) { - const lastElement = configArray[configArray.length - 1]; - - emitDeprecationWarning( - lastElement.filePath, - "ESLINT_PERSONAL_CONFOG_SUPPRESS" - ); - } - return this._cacheConfig(directoryPath, baseConfigArray); - } - if (configArray.length > 0 && configArray.isRoot()) { debug("Stop traversing because of 'root:true'."); configArray.unshift(...baseConfigArray); diff --git a/lib/cli-engine/config-array-factory.js b/lib/cli-engine/config-array-factory.js index c444031bcb0..f63230c9af9 100644 --- a/lib/cli-engine/config-array-factory.js +++ b/lib/cli-engine/config-array-factory.js @@ -434,6 +434,29 @@ class ConfigArrayFactory { ); } + /** + * 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 (error) { /* ignore */ } + } else { + return filePath; + } + } + } + return null; + } + /** * Load `.eslintignore` file. * @param {string} filePath The path to a `.eslintignore` file to load. From 88e69afc606258209cb5caf52d70341778063c74 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:13:29 +0900 Subject: [PATCH 03/12] fix typo --- lib/cli-engine/cascading-config-array-factory.js | 4 ++-- lib/shared/deprecation-warnings.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cli-engine/cascading-config-array-factory.js b/lib/cli-engine/cascading-config-array-factory.js index 552d87f62c2..7a54732eee8 100644 --- a/lib/cli-engine/cascading-config-array-factory.js +++ b/lib/cli-engine/cascading-config-array-factory.js @@ -328,7 +328,7 @@ class CascadingConfigArrayFactory { if (filePath) { emitDeprecationWarning( filePath, - "ESLINT_PERSONAL_CONFOG_SUPPRESS" + "ESLINT_PERSONAL_CONFIG_SUPPRESS" ); } } @@ -428,7 +428,7 @@ class CascadingConfigArrayFactory { emitDeprecationWarning( lastElement.filePath, - "ESLINT_PERSONAL_CONFOG_LOAD" + "ESLINT_PERSONAL_CONFIG_LOAD" ); finalConfigArray = finalConfigArray.concat(personalConfigArray); diff --git a/lib/shared/deprecation-warnings.js b/lib/shared/deprecation-warnings.js index fce52549bd1..37f25283dc2 100644 --- a/lib/shared/deprecation-warnings.js +++ b/lib/shared/deprecation-warnings.js @@ -19,10 +19,10 @@ const lodash = require("lodash"); const deprecationWarningMessages = { ESLINT_LEGACY_ECMAFEATURES: "The 'ecmaFeatures' config file property is deprecated, and has no effect.", - ESLINT_PERSONAL_CONFOG_LOAD: + ESLINT_PERSONAL_CONFIG_LOAD: "The '~/.eslintrc.*' config file has been deprecated. " + "Please use config files for each project or '--config' option.", - ESLINT_PERSONAL_CONFOG_SUPPRESS: + ESLINT_PERSONAL_CONFIG_SUPPRESS: "The '~/.eslintrc.*' config file has been deprecated. " + "Please remove it or add 'root:true' into the config file of your " + "projects in order to avoid loading '~/.eslintrc.*' accidentally." From e40898f133dc87a8a5a13eec777d6ab7b1ee9163 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:14:24 +0900 Subject: [PATCH 04/12] Update lib/shared/deprecation-warnings.js Co-Authored-By: Kai Cataldo --- lib/shared/deprecation-warnings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shared/deprecation-warnings.js b/lib/shared/deprecation-warnings.js index 37f25283dc2..11bdceacf3d 100644 --- a/lib/shared/deprecation-warnings.js +++ b/lib/shared/deprecation-warnings.js @@ -18,7 +18,7 @@ const lodash = require("lodash"); // Defitions for deprecation warnings. const deprecationWarningMessages = { ESLINT_LEGACY_ECMAFEATURES: - "The 'ecmaFeatures' config file property is deprecated, and has no effect.", + "The 'ecmaFeatures' config file property is deprecated and has no effect.", ESLINT_PERSONAL_CONFIG_LOAD: "The '~/.eslintrc.*' config file has been deprecated. " + "Please use config files for each project or '--config' option.", From 034a023935910e6b9b4fbb11b03080063a7d9706 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:14:31 +0900 Subject: [PATCH 05/12] Update lib/shared/deprecation-warnings.js Co-Authored-By: Kai Cataldo --- lib/shared/deprecation-warnings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shared/deprecation-warnings.js b/lib/shared/deprecation-warnings.js index 11bdceacf3d..74d3fec0168 100644 --- a/lib/shared/deprecation-warnings.js +++ b/lib/shared/deprecation-warnings.js @@ -21,7 +21,7 @@ const deprecationWarningMessages = { "The 'ecmaFeatures' config file property is deprecated and has no effect.", ESLINT_PERSONAL_CONFIG_LOAD: "The '~/.eslintrc.*' config file has been deprecated. " + - "Please use config files for each project or '--config' option.", + "Please use a config file per project or the '--config' option.", ESLINT_PERSONAL_CONFIG_SUPPRESS: "The '~/.eslintrc.*' config file has been deprecated. " + "Please remove it or add 'root:true' into the config file of your " + From 8d08cacab6afae2f933274465285a646f1ac0908 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:14:37 +0900 Subject: [PATCH 06/12] Update lib/shared/deprecation-warnings.js Co-Authored-By: Kai Cataldo --- lib/shared/deprecation-warnings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shared/deprecation-warnings.js b/lib/shared/deprecation-warnings.js index 74d3fec0168..0d8a731507d 100644 --- a/lib/shared/deprecation-warnings.js +++ b/lib/shared/deprecation-warnings.js @@ -20,7 +20,7 @@ const deprecationWarningMessages = { ESLINT_LEGACY_ECMAFEATURES: "The 'ecmaFeatures' config file property is deprecated and has no effect.", ESLINT_PERSONAL_CONFIG_LOAD: - "The '~/.eslintrc.*' config file has been deprecated. " + + "'~/.eslintrc.*' config files have been deprecated. " + "Please use a config file per project or the '--config' option.", ESLINT_PERSONAL_CONFIG_SUPPRESS: "The '~/.eslintrc.*' config file has been deprecated. " + From bd34015cd67c6794b4e25b21c41a89b36ec342d0 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:14:43 +0900 Subject: [PATCH 07/12] Update lib/shared/deprecation-warnings.js Co-Authored-By: Kai Cataldo --- lib/shared/deprecation-warnings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shared/deprecation-warnings.js b/lib/shared/deprecation-warnings.js index 0d8a731507d..db8442eaf9c 100644 --- a/lib/shared/deprecation-warnings.js +++ b/lib/shared/deprecation-warnings.js @@ -23,7 +23,7 @@ const deprecationWarningMessages = { "'~/.eslintrc.*' config files have been deprecated. " + "Please use a config file per project or the '--config' option.", ESLINT_PERSONAL_CONFIG_SUPPRESS: - "The '~/.eslintrc.*' config file has been deprecated. " + + "'~/.eslintrc.*' config files have been deprecated. " + "Please remove it or add 'root:true' into the config file of your " + "projects in order to avoid loading '~/.eslintrc.*' accidentally." }; From 086db3706b6bd611799a8719efd62451ba2ce551 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:14:49 +0900 Subject: [PATCH 08/12] Update lib/shared/deprecation-warnings.js Co-Authored-By: Kai Cataldo --- lib/shared/deprecation-warnings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shared/deprecation-warnings.js b/lib/shared/deprecation-warnings.js index db8442eaf9c..e1481a2e9aa 100644 --- a/lib/shared/deprecation-warnings.js +++ b/lib/shared/deprecation-warnings.js @@ -24,7 +24,7 @@ const deprecationWarningMessages = { "Please use a config file per project or the '--config' option.", ESLINT_PERSONAL_CONFIG_SUPPRESS: "'~/.eslintrc.*' config files have been deprecated. " + - "Please remove it or add 'root:true' into the config file of your " + + "Please remove it or add 'root:true' to the config files in your " + "projects in order to avoid loading '~/.eslintrc.*' accidentally." }; From f2ea7f3c351137eaefe7b5a5c2005913b79f067d Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:25:41 +0900 Subject: [PATCH 09/12] fix existing tests for the change of message --- tests/bin/eslint.js | 2 +- tests/lib/cli-engine/cascading-config-array-factory.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bin/eslint.js b/tests/bin/eslint.js index 932dd9ec451..774a1ace5c3 100644 --- a/tests/bin/eslint.js +++ b/tests/bin/eslint.js @@ -388,7 +388,7 @@ describe("bin/eslint.js", () => { const exitCodePromise = assertExitCode(child, 0); const outputPromise = getOutput(child).then(output => { - assert.include(output.stderr, "The 'ecmaFeatures' config file property is deprecated, and has no effect."); + assert.include(output.stderr, "The 'ecmaFeatures' config file property is deprecated and has no effect."); }); return Promise.all([exitCodePromise, outputPromise]); diff --git a/tests/lib/cli-engine/cascading-config-array-factory.js b/tests/lib/cli-engine/cascading-config-array-factory.js index 2748aa608a4..58d7d7f90e9 100644 --- a/tests/lib/cli-engine/cascading-config-array-factory.js +++ b/tests/lib/cli-engine/cascading-config-array-factory.js @@ -1162,7 +1162,7 @@ describe("CascadingConfigArrayFactory", () => { 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")` + `The 'ecmaFeatures' config file property is deprecated and has no effect. (found in "ecma-features${path.sep}.eslintrc.yml")` ); }); }); From 11e74dd59e495050bb514b15a11df1b6040a3b06 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 18 Dec 2019 14:27:09 +0900 Subject: [PATCH 10/12] update documentation --- docs/user-guide/configuring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md index 5022fc44d8e..e979e75e633 100644 --- a/docs/user-guide/configuring.md +++ b/docs/user-guide/configuring.md @@ -1099,7 +1099,7 @@ This message occurs because ESLint is unsure if you wanted to actually lint the ## Personal Configuration File (deprecated) -⚠️ **This feature has been deprecated**. ESLint will print deprecation warnings beginning with the 7.0.0 release. This feature will then be removed in the 8.0.0 release. If you want to continue to use personal configuration files, please use the [`--config` CLI option](https://eslint.org/docs/user-guide/command-line-interface#-c---config). For more information regarding this decision, please see [RFC 28](https://github.com/eslint/rfcs/pull/28) and [RFC 32](https://github.com/eslint/rfcs/pull/32). +⚠️ **This feature has been deprecated**. This feature will be removed in the 8.0.0 release. If you want to continue to use personal configuration files, please use the [`--config` CLI option](https://eslint.org/docs/user-guide/command-line-interface#-c---config). For more information regarding this decision, please see [RFC 28](https://github.com/eslint/rfcs/pull/28) and [RFC 32](https://github.com/eslint/rfcs/pull/32). `~/` refers to [the home directory of the current user on your preferred operating system](https://nodejs.org/api/os.html#os_os_homedir). The personal configuration file being referred to here is `~/.eslintrc.*` file, which is currently handled differently than other configuration files. From 1e8b79b1f90999c5a82922bb80f940c3e7b36e9b Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 20 Dec 2019 21:13:58 +0900 Subject: [PATCH 11/12] add tests --- .../cascading-config-array-factory.js | 15 +- .../cascading-config-array-factory.js | 303 ++++++++++++++++++ 2 files changed, 313 insertions(+), 5 deletions(-) diff --git a/lib/cli-engine/cascading-config-array-factory.js b/lib/cli-engine/cascading-config-array-factory.js index 7a54732eee8..ca03bec0638 100644 --- a/lib/cli-engine/cascading-config-array-factory.js +++ b/lib/cli-engine/cascading-config-array-factory.js @@ -415,14 +415,19 @@ class CascadingConfigArrayFactory { configArray.every(c => !c.filePath) && cliConfigArray.every(c => !c.filePath) // `--config` option can be a file. ) { - debug("Loading the config file of the home directory."); + const homePath = os.homedir(); + + debug("Loading the config file of the home directory:", homePath); const personalConfigArray = configArrayFactory.loadInDirectory( - os.homedir(), + homePath, { name: "PersonalConfig" } ); - if (personalConfigArray.length > 0) { + if ( + personalConfigArray.length > 0 && + !directoryPath.startsWith(homePath) + ) { const lastElement = personalConfigArray[personalConfigArray.length - 1]; @@ -430,9 +435,9 @@ class CascadingConfigArrayFactory { lastElement.filePath, "ESLINT_PERSONAL_CONFIG_LOAD" ); - finalConfigArray = - finalConfigArray.concat(personalConfigArray); } + + finalConfigArray = finalConfigArray.concat(personalConfigArray); } // Apply CLI options. diff --git a/tests/lib/cli-engine/cascading-config-array-factory.js b/tests/lib/cli-engine/cascading-config-array-factory.js index 58d7d7f90e9..9852078ba89 100644 --- a/tests/lib/cli-engine/cascading-config-array-factory.js +++ b/tests/lib/cli-engine/cascading-config-array-factory.js @@ -14,6 +14,9 @@ const { ConfigArrayFactory } = require("../../../lib/cli-engine/config-array-fac 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 @@ -87,6 +90,306 @@ describe("CascadingConfigArrayFactory", () => { }); }); + 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"] } + ); + }); + }); + }); + }); + // 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"); From 8175ed41c51ae189fef11430dcfdc646167467b6 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 20 Dec 2019 21:27:32 +0900 Subject: [PATCH 12/12] add more two tests --- .../cascading-config-array-factory.js | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/lib/cli-engine/cascading-config-array-factory.js b/tests/lib/cli-engine/cascading-config-array-factory.js index 9852078ba89..32d094aba81 100644 --- a/tests/lib/cli-engine/cascading-config-array-factory.js +++ b/tests/lib/cli-engine/cascading-config-array-factory.js @@ -388,6 +388,64 @@ describe("CascadingConfigArrayFactory", () => { }); }); }); + + 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.