Skip to content

Commit

Permalink
Update: Add option "ignoreGlobals" to camelcase rule (fixes #11716) (#…
Browse files Browse the repository at this point in the history
…12782)

* Update: Add option "ignoreGlobals" to camelcase rule (fixes 11716)

* Change reference check to look for global variable

* Fix behavior for global references and add tests

* Add more valid tests to camelcase rule

* Add more invalid tests for camelcase rule

* Don't ignore non-camelcase global variable used as object key

* Corrections to camelcase documentation
  • Loading branch information
mcdado authored Jun 26, 2020
1 parent 0655f66 commit 51e42ec
Show file tree
Hide file tree
Showing 3 changed files with 420 additions and 0 deletions.
24 changes: 24 additions & 0 deletions docs/rules/camelcase.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 no_camelcased */

const foo = no_camelcased;
```

### ignoreGlobals: true

Examples of **correct** code for this rule with the `{ "ignoreGlobals": true }` option:

```js
/*eslint camelcase: ["error", {ignoreGlobals: true}]*/
/* global no_camelcased */

const foo = no_camelcased;
```

## allow

Examples of **correct** code for this rule with the `allow` option:
Expand Down
47 changes: 47 additions & 0 deletions lib/rules/camelcase.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ module.exports = {
type: "boolean",
default: false
},
ignoreGlobals: {
type: "boolean",
default: false
},
properties: {
enum: ["always", "never"]
},
Expand Down Expand Up @@ -61,8 +65,11 @@ module.exports = {
let properties = options.properties || "";
const ignoreDestructuring = options.ignoreDestructuring;
const ignoreImports = options.ignoreImports;
const ignoreGlobals = options.ignoreGlobals;
const allow = options.allow || [];

let globalScope;

if (properties !== "always" && properties !== "never") {
properties = "always";
}
Expand Down Expand Up @@ -159,6 +166,37 @@ module.exports = {
return false;
}

/**
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a reference to a global variable.
*/
function isReferenceToGlobalVariable(node) {
const variable = globalScope.set.get(node.name);

return variable && variable.defs.length === 0 &&
variable.references.some(ref => ref.identifier === node);
}

/**
* Checks whether the given node represents a reference to a property of an object in an object literal expression.
* This allows to differentiate between a global variable that is allowed to be used as a reference, and the key
* of the expressed object (which shouldn't be allowed).
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a property name of an object literal expression
*/
function isPropertyNameInObjectLiteral(node) {
const parent = node.parent;

return (
parent.type === "Property" &&
parent.parent.type === "ObjectExpression" &&
!parent.computed &&
parent.key === node
);
}

/**
* Reports an AST node as a rule violation.
* @param {ASTNode} node The node to report.
Expand All @@ -174,6 +212,10 @@ module.exports = {

return {

Program() {
globalScope = context.getScope();
},

Identifier(node) {

/*
Expand All @@ -189,6 +231,11 @@ module.exports = {
return;
}

// Check if it's a global variable
if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
return;
}

// MemberExpressions get special rules
if (node.parent.type === "MemberExpression") {

Expand Down
Loading

0 comments on commit 51e42ec

Please sign in to comment.