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 isDetachedRulesetDeclarationNode = utils.isDetachedRulesetDeclarationNode;
const isRelationalOperatorNode = utils.isRelationalOperatorNode; const isRelationalOperatorNode = utils.isRelationalOperatorNode;
const isEqualityOperatorNode = utils.isEqualityOperatorNode; const isEqualityOperatorNode = utils.isEqualityOperatorNode;
const isMultiplicationNode = utils.isMultiplicationNode;
const isDivisionNode = utils.isDivisionNode;
const isAdditionNode = utils.isAdditionNode;
const isMathOperatorNode = utils.isMathOperatorNode; const isMathOperatorNode = utils.isMathOperatorNode;
const isEachKeywordNode = utils.isEachKeywordNode; const isEachKeywordNode = utils.isEachKeywordNode;
const isParenGroupNode = utils.isParenGroupNode; const isParenGroupNode = utils.isParenGroupNode;
const isForKeywordNode = utils.isForKeywordNode; const isForKeywordNode = utils.isForKeywordNode;
const isURLFunctionNode = utils.isURLFunctionNode; const isURLFunctionNode = utils.isURLFunctionNode;
const isIfElseKeywordNode = utils.isIfElseKeywordNode; const isIfElseKeywordNode = utils.isIfElseKeywordNode;
const hasLessExtendValueNode = utils.hasLessExtendValueNode; const hasLessExtendNode = utils.hasLessExtendNode;
const hasComposesValueNode = utils.hasComposesValueNode; const hasComposesNode = utils.hasComposesNode;
const hasParensAroundValueNode = utils.hasParensAroundValueNode; const hasParensAroundNode = utils.hasParensAroundNode;
const hasEmptyRawBefore = utils.hasEmptyRawBefore;
const isKeyValuePairNode = utils.isKeyValuePairNode; const isKeyValuePairNode = utils.isKeyValuePairNode;
const isDetachedRulesetCallNode = utils.isDetachedRulesetCallNode; const isDetachedRulesetCallNode = utils.isDetachedRulesetCallNode;
const isPostcssSimpleVarNode = utils.isPostcssSimpleVarNode; const isPostcssSimpleVarNode = utils.isPostcssSimpleVarNode;
const isSCSSMapItemNode = utils.isSCSSMapItemNode; 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) { function shouldPrintComma(options) {
switch (options.trailingComma) { 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 // 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 // less files with less, but we can hardcode this to work with scss as
// well. // well.
hasLessExtendValueNode(node) ? "" : " ", hasLessExtendNode(node) ? "" : " ",
hasComposesValueNode(node) hasComposesNode(node)
? removeLines(path.call(print, "value")) ? removeLines(path.call(print, "value"))
: path.call(print, "value"), : path.call(print, "value"),
node.raws.important node.raws.important
@ -190,7 +201,7 @@ function genericPrint(path, options, print) {
" ", " ",
path.call(print, "value"), path.call(print, "value"),
isSCSSControlDirectiveNode(node) isSCSSControlDirectiveNode(node)
? hasParensAroundValueNode(node) ? hasParensAroundNode(node)
? " " ? " "
: line : line
: "" : ""
@ -268,25 +279,26 @@ function genericPrint(path, options, print) {
} }
// postcss-selector-parser // postcss-selector-parser
case "selector-root": { case "selector-root": {
const atRuleAncestorNode = getAncestorNode(path, "css-atrule");
const insideAtRuleNode =
atRuleAncestorNode &&
["extend", "custom-selector", "nest"].indexOf(
atRuleAncestorNode.name
) !== -1;
return group( return group(
concat([ concat([
atRuleAncestorNode && atRuleAncestorNode.name === "custom-selector" insideAtRuleNode(path, "custom-selector")
? concat([atRuleAncestorNode.customSelector, line]) ? concat([getAncestorNode(path, "css-atrule").customSelector, line])
: "", : "",
join( join(
concat([",", insideAtRuleNode ? line : hardline]), concat([
",",
insideAtRuleNode(path, ["extend", "custom-selector", "nest"])
? line
: hardline
]),
path.map(print, "nodes") path.map(print, "nodes")
) )
]) ])
); );
} }
case "selector-selector": {
return group(indent(concat(path.map(print, "nodes"))));
}
case "selector-comment": { case "selector-comment": {
return node.value; return node.value;
} }
@ -349,11 +361,14 @@ function genericPrint(path, options, print) {
parentNode.nodes[0] === node parentNode.nodes[0] === node
? "" ? ""
: line; : line;
return concat([leading, node.value, isLastNode(path, node) ? "" : " "]); return concat([leading, node.value, isLastNode(path, node) ? "" : " "]);
} }
const leading = node.value.trim().startsWith("(") ? line : ""; const leading = node.value.trim().startsWith("(") ? line : "";
const value = const value =
adjustNumbers(adjustStrings(node.value.trim(), options)) || line; adjustNumbers(adjustStrings(node.value.trim(), options)) || line;
return concat([leading, value]); return concat([leading, value]);
} }
case "selector-universal": { case "selector-universal": {
@ -364,9 +379,6 @@ function genericPrint(path, options, print) {
adjustNumbers(node.value) adjustNumbers(node.value)
]); ]);
} }
case "selector-selector": {
return group(indent(concat(path.map(print, "nodes"))));
}
case "selector-pseudo": { case "selector-pseudo": {
return concat([ return concat([
maybeToLowerCase(node.value), maybeToLowerCase(node.value),
@ -391,6 +403,7 @@ function genericPrint(path, options, print) {
return node.value; return node.value;
} }
// postcss-values-parser // postcss-values-parser
case "value-value":
case "value-root": { case "value-root": {
return path.call(print, "group"); return path.call(print, "group");
} }
@ -458,8 +471,8 @@ function genericPrint(path, options, print) {
continue; continue;
} }
// Ignore colon // Ignore colon (i.e. `:`)
if (iNode.value === ":") { if (isColonNode(iNode) || isColonNode(iNextNode)) {
continue; continue;
} }
@ -483,166 +496,142 @@ function genericPrint(path, options, print) {
continue; continue;
} }
// Ignore spaces after `#` and after `{` and before `}` in SCSS interpolation (i.e. `#{variable}`)
if ( if (
(iPrevNode && isHashNode(iNode) ||
iPrevNode.type === "value-comment" && isLeftCurlyBraceNode(iNode) ||
iPrevNode.inline) || isRightCurlyBraceNode(iNextNode) ||
(iNextNode.type === "value-comment" && iNextNode.inline) (isLeftCurlyBraceNode(iNextNode) && hasEmptyRawBefore(iNextNode)) ||
) { (isRightCurlyBraceNode(iNode) && hasEmptyRawBefore(iNextNode))
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 === "")
) { ) {
continue; continue;
} }
// Ignore css variables and interpolation in SCSS (i.e. `--#{$var}`) // Ignore css variables and interpolation in SCSS (i.e. `--#{$var}`)
if (iNode.value === "--" && iNextNode.value === "#") { if (iNode.value === "--" && isHashNode(iNextNode)) {
continue; continue;
} }
const isNextHash =
iNextNode.type === "value-word" && iNextNode.value === "#";
const isMathOperator = isMathOperatorNode(iNode); const isMathOperator = isMathOperatorNode(iNode);
const isNextMathOperator = isMathOperatorNode(iNextNode); const isNextMathOperator = isMathOperatorNode(iNextNode);
// Formating math operations
if (isMathOperator || isNextMathOperator) {
const isMultiplication = const isMultiplication =
!isNextHash && isMathOperator && iNode.value === "*"; !isHashNode(iNextNode) && isMultiplicationNode(iNode);
const isNextMultiplication = const isNextMultiplication =
!isRightCurlyBrace && isNextMathOperator && iNextNode.value === "*"; !isRightCurlyBraceNode(iNode) && isMultiplicationNode(iNextNode);
const isDivision = !isHashNode(iNextNode) && isDivisionNode(iNode);
const isDivision = !isNextHash && isMathOperator && iNode.value === "/";
const isNextDivision = const isNextDivision =
!isRightCurlyBrace && isNextMathOperator && iNextNode.value === "/"; !isRightCurlyBraceNode(iNode) && isDivisionNode(iNextNode);
const isAddition = !isHashNode(iNextNode) && isAdditionNode(iNode);
const isAddition = !isNextHash && isMathOperator && iNode.value === "+";
const isNextAddition = const isNextAddition =
!isRightCurlyBrace && isNextMathOperator && iNextNode.value === "+"; !isRightCurlyBraceNode(iNode) && isAdditionNode(iNextNode);
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 = const hasSpaceBeforeOperator =
isNextNextFunction || isNextNextWord || isFunction || isWord; (iNextNextNode && iNextNextNode.type === "value-func") ||
(iNextNextNode && isWordNode(iNextNextNode)) ||
iNode.type === "value-func" ||
isWordNode(iNode);
const hasSpaceAfterOperator = const hasSpaceAfterOperator =
isNextFunction || isNextWord || isPrevFunction || isPrevWord; iNextNode.type === "value-func" ||
isWordNode(iNextNode) ||
(iPrevNode && iPrevNode.type === "value-func") ||
(iPrevNode && isWordNode(iPrevNode));
const insideCalcFunction = insideValueFunctionNode(path, "calc");
if ( if (
(isMathOperator || isNextMathOperator) &&
// Multiplication // Multiplication
!isMultiplication && !isMultiplication &&
!isNextMultiplication && !isNextMultiplication &&
// Division // Division
!(isNextDivision && (hasSpaceBeforeOperator || insideCalcFunction)) && !(
isNextDivision &&
(hasSpaceBeforeOperator || insideCalcFunction)
) &&
!(isDivision && (hasSpaceAfterOperator || insideCalcFunction)) && !(isDivision && (hasSpaceAfterOperator || insideCalcFunction)) &&
// Addition // Addition
!(isNextAddition && hasSpaceBeforeOperator) && !(isNextAddition && hasSpaceBeforeOperator) &&
!(isAddition && hasSpaceAfterOperator) !(isAddition && hasSpaceAfterOperator)
) { ) {
const isNextParenGroup = isParenGroupNode(iNextNode);
const isNextValueNumber = iNextNode.type === "value-number";
if ( if (
(iNextNode.raws && iNextNode.raws.before === "") || hasEmptyRawBefore(iNextNode) ||
(isMathOperator && (isMathOperator &&
(isNextParenGroup || (isParenGroupNode(iNextNode) ||
isNextWord || isWordNode(iNextNode) ||
isNextValueNumber || iNextNode.type === "value-number" ||
isMathOperatorNode(iNextNode)) && isMathOperatorNode(iNextNode)) &&
(!iPrevNode || (iPrevNode && isMathOperatorNode(iPrevNode)))) (!iPrevNode || (iPrevNode && isMathOperatorNode(iPrevNode))))
) { ) {
continue; continue;
} }
} }
}
const isEqualityOperator = // Ignore inline comment, they already contain newline at end (i.e. `// Comment`)
isControlDirective && isEqualityOperatorNode(iNode); // Add `hardline` after inline comment (i.e. `// comment\n foo: bar;`)
const isRelationalOperator = const isInlineComment = isInlineValueCommentNode(iNode);
isControlDirective && isRelationalOperatorNode(iNode);
const isNextEqualityOperator = if (
isControlDirective && isEqualityOperatorNode(iNextNode); (iPrevNode && isInlineValueCommentNode(iPrevNode)) ||
const isNextRelationalOperator = isInlineComment ||
isControlDirective && isRelationalOperatorNode(iNextNode); isInlineValueCommentNode(iNextNode)
const isNextIfElseKeyword = ) {
isControlDirective && isIfElseKeywordNode(iNextNode); if (isInlineComment) {
const isEachKeyword = isControlDirective && isEachKeywordNode(iNode); parts.push(hardline);
const isNextEachKeyword = }
isControlDirective && isEachKeywordNode(iNextNode);
const isForKeyword = 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 &&
atRuleAncestorNode.name === "for" && atRuleAncestorNode.name.toLowerCase() === "namespace"
isForKeywordNode(iNode); ) {
const isNextForKeyword = parts.push(" ");
isControlDirective && isForKeywordNode(iNextNode);
const IsNextColon = iNextNode.value === ":";
continue;
}
// Formatting `grid` property
if (isGridValue) { if (isGridValue) {
if (iNode.source.start.line !== iNextNode.source.start.line) { if (iNode.source.start.line !== iNextNode.source.start.line) {
parts.push(hardline); parts.push(hardline);
didBreak = true; didBreak = true;
} else { } else {
parts.push(" "); parts.push(" ");
} }
} else if (iNode.type === "value-comment" && iNode.inline) {
parts.push(hardline); continue;
} 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);
} }
// 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) { if (didBreak) {
@ -693,6 +682,7 @@ function genericPrint(path, options, print) {
} }
res.push(printed[i]); res.push(printed[i]);
} }
return group(indent(fill(res))); 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": { case "value-func": {
const insideAtRuleSupportsNode = insideAtRuleNode(path, "supports");
const isKeyword =
["not", "and", "or"].indexOf(node.value.toLowerCase()) !== -1;
return concat([ return concat([
node.value, node.value,
insideAtRuleSupportsNode && isKeyword ? " " : "", insideAtRuleNode(path, "supports") && isMediaAndSupportsKeywords(node)
? " "
: "",
path.call(print, "group") path.call(print, "group")
]); ]);
} }
@ -778,6 +763,7 @@ function genericPrint(path, options, print) {
case "value-colon": { case "value-colon": {
return concat([ return concat([
node.value, node.value,
// Don't add spaces on `:` in `url` function (i.e. `url(fbglyph: cross-outline, fig-white)`)
insideValueFunctionNode(path, "url") ? "" : line 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"); const atRuleAncestorNode = getAncestorNode(path, "css-atrule");
return ( 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"; 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) { function isMathOperatorNode(node) {
return ( return (
node.type === "value-operator" && isMultiplicationNode(node) ||
["+", "-", "/", "*", "%"].indexOf(node.value) !== -1 isDivisionNode(node) ||
isAdditionNode(node) ||
isSubtractionNode(node) ||
isModuloNode(node)
); );
} }
@ -214,7 +239,7 @@ function isPostcssSimpleVarNode(currentNode, nextNode) {
); );
} }
function hasLessExtendValueNode(node) { function hasLessExtendNode(node) {
return ( return (
node.value && node.value &&
node.value.type === "value-root" && node.value.type === "value-root" &&
@ -226,7 +251,7 @@ function hasLessExtendValueNode(node) {
); );
} }
function hasComposesValueNode(node) { function hasComposesNode(node) {
return ( return (
node.value && node.value &&
node.value.type === "value-root" && node.value.type === "value-root" &&
@ -236,7 +261,7 @@ function hasComposesValueNode(node) {
); );
} }
function hasParensAroundValueNode(node) { function hasParensAroundNode(node) {
return ( return (
node.value && node.value &&
node.value.group && node.value.group &&
@ -247,6 +272,10 @@ function hasParensAroundValueNode(node) {
); );
} }
function hasEmptyRawBefore(node) {
return node.raws && node.raws.before === "";
}
function isKeyValuePairNode(node) { function isKeyValuePairNode(node) {
return ( return (
node.type === "value-comma_group" && node.type === "value-comma_group" &&
@ -303,6 +332,36 @@ function isSCSSMapItemNode(path) {
return false; 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 = { module.exports = {
getAncestorCounter, getAncestorCounter,
getAncestorNode, getAncestorNode,
@ -321,19 +380,32 @@ module.exports = {
isDetachedRulesetDeclarationNode, isDetachedRulesetDeclarationNode,
isRelationalOperatorNode, isRelationalOperatorNode,
isEqualityOperatorNode, isEqualityOperatorNode,
isMultiplicationNode,
isDivisionNode,
isAdditionNode,
isSubtractionNode,
isModuloNode,
isMathOperatorNode, isMathOperatorNode,
isEachKeywordNode, isEachKeywordNode,
isParenGroupNode, isParenGroupNode,
isForKeywordNode, isForKeywordNode,
isURLFunctionNode, isURLFunctionNode,
isIfElseKeywordNode, isIfElseKeywordNode,
hasLessExtendValueNode, hasLessExtendNode,
hasComposesValueNode, hasComposesNode,
hasParensAroundValueNode, hasParensAroundNode,
hasEmptyRawBefore,
isSCSSNestedPropertyNode, isSCSSNestedPropertyNode,
isDetachedRulesetCallNode, isDetachedRulesetCallNode,
isPostcssSimpleVarNode, isPostcssSimpleVarNode,
isKeyValuePairNode, isKeyValuePairNode,
isKeyValuePairInParenGroupNode, isKeyValuePairInParenGroupNode,
isSCSSMapItemNode isSCSSMapItemNode,
isInlineValueCommentNode,
isHashNode,
isLeftCurlyBraceNode,
isRightCurlyBraceNode,
isWordNode,
isColonNode,
isMediaAndSupportsKeywords
}; };