Skip to content
This repository was archived by the owner on Jan 19, 2019. It is now read-only.

Commit ddf2d42

Browse files
authored
Fix: camelcase false positives on interface properties (fixes #177) (#183)
* Fix: camelcase properties in interfaces (fixes #177)
1 parent 576b1aa commit ddf2d42

File tree

4 files changed

+500
-0
lines changed

4 files changed

+500
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ This guarantees 100% compatibility between the plugin and the parser.
4747
<!-- Please run `npm run docs` to update this section -->
4848
<!-- begin rule list -->
4949
* [`typescript/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) — Require that member overloads be consecutive
50+
* [`typescript/camelcase`](./docs/rules/camelcase.md) — Enforce camelCase naming convention
5051
* [`typescript/class-name-casing`](./docs/rules/class-name-casing.md) — Require PascalCased class and interface names (`class-name` from TSLint)
5152
* [`typescript/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) — Require explicit return types on functions and class methods
5253
* [`typescript/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) — Require explicit accessibility modifiers on class properties and methods (`member-access` from TSLint)

docs/rules/camelcase.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# Enforce camelCase naming convention (camelcase)
2+
3+
When it comes to naming variables, style guides generally fall into one of two
4+
camps: camelcase (`variableName`) and underscores (`variable_name`). This rule
5+
focuses on using the camelcase approach. If your style guide calls for
6+
camelCasing your variable names, then this rule is for you!
7+
8+
## Rule Details
9+
10+
This rule looks for any underscores (`_`) located within the source code.
11+
It ignores leading and trailing underscores and only checks those in the middle
12+
of a variable name. If ESLint decides that the variable is a constant
13+
(all uppercase), then no warning will be thrown. Otherwise, a warning will be
14+
thrown. This rule only flags definitions and assignments but not function calls.
15+
In case of ES6 `import` statements, this rule only targets the name of the
16+
variable that will be imported into the local module scope.
17+
18+
***This rule was taken from the ESLint core rule `camelcase`.***
19+
***Available options and test cases may vary depending on the version of ESLint installed in the system.***
20+
21+
## Options
22+
23+
This rule has an object option:
24+
25+
* `"properties": "always"` (default) enforces camelcase style for property names
26+
* `"properties": "never"` does not check property names
27+
* `"ignoreDestructuring": false` (default) enforces camelcase style for destructured identifiers
28+
* `"ignoreDestructuring": true` does not check destructured identifiers
29+
* `allow` (`string[]`) list of properties to accept. Accept regex.
30+
31+
### properties: "always"
32+
33+
Examples of **incorrect** code for this rule with the default `{ "properties": "always" }` option:
34+
35+
```js
36+
/*eslint camelcase: "error"*/
37+
38+
import { no_camelcased } from "external-module"
39+
40+
var my_favorite_color = "#112C85";
41+
42+
function do_something() {
43+
// ...
44+
}
45+
46+
obj.do_something = function() {
47+
// ...
48+
};
49+
50+
function foo({ no_camelcased }) {
51+
// ...
52+
};
53+
54+
function foo({ isCamelcased: no_camelcased }) {
55+
// ...
56+
}
57+
58+
function foo({ no_camelcased = 'default value' }) {
59+
// ...
60+
};
61+
62+
var obj = {
63+
my_pref: 1
64+
};
65+
66+
var { category_id = 1 } = query;
67+
68+
var { foo: no_camelcased } = bar;
69+
70+
var { foo: bar_baz = 1 } = quz;
71+
```
72+
73+
Examples of **correct** code for this rule with the default `{ "properties": "always" }` option:
74+
75+
```js
76+
/*eslint camelcase: "error"*/
77+
78+
import { no_camelcased as camelCased } from "external-module";
79+
80+
var myFavoriteColor = "#112C85";
81+
var _myFavoriteColor = "#112C85";
82+
var myFavoriteColor_ = "#112C85";
83+
var MY_FAVORITE_COLOR = "#112C85";
84+
var foo = bar.baz_boom;
85+
var foo = { qux: bar.baz_boom };
86+
87+
obj.do_something();
88+
do_something();
89+
new do_something();
90+
91+
var { category_id: category } = query;
92+
93+
function foo({ isCamelCased }) {
94+
// ...
95+
};
96+
97+
function foo({ isCamelCased: isAlsoCamelCased }) {
98+
// ...
99+
}
100+
101+
function foo({ isCamelCased = 'default value' }) {
102+
// ...
103+
};
104+
105+
var { categoryId = 1 } = query;
106+
107+
var { foo: isCamelCased } = bar;
108+
109+
var { foo: isCamelCased = 1 } = quz;
110+
111+
```
112+
113+
### properties: "never"
114+
115+
Examples of **correct** code for this rule with the `{ "properties": "never" }` option:
116+
117+
```js
118+
/*eslint camelcase: ["error", {properties: "never"}]*/
119+
120+
var obj = {
121+
my_pref: 1
122+
};
123+
```
124+
125+
### ignoreDestructuring: false
126+
127+
Examples of **incorrect** code for this rule with the default `{ "ignoreDestructuring": false }` option:
128+
129+
```js
130+
/*eslint camelcase: "error"*/
131+
132+
var { category_id } = query;
133+
134+
var { category_id = 1 } = query;
135+
136+
var { category_id: category_id } = query;
137+
138+
var { category_id: category_alias } = query;
139+
140+
var { category_id: categoryId, ...other_props } = query;
141+
```
142+
143+
### ignoreDestructuring: true
144+
145+
Examples of **incorrect** code for this rule with the `{ "ignoreDestructuring": true }` option:
146+
147+
```js
148+
/*eslint camelcase: ["error", {ignoreDestructuring: true}]*/
149+
150+
var { category_id: category_alias } = query;
151+
152+
var { category_id, ...other_props } = query;
153+
```
154+
155+
Examples of **correct** code for this rule with the `{ "ignoreDestructuring": true }` option:
156+
157+
```js
158+
/*eslint camelcase: ["error", {ignoreDestructuring: true}]*/
159+
160+
var { category_id } = query;
161+
162+
var { category_id = 1 } = query;
163+
164+
var { category_id: category_id } = query;
165+
```
166+
167+
## allow
168+
169+
Examples of **correct** code for this rule with the `allow` option:
170+
171+
```js
172+
/*eslint camelcase: ["error", {allow: ["UNSAFE_componentWillMount"]}]*/
173+
174+
function UNSAFE_componentWillMount() {
175+
// ...
176+
}
177+
```
178+
179+
```js
180+
/*eslint camelcase: ["error", {allow: ["^UNSAFE_"]}]*/
181+
182+
function UNSAFE_componentWillMount() {
183+
// ...
184+
}
185+
186+
function UNSAFE_componentWillMount() {
187+
// ...
188+
}
189+
```
190+
191+
## When Not To Use It
192+
193+
If you have established coding standards using a different naming convention (separating words with underscores), turn this rule off.
194+
195+
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/camelcase.md)</sup>

lib/rules/camelcase.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* @fileoverview Rule to flag non-camelcased identifiers
3+
* @author Patricio Trevino
4+
*/
5+
"use strict";
6+
7+
const baseRule = require("eslint/lib/rules/camelcase");
8+
9+
//------------------------------------------------------------------------------
10+
// Rule Definition
11+
//------------------------------------------------------------------------------
12+
13+
module.exports = {
14+
meta: Object.assign({}, baseRule.meta, {
15+
docs: {
16+
description: "Enforce camelCase naming convention",
17+
},
18+
}),
19+
20+
create(context) {
21+
const rules = baseRule.create(context);
22+
const TS_PROPERTY_TYPES = [
23+
"TSPropertySignature",
24+
"ClassProperty",
25+
"TSParameterProperty",
26+
"TSAbstractClassProperty",
27+
];
28+
29+
const options = context.options[0] || {};
30+
let properties = options.properties || "";
31+
const allow = options.allow || [];
32+
33+
if (properties !== "always" && properties !== "never") {
34+
properties = "always";
35+
}
36+
37+
/**
38+
* Checks if a string contains an underscore and isn't all upper-case
39+
* @param {string} name The string to check.
40+
* @returns {boolean} if the string is underscored
41+
* @private
42+
*/
43+
function isUnderscored(name) {
44+
// if there's an underscore, it might be A_CONSTANT, which is okay
45+
return name.indexOf("_") > -1 && name !== name.toUpperCase();
46+
}
47+
48+
/**
49+
* Checks if a string match the ignore list
50+
* @param {string} name The string to check.
51+
* @returns {boolean} if the string is ignored
52+
* @private
53+
*/
54+
function isAllowed(name) {
55+
return (
56+
allow.findIndex(
57+
entry => name === entry || name.match(new RegExp(entry))
58+
) !== -1
59+
);
60+
}
61+
62+
/**
63+
* Checks if the the node is a valid TypeScript property type.
64+
* @param {Node} node the node to be validated.
65+
* @returns {boolean} true if the node is a TypeScript property type.
66+
* @private
67+
*/
68+
function isTSPropertyType(node) {
69+
if (!node.parent) return false;
70+
if (TS_PROPERTY_TYPES.includes(node.parent.type)) return true;
71+
72+
if (node.parent.type === "AssignmentPattern") {
73+
return (
74+
node.parent.parent &&
75+
TS_PROPERTY_TYPES.includes(node.parent.parent.type)
76+
);
77+
}
78+
79+
return false;
80+
}
81+
82+
return {
83+
Identifier(node) {
84+
/*
85+
* Leading and trailing underscores are commonly used to flag
86+
* private/protected identifiers, strip them
87+
*/
88+
const name = node.name.replace(/^_+|_+$/g, "");
89+
90+
// First, we ignore the node if it match the ignore list
91+
if (isAllowed(name)) {
92+
return;
93+
}
94+
95+
// Check TypeScript specific nodes
96+
if (isTSPropertyType(node)) {
97+
if (properties === "always" && isUnderscored(name)) {
98+
context.report({
99+
node,
100+
messageId: "notCamelCase",
101+
data: { name: node.name },
102+
});
103+
}
104+
105+
return;
106+
}
107+
108+
// Let the base rule deal with the rest
109+
// eslint-disable-next-line new-cap
110+
rules.Identifier(node);
111+
},
112+
};
113+
},
114+
};

0 commit comments

Comments
 (0)