2 * @fileoverview Disallows or enforces spaces inside of array brackets.
3 * @author Jamund Ferguson
7 const astUtils = require("./utils/ast-utils");
9 //------------------------------------------------------------------------------
11 //------------------------------------------------------------------------------
18 description: "enforce consistent spacing inside array brackets",
19 category: "Stylistic Issues",
21 url: "https://eslint.org/docs/rules/array-bracket-spacing"
24 fixable: "whitespace",
28 enum: ["always", "never"]
43 additionalProperties: false
48 unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.",
49 unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.",
50 missingSpaceAfter: "A space is required after '{{tokenValue}}'.",
51 missingSpaceBefore: "A space is required before '{{tokenValue}}'."
55 const spaced = context.options[0] === "always",
56 sourceCode = context.getSourceCode();
59 * Determines whether an option is set, relative to the spacing option.
60 * If spaced is "always", then check whether option is set to false.
61 * If spaced is "never", then check whether option is set to true.
62 * @param {Object} option The option to exclude.
63 * @returns {boolean} Whether or not the property is excluded.
65 function isOptionSet(option) {
66 return context.options[1] ? context.options[1][option] === !spaced : false;
71 singleElementException: isOptionSet("singleValue"),
72 objectsInArraysException: isOptionSet("objectsInArrays"),
73 arraysInArraysException: isOptionSet("arraysInArrays")
76 //--------------------------------------------------------------------------
78 //--------------------------------------------------------------------------
81 * Reports that there shouldn't be a space after the first token
82 * @param {ASTNode} node The node to report in the event of an error.
83 * @param {Token} token The token to use for the report.
86 function reportNoBeginningSpace(node, token) {
87 const nextToken = sourceCode.getTokenAfter(token);
91 loc: { start: token.loc.end, end: nextToken.loc.start },
92 messageId: "unexpectedSpaceAfter",
94 tokenValue: token.value
97 return fixer.removeRange([token.range[1], nextToken.range[0]]);
103 * Reports that there shouldn't be a space before the last token
104 * @param {ASTNode} node The node to report in the event of an error.
105 * @param {Token} token The token to use for the report.
108 function reportNoEndingSpace(node, token) {
109 const previousToken = sourceCode.getTokenBefore(token);
113 loc: { start: previousToken.loc.end, end: token.loc.start },
114 messageId: "unexpectedSpaceBefore",
116 tokenValue: token.value
119 return fixer.removeRange([previousToken.range[1], token.range[0]]);
125 * Reports that there should be a space after the first token
126 * @param {ASTNode} node The node to report in the event of an error.
127 * @param {Token} token The token to use for the report.
130 function reportRequiredBeginningSpace(node, token) {
134 messageId: "missingSpaceAfter",
136 tokenValue: token.value
139 return fixer.insertTextAfter(token, " ");
145 * Reports that there should be a space before the last token
146 * @param {ASTNode} node The node to report in the event of an error.
147 * @param {Token} token The token to use for the report.
150 function reportRequiredEndingSpace(node, token) {
154 messageId: "missingSpaceBefore",
156 tokenValue: token.value
159 return fixer.insertTextBefore(token, " ");
165 * Determines if a node is an object type
166 * @param {ASTNode} node The node to check.
167 * @returns {boolean} Whether or not the node is an object type.
169 function isObjectType(node) {
170 return node && (node.type === "ObjectExpression" || node.type === "ObjectPattern");
174 * Determines if a node is an array type
175 * @param {ASTNode} node The node to check.
176 * @returns {boolean} Whether or not the node is an array type.
178 function isArrayType(node) {
179 return node && (node.type === "ArrayExpression" || node.type === "ArrayPattern");
183 * Validates the spacing around array brackets
184 * @param {ASTNode} node The node we're checking for spacing
187 function validateArraySpacing(node) {
188 if (options.spaced && node.elements.length === 0) {
192 const first = sourceCode.getFirstToken(node),
193 second = sourceCode.getFirstToken(node, 1),
194 last = node.typeAnnotation
195 ? sourceCode.getTokenBefore(node.typeAnnotation)
196 : sourceCode.getLastToken(node),
197 penultimate = sourceCode.getTokenBefore(last),
198 firstElement = node.elements[0],
199 lastElement = node.elements[node.elements.length - 1];
201 const openingBracketMustBeSpaced =
202 options.objectsInArraysException && isObjectType(firstElement) ||
203 options.arraysInArraysException && isArrayType(firstElement) ||
204 options.singleElementException && node.elements.length === 1
205 ? !options.spaced : options.spaced;
207 const closingBracketMustBeSpaced =
208 options.objectsInArraysException && isObjectType(lastElement) ||
209 options.arraysInArraysException && isArrayType(lastElement) ||
210 options.singleElementException && node.elements.length === 1
211 ? !options.spaced : options.spaced;
213 if (astUtils.isTokenOnSameLine(first, second)) {
214 if (openingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(first, second)) {
215 reportRequiredBeginningSpace(node, first);
217 if (!openingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(first, second)) {
218 reportNoBeginningSpace(node, first);
222 if (first !== penultimate && astUtils.isTokenOnSameLine(penultimate, last)) {
223 if (closingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(penultimate, last)) {
224 reportRequiredEndingSpace(node, last);
226 if (!closingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(penultimate, last)) {
227 reportNoEndingSpace(node, last);
232 //--------------------------------------------------------------------------
234 //--------------------------------------------------------------------------
237 ArrayPattern: validateArraySpacing,
238 ArrayExpression: validateArraySpacing