UNPKG

4.08 kBJavaScriptView Raw
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"use strict";
8
9//------------------------------------------------------------------------------
10// Rule Definition
11//------------------------------------------------------------------------------
12
13module.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
110module.exports.schema = [
111 {
112 "type": "object",
113 "properties": {
114 "properties": {
115 "enum": ["always", "never"]
116 }
117 },
118 "additionalProperties": false
119 }
120];