2 * @fileoverview Disallows or enforces spaces inside of object literals.
3 * @author Jamund Ferguson
7 const astUtils
= require("./utils/ast-utils");
9 //------------------------------------------------------------------------------
11 //------------------------------------------------------------------------------
13 /** @type {import('../shared/types').Rule} */
19 description
: "enforce consistent spacing inside braces",
21 url
: "https://eslint.org/docs/rules/object-curly-spacing"
24 fixable
: "whitespace",
28 enum: ["always", "never"]
40 additionalProperties
: false
45 requireSpaceBefore
: "A space is required before '{{token}}'.",
46 requireSpaceAfter
: "A space is required after '{{token}}'.",
47 unexpectedSpaceBefore
: "There should be no space before '{{token}}'.",
48 unexpectedSpaceAfter
: "There should be no space after '{{token}}'."
53 const spaced
= context
.options
[0] === "always",
54 sourceCode
= context
.getSourceCode();
57 * Determines whether an option is set, relative to the spacing option.
58 * If spaced is "always", then check whether option is set to false.
59 * If spaced is "never", then check whether option is set to true.
60 * @param {Object} option The option to exclude.
61 * @returns {boolean} Whether or not the property is excluded.
63 function isOptionSet(option
) {
64 return context
.options
[1] ? context
.options
[1][option
] === !spaced
: false;
69 arraysInObjectsException
: isOptionSet("arraysInObjects"),
70 objectsInObjectsException
: isOptionSet("objectsInObjects")
73 //--------------------------------------------------------------------------
75 //--------------------------------------------------------------------------
78 * Reports that there shouldn't be a space after the first token
79 * @param {ASTNode} node The node to report in the event of an error.
80 * @param {Token} token The token to use for the report.
83 function reportNoBeginningSpace(node
, token
) {
84 const nextToken
= context
.getSourceCode().getTokenAfter(token
, { includeComments
: true });
88 loc
: { start
: token
.loc
.end
, end
: nextToken
.loc
.start
},
89 messageId
: "unexpectedSpaceAfter",
94 return fixer
.removeRange([token
.range
[1], nextToken
.range
[0]]);
100 * Reports that there shouldn't be a space before the last token
101 * @param {ASTNode} node The node to report in the event of an error.
102 * @param {Token} token The token to use for the report.
105 function reportNoEndingSpace(node
, token
) {
106 const previousToken
= context
.getSourceCode().getTokenBefore(token
, { includeComments
: true });
110 loc
: { start
: previousToken
.loc
.end
, end
: token
.loc
.start
},
111 messageId
: "unexpectedSpaceBefore",
116 return fixer
.removeRange([previousToken
.range
[1], token
.range
[0]]);
122 * Reports that there should be a space after the first token
123 * @param {ASTNode} node The node to report in the event of an error.
124 * @param {Token} token The token to use for the report.
127 function reportRequiredBeginningSpace(node
, token
) {
131 messageId
: "requireSpaceAfter",
136 return fixer
.insertTextAfter(token
, " ");
142 * Reports that there should be a space before the last token
143 * @param {ASTNode} node The node to report in the event of an error.
144 * @param {Token} token The token to use for the report.
147 function reportRequiredEndingSpace(node
, token
) {
151 messageId
: "requireSpaceBefore",
156 return fixer
.insertTextBefore(token
, " ");
162 * Determines if spacing in curly braces is valid.
163 * @param {ASTNode} node The AST node to check.
164 * @param {Token} first The first token to check (should be the opening brace)
165 * @param {Token} second The second token to check (should be first after the opening brace)
166 * @param {Token} penultimate The penultimate token to check (should be last before closing brace)
167 * @param {Token} last The last token to check (should be closing brace)
170 function validateBraceSpacing(node
, first
, second
, penultimate
, last
) {
171 if (astUtils
.isTokenOnSameLine(first
, second
)) {
172 const firstSpaced
= sourceCode
.isSpaceBetweenTokens(first
, second
);
174 if (options
.spaced
&& !firstSpaced
) {
175 reportRequiredBeginningSpace(node
, first
);
177 if (!options
.spaced
&& firstSpaced
&& second
.type
!== "Line") {
178 reportNoBeginningSpace(node
, first
);
182 if (astUtils
.isTokenOnSameLine(penultimate
, last
)) {
183 const shouldCheckPenultimate
= (
184 options
.arraysInObjectsException
&& astUtils
.isClosingBracketToken(penultimate
) ||
185 options
.objectsInObjectsException
&& astUtils
.isClosingBraceToken(penultimate
)
187 const penultimateType
= shouldCheckPenultimate
&& sourceCode
.getNodeByRangeIndex(penultimate
.range
[0]).type
;
189 const closingCurlyBraceMustBeSpaced
= (
190 options
.arraysInObjectsException
&& penultimateType
=== "ArrayExpression" ||
191 options
.objectsInObjectsException
&& (penultimateType
=== "ObjectExpression" || penultimateType
=== "ObjectPattern")
192 ) ? !options
.spaced
: options
.spaced
;
194 const lastSpaced
= sourceCode
.isSpaceBetweenTokens(penultimate
, last
);
196 if (closingCurlyBraceMustBeSpaced
&& !lastSpaced
) {
197 reportRequiredEndingSpace(node
, last
);
199 if (!closingCurlyBraceMustBeSpaced
&& lastSpaced
) {
200 reportNoEndingSpace(node
, last
);
206 * Gets '}' token of an object node.
208 * Because the last token of object patterns might be a type annotation,
209 * this traverses tokens preceded by the last property, then returns the
211 * @param {ASTNode} node The node to get. This node is an
212 * ObjectExpression or an ObjectPattern. And this node has one or
214 * @returns {Token} '}' token.
216 function getClosingBraceOfObject(node
) {
217 const lastProperty
= node
.properties
[node
.properties
.length
- 1];
219 return sourceCode
.getTokenAfter(lastProperty
, astUtils
.isClosingBraceToken
);
223 * Reports a given object node if spacing in curly braces is invalid.
224 * @param {ASTNode} node An ObjectExpression or ObjectPattern node to check.
227 function checkForObject(node
) {
228 if (node
.properties
.length
=== 0) {
232 const first
= sourceCode
.getFirstToken(node
),
233 last
= getClosingBraceOfObject(node
),
234 second
= sourceCode
.getTokenAfter(first
, { includeComments
: true }),
235 penultimate
= sourceCode
.getTokenBefore(last
, { includeComments
: true });
237 validateBraceSpacing(node
, first
, second
, penultimate
, last
);
241 * Reports a given import node if spacing in curly braces is invalid.
242 * @param {ASTNode} node An ImportDeclaration node to check.
245 function checkForImport(node
) {
246 if (node
.specifiers
.length
=== 0) {
250 let firstSpecifier
= node
.specifiers
[0];
251 const lastSpecifier
= node
.specifiers
[node
.specifiers
.length
- 1];
253 if (lastSpecifier
.type
!== "ImportSpecifier") {
256 if (firstSpecifier
.type
!== "ImportSpecifier") {
257 firstSpecifier
= node
.specifiers
[1];
260 const first
= sourceCode
.getTokenBefore(firstSpecifier
),
261 last
= sourceCode
.getTokenAfter(lastSpecifier
, astUtils
.isNotCommaToken
),
262 second
= sourceCode
.getTokenAfter(first
, { includeComments
: true }),
263 penultimate
= sourceCode
.getTokenBefore(last
, { includeComments
: true });
265 validateBraceSpacing(node
, first
, second
, penultimate
, last
);
269 * Reports a given export node if spacing in curly braces is invalid.
270 * @param {ASTNode} node An ExportNamedDeclaration node to check.
273 function checkForExport(node
) {
274 if (node
.specifiers
.length
=== 0) {
278 const firstSpecifier
= node
.specifiers
[0],
279 lastSpecifier
= node
.specifiers
[node
.specifiers
.length
- 1],
280 first
= sourceCode
.getTokenBefore(firstSpecifier
),
281 last
= sourceCode
.getTokenAfter(lastSpecifier
, astUtils
.isNotCommaToken
),
282 second
= sourceCode
.getTokenAfter(first
, { includeComments
: true }),
283 penultimate
= sourceCode
.getTokenBefore(last
, { includeComments
: true });
285 validateBraceSpacing(node
, first
, second
, penultimate
, last
);
288 //--------------------------------------------------------------------------
290 //--------------------------------------------------------------------------
295 ObjectPattern
: checkForObject
,
298 ObjectExpression
: checkForObject
,
300 // import {y} from 'x';
301 ImportDeclaration
: checkForImport
,
303 // export {name} from 'yo';
304 ExportNamedDeclaration
: checkForExport