Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking: runtime-deprecation on '~/.eslintrc' (refs eslint/rfcs#32) #12678

Merged
merged 14 commits into from Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/user-guide/configuring.md
Expand Up @@ -1100,7 +1100,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.

Expand Down
44 changes: 38 additions & 6 deletions lib/cli-engine/cascading-config-array-factory.js
Expand Up @@ -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");
Expand Down Expand Up @@ -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,
Expand All @@ -320,6 +322,16 @@ class CascadingConfigArrayFactory {
// 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);
}

Expand All @@ -344,7 +356,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) {
Expand Down Expand Up @@ -400,12 +415,29 @@ 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);

finalConfigArray = configArrayFactory.loadInDirectory(
os.homedir(),
{ name: "PersonalConfig", parent: finalConfigArray }
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.
Expand Down
23 changes: 23 additions & 0 deletions lib/cli-engine/config-array-factory.js
Expand Up @@ -436,6 +436,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.
Expand Down
29 changes: 2 additions & 27 deletions lib/shared/config-validator.js
Expand Up @@ -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();
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
56 changes: 56 additions & 0 deletions lib/shared/deprecation-warnings.js
@@ -0,0 +1,56 @@
/**
* @fileoverview Provide the function that emits deprecation warnings.
* @author Toru Nagashima <http://github.com/mysticatea>
*/
"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_CONFIG_LOAD:
"'~/.eslintrc.*' config files have been deprecated. " +
"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' to the config files in 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
};
2 changes: 1 addition & 1 deletion tests/bin/eslint.js
Expand Up @@ -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]);
Expand Down