From feeebeeb67d7e575d2354ed41673bd98de99cde5 Mon Sep 17 00:00:00 2001 From: David Gasperoni Date: Mon, 13 Jan 2020 17:39:44 +0100 Subject: [PATCH] Update: Add option "ignoreGlobals" to camelcase rule (fixes 11716) --- docs/rules/camelcase.md | 24 ++++++++++++++++++++++ lib/rules/camelcase.js | 40 ++++++++++++++++++++++++++++++++++++ tests/lib/rules/camelcase.js | 22 ++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/docs/rules/camelcase.md b/docs/rules/camelcase.md index 96135f8c9326..0a9c326445cf 100644 --- a/docs/rules/camelcase.md +++ b/docs/rules/camelcase.md @@ -16,6 +16,8 @@ This rule has an object option: * `"ignoreDestructuring": true` does not check destructured identifiers (but still checks any use of those identifiers later in the code) * `"ignoreImports": false` (default) enforces camelcase style for ES2015 imports * `"ignoreImports": true` does not check ES2015 imports (but still checks any use of the imports later in the code except function arguments) +* `"ignoreGlobals": false` (default) enforces camelcase style for global variables +* `"ignoreGlobals": true` does not enforce camelcase style for global variables * `allow` (`string[]`) list of properties to accept. Accept regex. ### properties: "always" @@ -217,6 +219,28 @@ Examples of **correct** code for this rule with the `{ "ignoreImports": true }` import { snake_cased } from 'mod'; ``` +### ignoreGlobals: false + +Examples of **incorrect** code for this rule with the default `{ "ignoreGlobals": false }` option: + +```js +/*eslint camelcase: ["error", {ignoreGlobals: false}]*/ +/* global some_property */ + +const property = some_property; +``` + +### ignoreGlobals: true + +Examples of **correct** code for this rule with the `{ "ignoreGlobals": true }` option: + +```js +/*eslint camelcase: ["error", {ignoreGlobals: true}]*/ +/* global some_property */ + +const property = some_property; +``` + ## allow Examples of **correct** code for this rule with the `allow` option: diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js index a06c7f94042a..8ef4bc6fbf8b 100644 --- a/lib/rules/camelcase.js +++ b/lib/rules/camelcase.js @@ -5,6 +5,28 @@ "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const { findVariable } = require("eslint-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Determines whether the given identifier node is a reference to a global variable. + * @param {ASTNode} node `Identifier` node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} True if the identifier is a reference to a global variable. + */ +function isGlobalReference(node, scope) { + const variable = findVariable(scope, node); + + return variable !== null && variable.scope.type === "global" && variable.defs.length === 0; +} + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -32,6 +54,10 @@ module.exports = { type: "boolean", default: false }, + ignoreGlobals: { + type: "boolean", + default: false + }, properties: { enum: ["always", "never"] }, @@ -61,7 +87,9 @@ module.exports = { let properties = options.properties || ""; const ignoreDestructuring = options.ignoreDestructuring; const ignoreImports = options.ignoreImports; + const ignoreGlobals = options.ignoreGlobals; const allow = options.allow || []; + const currentScope = context.getScope(); if (properties !== "always" && properties !== "never") { properties = "always"; @@ -229,6 +257,18 @@ module.exports = { report(node); } + // Check if it's a global variable + } else if (isGlobalReference(node, currentScope)) { + + if (ignoreGlobals) { + return; + } + + // Report only if the local imported identifier is underscored + if (nameIsUnderscored) { + report(node); + } + // Report anything that is underscored that isn't a CallExpression } else if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) { report(node); diff --git a/tests/lib/rules/camelcase.js b/tests/lib/rules/camelcase.js index 1181561efa8f..fdd15ee6a242 100644 --- a/tests/lib/rules/camelcase.js +++ b/tests/lib/rules/camelcase.js @@ -161,6 +161,16 @@ ruleTester.run("camelcase", rule, { options: [{ ignoreImports: false }], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { + code: "var _camelCased = camelCased", + options: [{ ignoreGlobals: false }], + globals: { camelCased: false } + }, + { + code: "var camelCased = snake_cased", + options: [{ ignoreGlobals: true }], + globals: { snake_cased: false } // eslint-disable-line camelcase + }, { code: "function foo({ no_camelcased: camelCased }) {};", parserOptions: { ecmaVersion: 6 } @@ -590,6 +600,18 @@ ruleTester.run("camelcase", rule, { } ] }, + { + code: "var camelCased = snake_cased", + options: [{ ignoreGlobals: false }], + globals: { snake_cased: false }, // eslint-disable-line camelcase + errors: [ + { + messageId: "notCamelCase", + data: { name: "snake_cased" }, + type: "Identifier" + } + ] + }, { code: "function foo({ no_camelcased }) {};", parserOptions: { ecmaVersion: 6 },