1 | /**
|
2 | * @fileoverview Rule to flag non-camelcased identifiers
|
3 | * @author Nicholas C. Zakas
|
4 | * @copyright 2015 Dieter Oberkofler. All rights reserved.
|
5 | */
|
6 |
|
7 | ;
|
8 |
|
9 | //------------------------------------------------------------------------------
|
10 | // Rule Definition
|
11 | //------------------------------------------------------------------------------
|
12 |
|
13 | module.exports = function(context) {
|
14 |
|
15 | //--------------------------------------------------------------------------
|
16 | // Helpers
|
17 | //--------------------------------------------------------------------------
|
18 |
|
19 | // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
|
20 | var reported = [];
|
21 |
|
22 | /**
|
23 | * Checks if a string contains an underscore and isn't all upper-case
|
24 | * @param {String} name The string to check.
|
25 | * @returns {boolean} if the string is underscored
|
26 | * @private
|
27 | */
|
28 | function isUnderscored(name) {
|
29 |
|
30 | // if there's an underscore, it might be A_CONSTANT, which is okay
|
31 | return name.indexOf("_") > -1 && name !== name.toUpperCase();
|
32 | }
|
33 |
|
34 | /**
|
35 | * Reports an AST node as a rule violation.
|
36 | * @param {ASTNode} node The node to report.
|
37 | * @returns {void}
|
38 | * @private
|
39 | */
|
40 | function report(node) {
|
41 | if (reported.indexOf(node) < 0) {
|
42 | reported.push(node);
|
43 | context.report(node, "Identifier '{{name}}' is not in camel case.", { name: node.name });
|
44 | }
|
45 | }
|
46 |
|
47 | var options = context.options[0] || {},
|
48 | properties = options.properties || "";
|
49 |
|
50 | if (properties !== "always" && properties !== "never") {
|
51 | properties = "always";
|
52 | }
|
53 |
|
54 | return {
|
55 |
|
56 | "Identifier": function(node) {
|
57 | // Leading and trailing underscores are commonly used to flag private/protected identifiers, strip them
|
58 | var name = node.name.replace(/^_+|_+$/g, ""),
|
59 | effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
|
60 |
|
61 | // MemberExpressions get special rules
|
62 | if (node.parent.type === "MemberExpression") {
|
63 |
|
64 | // "never" check properties
|
65 | if (properties === "never") {
|
66 | return;
|
67 | }
|
68 |
|
69 | // Always report underscored object names
|
70 | if (node.parent.object.type === "Identifier" &&
|
71 | node.parent.object.name === node.name &&
|
72 | isUnderscored(name)) {
|
73 | report(node);
|
74 |
|
75 | // Report AssignmentExpressions only if they are the left side of the assignment
|
76 | } else if (effectiveParent.type === "AssignmentExpression" &&
|
77 | isUnderscored(name) &&
|
78 | (effectiveParent.right.type !== "MemberExpression" ||
|
79 | effectiveParent.left.type === "MemberExpression" &&
|
80 | effectiveParent.left.property.name === node.name)) {
|
81 | report(node);
|
82 | }
|
83 |
|
84 | // Properties have their own rules
|
85 | } else if (node.parent.type === "Property") {
|
86 | // "never" check properties
|
87 | if (properties === "never") {
|
88 | return;
|
89 | }
|
90 |
|
91 | if (node.parent.parent && node.parent.parent.type === "ObjectPattern" &&
|
92 | node.parent.key === node && node.parent.value !== node) {
|
93 | return;
|
94 | }
|
95 |
|
96 | if (isUnderscored(name) && effectiveParent.type !== "CallExpression") {
|
97 | report(node);
|
98 | }
|
99 |
|
100 | // Report anything that is underscored that isn't a CallExpression
|
101 | } else if (isUnderscored(name) && effectiveParent.type !== "CallExpression") {
|
102 | report(node);
|
103 | }
|
104 | }
|
105 |
|
106 | };
|
107 |
|
108 | };
|
109 |
|
110 | module.exports.schema = [
|
111 | {
|
112 | "type": "object",
|
113 | "properties": {
|
114 | "properties": {
|
115 | "enum": ["always", "never"]
|
116 | }
|
117 | },
|
118 | "additionalProperties": false
|
119 | }
|
120 | ];
|