254 lines
9.9 KiB
Plaintext
254 lines
9.9 KiB
Plaintext
import { f as createRule, g as import_ast_utils, m as AST_NODE_TYPES } from "../utils.js";
|
|
var object_curly_spacing_default = createRule({
|
|
name: "object-curly-spacing",
|
|
meta: {
|
|
type: "layout",
|
|
docs: { description: "Enforce consistent spacing inside braces" },
|
|
fixable: "whitespace",
|
|
schema: [{
|
|
type: "string",
|
|
enum: ["always", "never"]
|
|
}, {
|
|
type: "object",
|
|
properties: {
|
|
arraysInObjects: { type: "boolean" },
|
|
objectsInObjects: { type: "boolean" },
|
|
overrides: {
|
|
type: "object",
|
|
properties: Object.fromEntries([
|
|
"ObjectPattern",
|
|
"ObjectExpression",
|
|
"ImportDeclaration",
|
|
"ImportAttributes",
|
|
"ExportNamedDeclaration",
|
|
"ExportAllDeclaration",
|
|
"TSMappedType",
|
|
"TSTypeLiteral",
|
|
"TSInterfaceBody",
|
|
"TSEnumBody"
|
|
].map((node) => [node, {
|
|
type: "string",
|
|
enum: ["always", "never"]
|
|
}])),
|
|
additionalProperties: false
|
|
},
|
|
emptyObjects: {
|
|
type: "string",
|
|
enum: [
|
|
"ignore",
|
|
"always",
|
|
"never"
|
|
]
|
|
}
|
|
},
|
|
additionalProperties: false
|
|
}],
|
|
defaultOptions: ["never"],
|
|
messages: {
|
|
requireSpaceBefore: "A space is required before '{{token}}'.",
|
|
requireSpaceAfter: "A space is required after '{{token}}'.",
|
|
unexpectedSpaceBefore: "There should be no space before '{{token}}'.",
|
|
unexpectedSpaceAfter: "There should be no space after '{{token}}'.",
|
|
requiredSpaceInEmptyObject: "A space is required in empty '{{node}}'.",
|
|
unexpectedSpaceInEmptyObject: "There should be no space in empty '{{node}}'."
|
|
}
|
|
},
|
|
create(context, [firstOption, secondOption]) {
|
|
const spaced = firstOption === "always";
|
|
const sourceCode = context.sourceCode;
|
|
function isOptionSet(option) {
|
|
return secondOption ? secondOption[option] === !spaced : false;
|
|
}
|
|
const options = {
|
|
spaced,
|
|
arraysInObjectsException: isOptionSet("arraysInObjects"),
|
|
objectsInObjectsException: isOptionSet("objectsInObjects"),
|
|
overrides: secondOption?.overrides ?? {},
|
|
emptyObjects: secondOption?.emptyObjects ?? "ignore"
|
|
};
|
|
function reportNoBeginningSpace(node, token) {
|
|
const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
|
|
context.report({
|
|
node,
|
|
loc: {
|
|
start: token.loc.end,
|
|
end: nextToken.loc.start
|
|
},
|
|
messageId: "unexpectedSpaceAfter",
|
|
data: { token: token.value },
|
|
fix(fixer) {
|
|
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
|
}
|
|
});
|
|
}
|
|
function reportNoEndingSpace(node, token) {
|
|
const previousToken = sourceCode.getTokenBefore(token, { includeComments: true });
|
|
context.report({
|
|
node,
|
|
loc: {
|
|
start: previousToken.loc.end,
|
|
end: token.loc.start
|
|
},
|
|
messageId: "unexpectedSpaceBefore",
|
|
data: { token: token.value },
|
|
fix(fixer) {
|
|
return fixer.removeRange([previousToken.range[1], token.range[0]]);
|
|
}
|
|
});
|
|
}
|
|
function reportRequiredBeginningSpace(node, token) {
|
|
context.report({
|
|
node,
|
|
loc: token.loc,
|
|
messageId: "requireSpaceAfter",
|
|
data: { token: token.value },
|
|
fix(fixer) {
|
|
return fixer.insertTextAfter(token, " ");
|
|
}
|
|
});
|
|
}
|
|
function reportRequiredEndingSpace(node, token) {
|
|
context.report({
|
|
node,
|
|
loc: token.loc,
|
|
messageId: "requireSpaceBefore",
|
|
data: { token: token.value },
|
|
fix(fixer) {
|
|
return fixer.insertTextBefore(token, " ");
|
|
}
|
|
});
|
|
}
|
|
function validateBraceSpacing(node, openingToken, closingToken, nodeType = node.type) {
|
|
const tokenAfterOpening = sourceCode.getTokenAfter(openingToken, { includeComments: true });
|
|
const spaced = options.overrides[nodeType] ? options.overrides[nodeType] === "always" : options.spaced;
|
|
if ((0, import_ast_utils.isTokenOnSameLine)(openingToken, tokenAfterOpening)) {
|
|
const firstSpaced = sourceCode.isSpaceBetween(openingToken, tokenAfterOpening);
|
|
const secondType = sourceCode.getNodeByRangeIndex(tokenAfterOpening.range[0]).type;
|
|
const openingCurlyBraceMustBeSpaced = options.arraysInObjectsException && [AST_NODE_TYPES.TSMappedType, AST_NODE_TYPES.TSIndexSignature].includes(secondType) ? !spaced : spaced;
|
|
if (openingCurlyBraceMustBeSpaced && !firstSpaced) reportRequiredBeginningSpace(node, openingToken);
|
|
if (!openingCurlyBraceMustBeSpaced && firstSpaced && !((0, import_ast_utils.isCommentToken)(tokenAfterOpening) && !(0, import_ast_utils.isTokenOnSameLine)(openingToken, closingToken))) reportNoBeginningSpace(node, openingToken);
|
|
}
|
|
const tokenBeforeClosing = sourceCode.getTokenBefore(closingToken, { includeComments: true });
|
|
if ((0, import_ast_utils.isTokenOnSameLine)(tokenBeforeClosing, closingToken)) {
|
|
const penultimateType = options.arraysInObjectsException && (0, import_ast_utils.isClosingBracketToken)(tokenBeforeClosing) || options.objectsInObjectsException && (0, import_ast_utils.isClosingBraceToken)(tokenBeforeClosing) ? sourceCode.getNodeByRangeIndex(tokenBeforeClosing.range[0]).type : void 0;
|
|
const closingCurlyBraceMustBeSpaced = options.arraysInObjectsException && [AST_NODE_TYPES.ArrayExpression, AST_NODE_TYPES.TSTupleType].includes(penultimateType) || options.objectsInObjectsException && penultimateType !== void 0 && [
|
|
AST_NODE_TYPES.ObjectExpression,
|
|
AST_NODE_TYPES.ObjectPattern,
|
|
AST_NODE_TYPES.TSMappedType,
|
|
AST_NODE_TYPES.TSTypeLiteral
|
|
].includes(penultimateType) ? !spaced : spaced;
|
|
const lastSpaced = sourceCode.isSpaceBetween(tokenBeforeClosing, closingToken);
|
|
if (closingCurlyBraceMustBeSpaced && !lastSpaced) reportRequiredEndingSpace(node, closingToken);
|
|
if (!closingCurlyBraceMustBeSpaced && lastSpaced) reportNoEndingSpace(node, closingToken);
|
|
}
|
|
}
|
|
function checkSpaceInEmptyObjectLike(node, openingToken, closingToken, nodeType = node.type) {
|
|
if (options.emptyObjects === "ignore" || !(0, import_ast_utils.isTokenOnSameLine)(openingToken, closingToken) || sourceCode.commentsExistBetween(openingToken, closingToken)) return;
|
|
const sourceBetween = sourceCode.getText().slice(openingToken.range[0] + 1, closingToken.range[1] - 1);
|
|
if (sourceBetween.trim() !== "") return;
|
|
if (options.emptyObjects === "always") {
|
|
if (sourceBetween === " ") return;
|
|
context.report({
|
|
node,
|
|
loc: {
|
|
start: openingToken.loc.end,
|
|
end: closingToken.loc.start
|
|
},
|
|
messageId: "requiredSpaceInEmptyObject",
|
|
data: { node: nodeType },
|
|
fix(fixer) {
|
|
return fixer.replaceTextRange([openingToken.range[1], closingToken.range[0]], " ");
|
|
}
|
|
});
|
|
} else if (options.emptyObjects === "never") {
|
|
if (sourceBetween === "") return;
|
|
context.report({
|
|
node,
|
|
loc: {
|
|
start: openingToken.loc.end,
|
|
end: closingToken.loc.start
|
|
},
|
|
messageId: "unexpectedSpaceInEmptyObject",
|
|
data: { node: nodeType },
|
|
fix(fixer) {
|
|
return fixer.removeRange([openingToken.range[1], closingToken.range[0]]);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function getBraceToken(node, nodeType = node.type) {
|
|
switch (nodeType) {
|
|
case "ImportDeclaration":
|
|
case "ExportNamedDeclaration":
|
|
case "ExportAllDeclaration": {
|
|
const attrTokens = sourceCode.getTokens(node);
|
|
return [attrTokens.find((token) => (0, import_ast_utils.isOpeningBraceToken)(token)), attrTokens.find((token) => (0, import_ast_utils.isClosingBraceToken)(token))];
|
|
}
|
|
case "ImportAttributes": {
|
|
const attrTokens = sourceCode.getTokens(node);
|
|
const openingAttrToken = attrTokens.findLast((token) => (0, import_ast_utils.isOpeningBraceToken)(token));
|
|
const closingAttrToken = attrTokens.findLast((token) => (0, import_ast_utils.isClosingBraceToken)(token));
|
|
if (!openingAttrToken || !closingAttrToken || !node.source || openingAttrToken.range[0] < node.source.range[0]) return [null, null];
|
|
return [openingAttrToken, closingAttrToken];
|
|
}
|
|
case "ObjectPattern":
|
|
case "ObjectExpression":
|
|
case "TSMappedType":
|
|
case "TSTypeLiteral":
|
|
case "TSInterfaceBody":
|
|
case "TSEnumBody": {
|
|
const allTokens = sourceCode.getTokens(node);
|
|
return [allTokens.find((token) => (0, import_ast_utils.isOpeningBraceToken)(token)), node.type === "ObjectPattern" && node.typeAnnotation ? sourceCode.getTokenBefore(node.typeAnnotation) : allTokens.findLast((token) => (0, import_ast_utils.isClosingBraceToken)(token))];
|
|
}
|
|
default: throw new Error(`Unsupported node type: ${nodeType}`);
|
|
}
|
|
}
|
|
function checkForObjectLike(node, properties, nodeType = node.type) {
|
|
const [openingToken, closingToken] = getBraceToken(node, nodeType);
|
|
if (!openingToken || !closingToken) return;
|
|
if (properties.length === 0) {
|
|
checkSpaceInEmptyObjectLike(node, openingToken, closingToken, nodeType);
|
|
return;
|
|
}
|
|
validateBraceSpacing(node, openingToken, closingToken, nodeType);
|
|
}
|
|
return {
|
|
ObjectPattern(node) {
|
|
checkForObjectLike(node, node.properties);
|
|
},
|
|
ObjectExpression(node) {
|
|
checkForObjectLike(node, node.properties);
|
|
},
|
|
ImportDeclaration(node) {
|
|
if (node.attributes) checkForObjectLike(node, node.attributes, "ImportAttributes");
|
|
const firstSpecifierIndex = node.specifiers.findIndex((specifier) => specifier.type === "ImportSpecifier");
|
|
if (firstSpecifierIndex === -1) {
|
|
checkForObjectLike(node, []);
|
|
return;
|
|
}
|
|
checkForObjectLike(node, node.specifiers.slice(firstSpecifierIndex));
|
|
},
|
|
ExportNamedDeclaration(node) {
|
|
checkForObjectLike(node, node.specifiers);
|
|
if (node.attributes) checkForObjectLike(node, node.attributes, "ImportAttributes");
|
|
},
|
|
ExportAllDeclaration(node) {
|
|
if (node.attributes) checkForObjectLike(node, node.attributes, "ImportAttributes");
|
|
},
|
|
TSMappedType(node) {
|
|
validateBraceSpacing(node, sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
|
|
},
|
|
TSTypeLiteral(node) {
|
|
checkForObjectLike(node, node.members);
|
|
},
|
|
TSInterfaceBody(node) {
|
|
checkForObjectLike(node, node.body);
|
|
},
|
|
TSEnumBody(node) {
|
|
checkForObjectLike(node, node.members);
|
|
}
|
|
};
|
|
}
|
|
});
|
|
export { object_curly_spacing_default as t };
|