You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
4.5 KiB

/**
* @fileoverview A rule to ensure whitespace before blocks.
* @author Mathias Schreck <https://github.com/lo1tuma>
* @copyright 2014 Mathias Schreck. All rights reserved.
*/
"use strict";
var astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var config = context.options[0],
sourceCode = context.getSourceCode(),
checkFunctions = true,
checkKeywords = true,
checkClasses = true;
if (typeof config === "object") {
checkFunctions = config.functions !== "never";
checkKeywords = config.keywords !== "never";
checkClasses = config.classes !== "never";
} else if (config === "never") {
checkFunctions = false;
checkKeywords = false;
checkClasses = false;
}
/**
* Checks whether or not a given token is an arrow operator (=>) or a keyword
* in order to avoid to conflict with `arrow-spacing` and `keyword-spacing`.
*
* @param {Token} token - A token to check.
* @returns {boolean} `true` if the token is an arrow operator.
*/
function isConflicted(token) {
return (token.type === "Punctuator" && token.value === "=>") || token.type === "Keyword";
}
/**
* Checks the given BlockStatement node has a preceding space if it doesnt start on a new line.
* @param {ASTNode|Token} node The AST node of a BlockStatement.
* @returns {void} undefined.
*/
function checkPrecedingSpace(node) {
var precedingToken = context.getTokenBefore(node),
hasSpace,
parent,
requireSpace;
if (precedingToken && !isConflicted(precedingToken) && astUtils.isTokenOnSameLine(precedingToken, node)) {
hasSpace = sourceCode.isSpaceBetweenTokens(precedingToken, node);
parent = context.getAncestors().pop();
if (parent.type === "FunctionExpression" || parent.type === "FunctionDeclaration") {
requireSpace = checkFunctions;
} else if (node.type === "ClassBody") {
requireSpace = checkClasses;
} else {
requireSpace = checkKeywords;
}
if (requireSpace) {
if (!hasSpace) {
context.report({
node: node,
message: "Missing space before opening brace.",
fix: function(fixer) {
return fixer.insertTextBefore(node, " ");
}
});
}
} else {
if (hasSpace) {
context.report({
node: node,
message: "Unexpected space before opening brace.",
fix: function(fixer) {
return fixer.removeRange([precedingToken.range[1], node.range[0]]);
}
});
}
}
}
}
/**
* Checks if the CaseBlock of an given SwitchStatement node has a preceding space.
* @param {ASTNode} node The node of a SwitchStatement.
* @returns {void} undefined.
*/
function checkSpaceBeforeCaseBlock(node) {
var cases = node.cases,
firstCase,
openingBrace;
if (cases.length > 0) {
firstCase = cases[0];
openingBrace = context.getTokenBefore(firstCase);
} else {
openingBrace = context.getLastToken(node, 1);
}
checkPrecedingSpace(openingBrace);
}
return {
"BlockStatement": checkPrecedingSpace,
"ClassBody": checkPrecedingSpace,
"SwitchStatement": checkSpaceBeforeCaseBlock
};
};
module.exports.schema = [
{
"oneOf": [
{
"enum": ["always", "never"]
},
{
"type": "object",
"properties": {
"keywords": {
"enum": ["always", "never"]
},
"functions": {
"enum": ["always", "never"]
},
"classes": {
"enum": ["always", "never"]
}
},
"additionalProperties": false
}
]
}
];