1 | /**
|
2 | * @fileoverview Rule to flag trailing underscores in variable declarations.
|
3 | * @author Matt DuVall <http://www.mattduvall.com>
|
4 | */
|
5 |
|
6 | ;
|
7 |
|
8 | //------------------------------------------------------------------------------
|
9 | // Rule Definition
|
10 | //------------------------------------------------------------------------------
|
11 |
|
12 | module.exports = function(context) {
|
13 |
|
14 | var options = context.options[0] || {};
|
15 | var ALLOWED_VARIABLES = options.allow ? options.allow : [];
|
16 | var allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false;
|
17 |
|
18 | //-------------------------------------------------------------------------
|
19 | // Helpers
|
20 | //-------------------------------------------------------------------------
|
21 |
|
22 | /**
|
23 | * Check if identifier is present inside the allowed option
|
24 | * @param {string} identifier name of the node
|
25 | * @returns {boolean} true if its is present
|
26 | * @private
|
27 | */
|
28 | function isAllowed(identifier) {
|
29 | return ALLOWED_VARIABLES.some(function(ident) {
|
30 | return ident === identifier;
|
31 | });
|
32 | }
|
33 |
|
34 | /**
|
35 | * Check if identifier has a underscore at the end
|
36 | * @param {ASTNode} identifier node to evaluate
|
37 | * @returns {boolean} true if its is present
|
38 | * @private
|
39 | */
|
40 | function hasTrailingUnderscore(identifier) {
|
41 | var len = identifier.length;
|
42 |
|
43 | return identifier !== "_" && (identifier[0] === "_" || identifier[len - 1] === "_");
|
44 | }
|
45 |
|
46 | /**
|
47 | * Check if identifier is a special case member expression
|
48 | * @param {ASTNode} identifier node to evaluate
|
49 | * @returns {boolean} true if its is a special case
|
50 | * @private
|
51 | */
|
52 | function isSpecialCaseIdentifierForMemberExpression(identifier) {
|
53 | return identifier === "__proto__";
|
54 | }
|
55 |
|
56 | /**
|
57 | * Check if identifier is a special case variable expression
|
58 | * @param {ASTNode} identifier node to evaluate
|
59 | * @returns {boolean} true if its is a special case
|
60 | * @private
|
61 | */
|
62 | function isSpecialCaseIdentifierInVariableExpression(identifier) {
|
63 | // Checks for the underscore library usage here
|
64 | return identifier === "_";
|
65 | }
|
66 |
|
67 | /**
|
68 | * Check if function has a underscore at the end
|
69 | * @param {ASTNode} node node to evaluate
|
70 | * @returns {void}
|
71 | * @private
|
72 | */
|
73 | function checkForTrailingUnderscoreInFunctionDeclaration(node) {
|
74 | if (node.id) {
|
75 | var identifier = node.id.name;
|
76 |
|
77 | if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
|
78 | context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
|
79 | }
|
80 | }
|
81 | }
|
82 |
|
83 | /**
|
84 | * Check if variable expression has a underscore at the end
|
85 | * @param {ASTNode} node node to evaluate
|
86 | * @returns {void}
|
87 | * @private
|
88 | */
|
89 | function checkForTrailingUnderscoreInVariableExpression(node) {
|
90 | var identifier = node.id.name;
|
91 |
|
92 | if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
|
93 | !isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
|
94 | context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
|
95 | }
|
96 | }
|
97 |
|
98 | /**
|
99 | * Check if member expression has a underscore at the end
|
100 | * @param {ASTNode} node node to evaluate
|
101 | * @returns {void}
|
102 | * @private
|
103 | */
|
104 | function checkForTrailingUnderscoreInMemberExpression(node) {
|
105 | var identifier = node.property.name,
|
106 | isMemberOfThis = node.object.type === "ThisExpression";
|
107 |
|
108 | if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
|
109 | !(isMemberOfThis && allowAfterThis) &&
|
110 | !isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) {
|
111 | context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
|
112 | }
|
113 | }
|
114 |
|
115 | //--------------------------------------------------------------------------
|
116 | // Public API
|
117 | //--------------------------------------------------------------------------
|
118 |
|
119 | return {
|
120 | "FunctionDeclaration": checkForTrailingUnderscoreInFunctionDeclaration,
|
121 | "VariableDeclarator": checkForTrailingUnderscoreInVariableExpression,
|
122 | "MemberExpression": checkForTrailingUnderscoreInMemberExpression
|
123 | };
|
124 |
|
125 | };
|
126 |
|
127 | module.exports.schema = [
|
128 | {
|
129 | "type": "object",
|
130 | "properties": {
|
131 | "allow": {
|
132 | "type": "array",
|
133 | "items": {
|
134 | "type": "string"
|
135 | }
|
136 | },
|
137 | "allowAfterThis": {
|
138 | "type": "boolean"
|
139 | }
|
140 | },
|
141 | "additionalProperties": false
|
142 | }
|
143 | ];
|