Skip to content

Commit 51e42ec

Browse files
authored
Update: Add option "ignoreGlobals" to camelcase rule (fixes #11716) (#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
1 parent 0655f66 commit 51e42ec

File tree

3 files changed

+420
-0
lines changed

3 files changed

+420
-0
lines changed

docs/rules/camelcase.md

+24
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ This rule has an object option:
1616
* `"ignoreDestructuring": true` does not check destructured identifiers (but still checks any use of those identifiers later in the code)
1717
* `"ignoreImports": false` (default) enforces camelcase style for ES2015 imports
1818
* `"ignoreImports": true` does not check ES2015 imports (but still checks any use of the imports later in the code except function arguments)
19+
* `"ignoreGlobals": false` (default) enforces camelcase style for global variables
20+
* `"ignoreGlobals": true` does not enforce camelcase style for global variables
1921
* `allow` (`string[]`) list of properties to accept. Accept regex.
2022

2123
### properties: "always"
@@ -217,6 +219,28 @@ Examples of **correct** code for this rule with the `{ "ignoreImports": true }`
217219
import { snake_cased } from 'mod';
218220
```
219221

222+
### ignoreGlobals: false
223+
224+
Examples of **incorrect** code for this rule with the default `{ "ignoreGlobals": false }` option:
225+
226+
```js
227+
/*eslint camelcase: ["error", {ignoreGlobals: false}]*/
228+
/* global no_camelcased */
229+
230+
const foo = no_camelcased;
231+
```
232+
233+
### ignoreGlobals: true
234+
235+
Examples of **correct** code for this rule with the `{ "ignoreGlobals": true }` option:
236+
237+
```js
238+
/*eslint camelcase: ["error", {ignoreGlobals: true}]*/
239+
/* global no_camelcased */
240+
241+
const foo = no_camelcased;
242+
```
243+
220244
## allow
221245

222246
Examples of **correct** code for this rule with the `allow` option:

lib/rules/camelcase.js

+47
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ module.exports = {
3232
type: "boolean",
3333
default: false
3434
},
35+
ignoreGlobals: {
36+
type: "boolean",
37+
default: false
38+
},
3539
properties: {
3640
enum: ["always", "never"]
3741
},
@@ -61,8 +65,11 @@ module.exports = {
6165
let properties = options.properties || "";
6266
const ignoreDestructuring = options.ignoreDestructuring;
6367
const ignoreImports = options.ignoreImports;
68+
const ignoreGlobals = options.ignoreGlobals;
6469
const allow = options.allow || [];
6570

71+
let globalScope;
72+
6673
if (properties !== "always" && properties !== "never") {
6774
properties = "always";
6875
}
@@ -159,6 +166,37 @@ module.exports = {
159166
return false;
160167
}
161168

169+
/**
170+
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
171+
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
172+
* @param {ASTNode} node `Identifier` node to check.
173+
* @returns {boolean} `true` if the node is a reference to a global variable.
174+
*/
175+
function isReferenceToGlobalVariable(node) {
176+
const variable = globalScope.set.get(node.name);
177+
178+
return variable && variable.defs.length === 0 &&
179+
variable.references.some(ref => ref.identifier === node);
180+
}
181+
182+
/**
183+
* Checks whether the given node represents a reference to a property of an object in an object literal expression.
184+
* This allows to differentiate between a global variable that is allowed to be used as a reference, and the key
185+
* of the expressed object (which shouldn't be allowed).
186+
* @param {ASTNode} node `Identifier` node to check.
187+
* @returns {boolean} `true` if the node is a property name of an object literal expression
188+
*/
189+
function isPropertyNameInObjectLiteral(node) {
190+
const parent = node.parent;
191+
192+
return (
193+
parent.type === "Property" &&
194+
parent.parent.type === "ObjectExpression" &&
195+
!parent.computed &&
196+
parent.key === node
197+
);
198+
}
199+
162200
/**
163201
* Reports an AST node as a rule violation.
164202
* @param {ASTNode} node The node to report.
@@ -174,6 +212,10 @@ module.exports = {
174212

175213
return {
176214

215+
Program() {
216+
globalScope = context.getScope();
217+
},
218+
177219
Identifier(node) {
178220

179221
/*
@@ -189,6 +231,11 @@ module.exports = {
189231
return;
190232
}
191233

234+
// Check if it's a global variable
235+
if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
236+
return;
237+
}
238+
192239
// MemberExpressions get special rules
193240
if (node.parent.type === "MemberExpression") {
194241

0 commit comments

Comments
 (0)