refactor: printer (#4501)

master
Evilebot Tnawi 2018-05-22 21:10:24 +03:00 committed by GitHub
parent 0b8a8d84f6
commit 7a8dc82065
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 230 additions and 172 deletions

View File

@ -39,19 +39,30 @@ const isSCSSControlDirectiveNode = utils.isSCSSControlDirectiveNode;
const isDetachedRulesetDeclarationNode = utils.isDetachedRulesetDeclarationNode;
const isRelationalOperatorNode = utils.isRelationalOperatorNode;
const isEqualityOperatorNode = utils.isEqualityOperatorNode;
const isMultiplicationNode = utils.isMultiplicationNode;
const isDivisionNode = utils.isDivisionNode;
const isAdditionNode = utils.isAdditionNode;
const isMathOperatorNode = utils.isMathOperatorNode;
const isEachKeywordNode = utils.isEachKeywordNode;
const isParenGroupNode = utils.isParenGroupNode;
const isForKeywordNode = utils.isForKeywordNode;
const isURLFunctionNode = utils.isURLFunctionNode;
const isIfElseKeywordNode = utils.isIfElseKeywordNode;
const hasLessExtendValueNode = utils.hasLessExtendValueNode;
const hasComposesValueNode = utils.hasComposesValueNode;
const hasParensAroundValueNode = utils.hasParensAroundValueNode;
const hasLessExtendNode = utils.hasLessExtendNode;
const hasComposesNode = utils.hasComposesNode;
const hasParensAroundNode = utils.hasParensAroundNode;
const hasEmptyRawBefore = utils.hasEmptyRawBefore;
const isKeyValuePairNode = utils.isKeyValuePairNode;
const isDetachedRulesetCallNode = utils.isDetachedRulesetCallNode;
const isPostcssSimpleVarNode = utils.isPostcssSimpleVarNode;
const isSCSSMapItemNode = utils.isSCSSMapItemNode;
const isInlineValueCommentNode = utils.isInlineValueCommentNode;
const isHashNode = utils.isHashNode;
const isLeftCurlyBraceNode = utils.isLeftCurlyBraceNode;
const isRightCurlyBraceNode = utils.isRightCurlyBraceNode;
const isWordNode = utils.isWordNode;
const isColonNode = utils.isColonNode;
const isMediaAndSupportsKeywords = utils.isMediaAndSupportsKeywords;
function shouldPrintComma(options) {
switch (options.trailingComma) {
@ -135,8 +146,8 @@ function genericPrint(path, options, print) {
// it will put a space after `:` and break it. Ideally we should parse
// less files with less, but we can hardcode this to work with scss as
// well.
hasLessExtendValueNode(node) ? "" : " ",
hasComposesValueNode(node)
hasLessExtendNode(node) ? "" : " ",
hasComposesNode(node)
? removeLines(path.call(print, "value"))
: path.call(print, "value"),
node.raws.important
@ -190,7 +201,7 @@ function genericPrint(path, options, print) {
" ",
path.call(print, "value"),
isSCSSControlDirectiveNode(node)
? hasParensAroundValueNode(node)
? hasParensAroundNode(node)
? " "
: line
: ""
@ -268,25 +279,26 @@ function genericPrint(path, options, print) {
}
// postcss-selector-parser
case "selector-root": {
const atRuleAncestorNode = getAncestorNode(path, "css-atrule");
const insideAtRuleNode =
atRuleAncestorNode &&
["extend", "custom-selector", "nest"].indexOf(
atRuleAncestorNode.name
) !== -1;
return group(
concat([
atRuleAncestorNode && atRuleAncestorNode.name === "custom-selector"
? concat([atRuleAncestorNode.customSelector, line])
insideAtRuleNode(path, "custom-selector")
? concat([getAncestorNode(path, "css-atrule").customSelector, line])
: "",
join(
concat([",", insideAtRuleNode ? line : hardline]),
concat([
",",
insideAtRuleNode(path, ["extend", "custom-selector", "nest"])
? line
: hardline
]),
path.map(print, "nodes")
)
])
);
}
case "selector-selector": {
return group(indent(concat(path.map(print, "nodes"))));
}
case "selector-comment": {
return node.value;
}
@ -349,11 +361,14 @@ function genericPrint(path, options, print) {
parentNode.nodes[0] === node
? ""
: line;
return concat([leading, node.value, isLastNode(path, node) ? "" : " "]);
}
const leading = node.value.trim().startsWith("(") ? line : "";
const value =
adjustNumbers(adjustStrings(node.value.trim(), options)) || line;
return concat([leading, value]);
}
case "selector-universal": {
@ -364,9 +379,6 @@ function genericPrint(path, options, print) {
adjustNumbers(node.value)
]);
}
case "selector-selector": {
return group(indent(concat(path.map(print, "nodes"))));
}
case "selector-pseudo": {
return concat([
maybeToLowerCase(node.value),
@ -391,6 +403,7 @@ function genericPrint(path, options, print) {
return node.value;
}
// postcss-values-parser
case "value-value":
case "value-root": {
return path.call(print, "group");
}
@ -458,8 +471,8 @@ function genericPrint(path, options, print) {
continue;
}
// Ignore colon
if (iNode.value === ":") {
// Ignore colon (i.e. `:`)
if (isColonNode(iNode) || isColonNode(iNextNode)) {
continue;
}
@ -483,166 +496,142 @@ function genericPrint(path, options, print) {
continue;
}
// Ignore spaces after `#` and after `{` and before `}` in SCSS interpolation (i.e. `#{variable}`)
if (
(iPrevNode &&
iPrevNode.type === "value-comment" &&
iPrevNode.inline) ||
(iNextNode.type === "value-comment" && iNextNode.inline)
) {
continue;
}
const isHash = iNode.type === "value-word" && iNode.value === "#";
const isLeftCurlyBrace =
iNode.type === "value-word" && iNode.value === "{";
const isNextLeftCurlyBrace =
iNextNode.type === "value-word" && iNextNode.value === "{";
const isRightCurlyBrace =
iNode.type === "value-word" && iNode.value === "}";
const isNextRightCurlyBrace =
iNextNode.type === "value-word" && iNextNode.value === "}";
// Ignore interpolation in SCSS (i.e. `#{variable}`)
if (
isHash ||
isLeftCurlyBrace ||
isNextRightCurlyBrace ||
(isNextLeftCurlyBrace &&
iNextNode.raws &&
iNextNode.raws.before === "") ||
(isRightCurlyBrace && iNextNode.raws && iNextNode.raws.before === "")
isHashNode(iNode) ||
isLeftCurlyBraceNode(iNode) ||
isRightCurlyBraceNode(iNextNode) ||
(isLeftCurlyBraceNode(iNextNode) && hasEmptyRawBefore(iNextNode)) ||
(isRightCurlyBraceNode(iNode) && hasEmptyRawBefore(iNextNode))
) {
continue;
}
// Ignore css variables and interpolation in SCSS (i.e. `--#{$var}`)
if (iNode.value === "--" && iNextNode.value === "#") {
if (iNode.value === "--" && isHashNode(iNextNode)) {
continue;
}
const isNextHash =
iNextNode.type === "value-word" && iNextNode.value === "#";
const isMathOperator = isMathOperatorNode(iNode);
const isNextMathOperator = isMathOperatorNode(iNextNode);
const isMultiplication =
!isNextHash && isMathOperator && iNode.value === "*";
const isNextMultiplication =
!isRightCurlyBrace && isNextMathOperator && iNextNode.value === "*";
// Formating math operations
if (isMathOperator || isNextMathOperator) {
const isMultiplication =
!isHashNode(iNextNode) && isMultiplicationNode(iNode);
const isNextMultiplication =
!isRightCurlyBraceNode(iNode) && isMultiplicationNode(iNextNode);
const isDivision = !isHashNode(iNextNode) && isDivisionNode(iNode);
const isNextDivision =
!isRightCurlyBraceNode(iNode) && isDivisionNode(iNextNode);
const isAddition = !isHashNode(iNextNode) && isAdditionNode(iNode);
const isNextAddition =
!isRightCurlyBraceNode(iNode) && isAdditionNode(iNextNode);
const isDivision = !isNextHash && isMathOperator && iNode.value === "/";
const isNextDivision =
!isRightCurlyBrace && isNextMathOperator && iNextNode.value === "/";
const isAddition = !isNextHash && isMathOperator && iNode.value === "+";
const isNextAddition =
!isRightCurlyBrace && isNextMathOperator && iNextNode.value === "+";
const isPrevFunction = iPrevNode && iPrevNode.type === "value-func";
const isFunction = iNode.type === "value-func";
const isNextFunction = iNextNode.type === "value-func";
const isNextNextFunction =
iNextNextNode && iNextNextNode.type === "value-func";
const isPrevWord =
iPrevNode &&
["value-word", "value-atword"].indexOf(iPrevNode.type) !== -1;
const isWord =
["value-word", "value-atword"].indexOf(iNode.type) !== -1;
const isNextWord =
["value-word", "value-atword"].indexOf(iNextNode.type) !== -1;
const isNextNextWord =
iNextNextNode &&
["value-word", "value-atword"].indexOf(iNextNextNode.type) !== -1;
// Math operators
const insideCalcFunction = insideValueFunctionNode(path, "calc");
const hasSpaceBeforeOperator =
isNextNextFunction || isNextNextWord || isFunction || isWord;
const hasSpaceAfterOperator =
isNextFunction || isNextWord || isPrevFunction || isPrevWord;
if (
(isMathOperator || isNextMathOperator) &&
// Multiplication
!isMultiplication &&
!isNextMultiplication &&
// Division
!(isNextDivision && (hasSpaceBeforeOperator || insideCalcFunction)) &&
!(isDivision && (hasSpaceAfterOperator || insideCalcFunction)) &&
// Addition
!(isNextAddition && hasSpaceBeforeOperator) &&
!(isAddition && hasSpaceAfterOperator)
) {
const isNextParenGroup = isParenGroupNode(iNextNode);
const isNextValueNumber = iNextNode.type === "value-number";
const hasSpaceBeforeOperator =
(iNextNextNode && iNextNextNode.type === "value-func") ||
(iNextNextNode && isWordNode(iNextNextNode)) ||
iNode.type === "value-func" ||
isWordNode(iNode);
const hasSpaceAfterOperator =
iNextNode.type === "value-func" ||
isWordNode(iNextNode) ||
(iPrevNode && iPrevNode.type === "value-func") ||
(iPrevNode && isWordNode(iPrevNode));
const insideCalcFunction = insideValueFunctionNode(path, "calc");
if (
(iNextNode.raws && iNextNode.raws.before === "") ||
(isMathOperator &&
(isNextParenGroup ||
isNextWord ||
isNextValueNumber ||
isMathOperatorNode(iNextNode)) &&
(!iPrevNode || (iPrevNode && isMathOperatorNode(iPrevNode))))
// Multiplication
!isMultiplication &&
!isNextMultiplication &&
// Division
!(
isNextDivision &&
(hasSpaceBeforeOperator || insideCalcFunction)
) &&
!(isDivision && (hasSpaceAfterOperator || insideCalcFunction)) &&
// Addition
!(isNextAddition && hasSpaceBeforeOperator) &&
!(isAddition && hasSpaceAfterOperator)
) {
continue;
if (
hasEmptyRawBefore(iNextNode) ||
(isMathOperator &&
(isParenGroupNode(iNextNode) ||
isWordNode(iNextNode) ||
iNextNode.type === "value-number" ||
isMathOperatorNode(iNextNode)) &&
(!iPrevNode || (iPrevNode && isMathOperatorNode(iPrevNode))))
) {
continue;
}
}
}
const isEqualityOperator =
isControlDirective && isEqualityOperatorNode(iNode);
const isRelationalOperator =
isControlDirective && isRelationalOperatorNode(iNode);
const isNextEqualityOperator =
isControlDirective && isEqualityOperatorNode(iNextNode);
const isNextRelationalOperator =
isControlDirective && isRelationalOperatorNode(iNextNode);
const isNextIfElseKeyword =
isControlDirective && isIfElseKeywordNode(iNextNode);
const isEachKeyword = isControlDirective && isEachKeywordNode(iNode);
const isNextEachKeyword =
isControlDirective && isEachKeywordNode(iNextNode);
const isForKeyword =
atRuleAncestorNode &&
atRuleAncestorNode.name === "for" &&
isForKeywordNode(iNode);
const isNextForKeyword =
isControlDirective && isForKeywordNode(iNextNode);
const IsNextColon = iNextNode.value === ":";
// Ignore inline comment, they already contain newline at end (i.e. `// Comment`)
// Add `hardline` after inline comment (i.e. `// comment\n foo: bar;`)
const isInlineComment = isInlineValueCommentNode(iNode);
if (
(iPrevNode && isInlineValueCommentNode(iPrevNode)) ||
isInlineComment ||
isInlineValueCommentNode(iNextNode)
) {
if (isInlineComment) {
parts.push(hardline);
}
continue;
}
// Handle keywords in SCSS control directive
if (
isControlDirective &&
(isEqualityOperatorNode(iNextNode) ||
isRelationalOperatorNode(iNextNode) ||
isIfElseKeywordNode(iNextNode) ||
isEachKeywordNode(iNode) ||
isForKeywordNode(iNode))
) {
parts.push(" ");
continue;
}
// At-rule `namespace` should be in one line
if (
atRuleAncestorNode &&
atRuleAncestorNode.name.toLowerCase() === "namespace"
) {
parts.push(" ");
continue;
}
// Formatting `grid` property
if (isGridValue) {
if (iNode.source.start.line !== iNextNode.source.start.line) {
parts.push(hardline);
didBreak = true;
} else {
parts.push(" ");
}
} else if (iNode.type === "value-comment" && iNode.inline) {
parts.push(hardline);
} else if (
isNextMathOperator ||
isNextEqualityOperator ||
isNextRelationalOperator ||
isNextIfElseKeyword ||
isForKeyword ||
isEachKeyword ||
(atRuleAncestorNode &&
atRuleAncestorNode.name.toLowerCase() === "namespace")
) {
parts.push(" ");
} else if (
!IsNextColon ||
isEqualityOperator ||
isRelationalOperator ||
isNextForKeyword ||
isNextEachKeyword
) {
parts.push(line);
continue;
}
// Add `space` before next math operation
// Note: `grip` property have `/` delimiter and it is not math operation, so
// `grid` property handles above
if (isNextMathOperator) {
parts.push(" ");
continue;
}
// Be default all values go through `line`
parts.push(line);
}
if (didBreak) {
@ -693,6 +682,7 @@ function genericPrint(path, options, print) {
}
res.push(printed[i]);
}
return group(indent(fill(res)));
}
@ -745,17 +735,12 @@ function genericPrint(path, options, print) {
}
);
}
case "value-value": {
return path.call(print, "group");
}
case "value-func": {
const insideAtRuleSupportsNode = insideAtRuleNode(path, "supports");
const isKeyword =
["not", "and", "or"].indexOf(node.value.toLowerCase()) !== -1;
return concat([
node.value,
insideAtRuleSupportsNode && isKeyword ? " " : "",
insideAtRuleNode(path, "supports") && isMediaAndSupportsKeywords(node)
? " "
: "",
path.call(print, "group")
]);
}
@ -778,6 +763,7 @@ function genericPrint(path, options, print) {
case "value-colon": {
return concat([
node.value,
// Don't add spaces on `:` in `url` function (i.e. `url(fbglyph: cross-outline, fig-white)`)
insideValueFunctionNode(path, "url") ? "" : line
]);
}

View File

@ -90,11 +90,13 @@ function insideICSSRuleNode(path) {
);
}
function insideAtRuleNode(path, atRuleName) {
function insideAtRuleNode(path, atRuleNameOrAtRuleNames) {
const atRuleNames = [].concat(atRuleNameOrAtRuleNames);
const atRuleAncestorNode = getAncestorNode(path, "css-atrule");
return (
atRuleAncestorNode && atRuleAncestorNode.name.toLowerCase() === atRuleName
atRuleAncestorNode &&
atRuleNames.indexOf(atRuleAncestorNode.name.toLowerCase()) !== -1
);
}
@ -163,10 +165,33 @@ function isEachKeywordNode(node) {
return node.type === "value-word" && node.value === "in";
}
function isMultiplicationNode(node) {
return node.type === "value-operator" && node.value === "*";
}
function isDivisionNode(node) {
return node.type === "value-operator" && node.value === "/";
}
function isAdditionNode(node) {
return node.type === "value-operator" && node.value === "+";
}
function isSubtractionNode(node) {
return node.type === "value-operator" && node.value === "-";
}
function isModuloNode(node) {
return node.type === "value-operator" && node.value === "%";
}
function isMathOperatorNode(node) {
return (
node.type === "value-operator" &&
["+", "-", "/", "*", "%"].indexOf(node.value) !== -1
isMultiplicationNode(node) ||
isDivisionNode(node) ||
isAdditionNode(node) ||
isSubtractionNode(node) ||
isModuloNode(node)
);
}
@ -214,7 +239,7 @@ function isPostcssSimpleVarNode(currentNode, nextNode) {
);
}
function hasLessExtendValueNode(node) {
function hasLessExtendNode(node) {
return (
node.value &&
node.value.type === "value-root" &&
@ -226,7 +251,7 @@ function hasLessExtendValueNode(node) {
);
}
function hasComposesValueNode(node) {
function hasComposesNode(node) {
return (
node.value &&
node.value.type === "value-root" &&
@ -236,7 +261,7 @@ function hasComposesValueNode(node) {
);
}
function hasParensAroundValueNode(node) {
function hasParensAroundNode(node) {
return (
node.value &&
node.value.group &&
@ -247,6 +272,10 @@ function hasParensAroundValueNode(node) {
);
}
function hasEmptyRawBefore(node) {
return node.raws && node.raws.before === "";
}
function isKeyValuePairNode(node) {
return (
node.type === "value-comma_group" &&
@ -303,6 +332,36 @@ function isSCSSMapItemNode(path) {
return false;
}
function isInlineValueCommentNode(node) {
return node.type === "value-comment" && node.inline;
}
function isHashNode(node) {
return node.type === "value-word" && node.value === "#";
}
function isLeftCurlyBraceNode(node) {
return node.type === "value-word" && node.value === "{";
}
function isRightCurlyBraceNode(node) {
return node.type === "value-word" && node.value === "}";
}
function isWordNode(node) {
return ["value-word", "value-atword"].indexOf(node.type) !== -1;
}
function isColonNode(node) {
return node.type === "value-colon";
}
function isMediaAndSupportsKeywords(node) {
return (
node.value && ["not", "and", "or"].indexOf(node.value.toLowerCase()) !== -1
);
}
module.exports = {
getAncestorCounter,
getAncestorNode,
@ -321,19 +380,32 @@ module.exports = {
isDetachedRulesetDeclarationNode,
isRelationalOperatorNode,
isEqualityOperatorNode,
isMultiplicationNode,
isDivisionNode,
isAdditionNode,
isSubtractionNode,
isModuloNode,
isMathOperatorNode,
isEachKeywordNode,
isParenGroupNode,
isForKeywordNode,
isURLFunctionNode,
isIfElseKeywordNode,
hasLessExtendValueNode,
hasComposesValueNode,
hasParensAroundValueNode,
hasLessExtendNode,
hasComposesNode,
hasParensAroundNode,
hasEmptyRawBefore,
isSCSSNestedPropertyNode,
isDetachedRulesetCallNode,
isPostcssSimpleVarNode,
isKeyValuePairNode,
isKeyValuePairInParenGroupNode,
isSCSSMapItemNode
isSCSSMapItemNode,
isInlineValueCommentNode,
isHashNode,
isLeftCurlyBraceNode,
isRightCurlyBraceNode,
isWordNode,
isColonNode,
isMediaAndSupportsKeywords
};