UNPKG

8.54 kBJavaScriptView Raw
1/**
2 * @fileoverview Disallows or enforces spaces inside of array brackets.
3 * @author Jamund Ferguson
4 * @copyright 2015 Jamund Ferguson. All rights reserved.
5 * @copyright 2014 Brandyn Bennett. All rights reserved.
6 * @copyright 2014 Michael Ficarra. No rights reserved.
7 * @copyright 2014 Vignesh Anand. All rights reserved.
8 */
9"use strict";
10
11var astUtils = require("../ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17module.exports = {
18 meta: {
19 docs: {
20 description: "Enforce spacing inside array brackets",
21 category: "Stylistic Issues",
22 recommended: false,
23 fixable: "whitespace"
24 },
25 schema: [
26 {
27 "enum": ["always", "never"]
28 },
29 {
30 "type": "object",
31 "properties": {
32 "singleValue": {
33 "type": "boolean"
34 },
35 "objectsInArrays": {
36 "type": "boolean"
37 },
38 "arraysInArrays": {
39 "type": "boolean"
40 }
41 },
42 "additionalProperties": false
43 }
44 ]
45 },
46 create: function(context) {
47 var spaced = context.options[0] === "always",
48 sourceCode = context.getSourceCode();
49
50 /**
51 * Determines whether an option is set, relative to the spacing option.
52 * If spaced is "always", then check whether option is set to false.
53 * If spaced is "never", then check whether option is set to true.
54 * @param {Object} option - The option to exclude.
55 * @returns {boolean} Whether or not the property is excluded.
56 */
57 function isOptionSet(option) {
58 return context.options[1] ? context.options[1][option] === !spaced : false;
59 }
60
61 var options = {
62 spaced: spaced,
63 singleElementException: isOptionSet("singleValue"),
64 objectsInArraysException: isOptionSet("objectsInArrays"),
65 arraysInArraysException: isOptionSet("arraysInArrays")
66 };
67
68 //--------------------------------------------------------------------------
69 // Helpers
70 //--------------------------------------------------------------------------
71
72 /**
73 * Reports that there shouldn't be a space after the first token
74 * @param {ASTNode} node - The node to report in the event of an error.
75 * @param {Token} token - The token to use for the report.
76 * @returns {void}
77 */
78 function reportNoBeginningSpace(node, token) {
79 context.report({
80 node: node,
81 loc: token.loc.start,
82 message: "There should be no space after '" + token.value + "'",
83 fix: function(fixer) {
84 var nextToken = context.getSourceCode().getTokenAfter(token);
85 return fixer.removeRange([token.range[1], nextToken.range[0]]);
86 }
87 });
88 }
89
90 /**
91 * Reports that there shouldn't be a space before the last token
92 * @param {ASTNode} node - The node to report in the event of an error.
93 * @param {Token} token - The token to use for the report.
94 * @returns {void}
95 */
96 function reportNoEndingSpace(node, token) {
97 context.report({
98 node: node,
99 loc: token.loc.start,
100 message: "There should be no space before '" + token.value + "'",
101 fix: function(fixer) {
102 var previousToken = context.getSourceCode().getTokenBefore(token);
103 return fixer.removeRange([previousToken.range[1], token.range[0]]);
104 }
105 });
106 }
107
108 /**
109 * Reports that there should be a space after the first token
110 * @param {ASTNode} node - The node to report in the event of an error.
111 * @param {Token} token - The token to use for the report.
112 * @returns {void}
113 */
114 function reportRequiredBeginningSpace(node, token) {
115 context.report({
116 node: node,
117 loc: token.loc.start,
118 message: "A space is required after '" + token.value + "'",
119 fix: function(fixer) {
120 return fixer.insertTextAfter(token, " ");
121 }
122 });
123 }
124
125 /**
126 * Reports that there should be a space before the last token
127 * @param {ASTNode} node - The node to report in the event of an error.
128 * @param {Token} token - The token to use for the report.
129 * @returns {void}
130 */
131 function reportRequiredEndingSpace(node, token) {
132 context.report({
133 node: node,
134 loc: token.loc.start,
135 message: "A space is required before '" + token.value + "'",
136 fix: function(fixer) {
137 return fixer.insertTextBefore(token, " ");
138 }
139 });
140 }
141
142 /**
143 * Determines if a node is an object type
144 * @param {ASTNode} node - The node to check.
145 * @returns {boolean} Whether or not the node is an object type.
146 */
147 function isObjectType(node) {
148 return node && (node.type === "ObjectExpression" || node.type === "ObjectPattern");
149 }
150
151 /**
152 * Determines if a node is an array type
153 * @param {ASTNode} node - The node to check.
154 * @returns {boolean} Whether or not the node is an array type.
155 */
156 function isArrayType(node) {
157 return node && (node.type === "ArrayExpression" || node.type === "ArrayPattern");
158 }
159
160 /**
161 * Validates the spacing around array brackets
162 * @param {ASTNode} node - The node we're checking for spacing
163 * @returns {void}
164 */
165 function validateArraySpacing(node) {
166 if (options.spaced && node.elements.length === 0) {
167 return;
168 }
169
170 var first = context.getFirstToken(node),
171 second = context.getFirstToken(node, 1),
172 penultimate = context.getLastToken(node, 1),
173 last = context.getLastToken(node),
174 firstElement = node.elements[0],
175 lastElement = node.elements[node.elements.length - 1];
176
177 var openingBracketMustBeSpaced =
178 options.objectsInArraysException && isObjectType(firstElement) ||
179 options.arraysInArraysException && isArrayType(firstElement) ||
180 options.singleElementException && node.elements.length === 1
181 ? !options.spaced : options.spaced;
182
183 var closingBracketMustBeSpaced =
184 options.objectsInArraysException && isObjectType(lastElement) ||
185 options.arraysInArraysException && isArrayType(lastElement) ||
186 options.singleElementException && node.elements.length === 1
187 ? !options.spaced : options.spaced;
188
189 if (astUtils.isTokenOnSameLine(first, second)) {
190 if (openingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(first, second)) {
191 reportRequiredBeginningSpace(node, first);
192 }
193 if (!openingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(first, second)) {
194 reportNoBeginningSpace(node, first);
195 }
196 }
197
198 if (first !== penultimate && astUtils.isTokenOnSameLine(penultimate, last)) {
199 if (closingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(penultimate, last)) {
200 reportRequiredEndingSpace(node, last);
201 }
202 if (!closingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(penultimate, last)) {
203 reportNoEndingSpace(node, last);
204 }
205 }
206 }
207
208 //--------------------------------------------------------------------------
209 // Public
210 //--------------------------------------------------------------------------
211
212 return {
213 ArrayPattern: validateArraySpacing,
214 ArrayExpression: validateArraySpacing
215 };
216 }
217};