943 lines
26 KiB
JavaScript
943 lines
26 KiB
JavaScript
"use strict";
|
|
|
|
const clean = require("./clean");
|
|
const embed = require("./embed");
|
|
const { insertPragma } = require("./pragma");
|
|
const {
|
|
printNumber,
|
|
printString,
|
|
hasIgnoreComment,
|
|
hasNewline
|
|
} = require("../common/util");
|
|
const { isNextLineEmpty } = require("../common/util-shared");
|
|
|
|
const {
|
|
builders: {
|
|
concat,
|
|
join,
|
|
line,
|
|
hardline,
|
|
softline,
|
|
group,
|
|
fill,
|
|
indent,
|
|
dedent,
|
|
ifBreak
|
|
},
|
|
utils: { removeLines }
|
|
} = require("../doc");
|
|
|
|
const {
|
|
getAncestorNode,
|
|
getPropOfDeclNode,
|
|
maybeToLowerCase,
|
|
insideValueFunctionNode,
|
|
insideICSSRuleNode,
|
|
insideAtRuleNode,
|
|
insideURLFunctionInImportAtRuleNode,
|
|
isKeyframeAtRuleKeywords,
|
|
isHTMLTag,
|
|
isWideKeywords,
|
|
isSCSS,
|
|
isLastNode,
|
|
isSCSSControlDirectiveNode,
|
|
isDetachedRulesetDeclarationNode,
|
|
isRelationalOperatorNode,
|
|
isEqualityOperatorNode,
|
|
isMultiplicationNode,
|
|
isDivisionNode,
|
|
isAdditionNode,
|
|
isSubtractionNode,
|
|
isMathOperatorNode,
|
|
isEachKeywordNode,
|
|
isForKeywordNode,
|
|
isURLFunctionNode,
|
|
isIfElseKeywordNode,
|
|
hasComposesNode,
|
|
hasParensAroundNode,
|
|
hasEmptyRawBefore,
|
|
isKeyValuePairNode,
|
|
isDetachedRulesetCallNode,
|
|
isTemplatePlaceholderNode,
|
|
isTemplatePropNode,
|
|
isPostcssSimpleVarNode,
|
|
isSCSSMapItemNode,
|
|
isInlineValueCommentNode,
|
|
isHashNode,
|
|
isLeftCurlyBraceNode,
|
|
isRightCurlyBraceNode,
|
|
isWordNode,
|
|
isColonNode,
|
|
isMediaAndSupportsKeywords,
|
|
isColorAdjusterFuncNode
|
|
} = require("./utils");
|
|
|
|
function shouldPrintComma(options) {
|
|
switch (options.trailingComma) {
|
|
case "all":
|
|
case "es5":
|
|
return true;
|
|
case "none":
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function genericPrint(path, options, print) {
|
|
const node = path.getValue();
|
|
|
|
/* istanbul ignore if */
|
|
if (!node) {
|
|
return "";
|
|
}
|
|
|
|
if (typeof node === "string") {
|
|
return node;
|
|
}
|
|
|
|
switch (node.type) {
|
|
case "yaml":
|
|
case "toml":
|
|
return concat([node.raw, hardline]);
|
|
case "css-root": {
|
|
const nodes = printNodeSequence(path, options, print);
|
|
|
|
if (nodes.parts.length) {
|
|
return concat([nodes, hardline]);
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
case "css-comment": {
|
|
if (node.raws.content) {
|
|
return node.raws.content;
|
|
}
|
|
const text = options.originalText.slice(
|
|
options.locStart(node),
|
|
options.locEnd(node)
|
|
);
|
|
const rawText = node.raws.text || node.text;
|
|
// Workaround a bug where the location is off.
|
|
// https://github.com/postcss/postcss-scss/issues/63
|
|
if (text.indexOf(rawText) === -1) {
|
|
if (node.raws.inline) {
|
|
return concat(["// ", rawText]);
|
|
}
|
|
return concat(["/* ", rawText, " */"]);
|
|
}
|
|
return text;
|
|
}
|
|
case "css-rule": {
|
|
return concat([
|
|
path.call(print, "selector"),
|
|
node.important ? " !important" : "",
|
|
node.nodes
|
|
? concat([
|
|
" {",
|
|
node.nodes.length > 0
|
|
? indent(
|
|
concat([hardline, printNodeSequence(path, options, print)])
|
|
)
|
|
: "",
|
|
hardline,
|
|
"}",
|
|
isDetachedRulesetDeclarationNode(node) ? ";" : ""
|
|
])
|
|
: ";"
|
|
]);
|
|
}
|
|
case "css-decl": {
|
|
const parentNode = path.getParentNode();
|
|
|
|
return concat([
|
|
node.raws.before.replace(/[\s;]/g, ""),
|
|
insideICSSRuleNode(path) ? node.prop : maybeToLowerCase(node.prop),
|
|
node.raws.between.trim() === ":" ? ":" : node.raws.between.trim(),
|
|
node.extend ? "" : " ",
|
|
hasComposesNode(node)
|
|
? removeLines(path.call(print, "value"))
|
|
: path.call(print, "value"),
|
|
node.raws.important
|
|
? node.raws.important.replace(/\s*!\s*important/i, " !important")
|
|
: node.important
|
|
? " !important"
|
|
: "",
|
|
node.raws.scssDefault
|
|
? node.raws.scssDefault.replace(/\s*!default/i, " !default")
|
|
: node.scssDefault
|
|
? " !default"
|
|
: "",
|
|
node.raws.scssGlobal
|
|
? node.raws.scssGlobal.replace(/\s*!global/i, " !global")
|
|
: node.scssGlobal
|
|
? " !global"
|
|
: "",
|
|
node.nodes
|
|
? concat([
|
|
" {",
|
|
indent(
|
|
concat([softline, printNodeSequence(path, options, print)])
|
|
),
|
|
softline,
|
|
"}"
|
|
])
|
|
: isTemplatePropNode(node) &&
|
|
!parentNode.raws.semicolon &&
|
|
options.originalText[options.locEnd(node) - 1] !== ";"
|
|
? ""
|
|
: ";"
|
|
]);
|
|
}
|
|
case "css-atrule": {
|
|
const parentNode = path.getParentNode();
|
|
|
|
return concat([
|
|
"@",
|
|
// If a Less file ends up being parsed with the SCSS parser, Less
|
|
// variable declarations will be parsed as at-rules with names ending
|
|
// with a colon, so keep the original case then.
|
|
isDetachedRulesetCallNode(node) || node.name.endsWith(":")
|
|
? node.name
|
|
: maybeToLowerCase(node.name),
|
|
node.params
|
|
? concat([
|
|
isDetachedRulesetCallNode(node)
|
|
? ""
|
|
: isTemplatePlaceholderNode(node) &&
|
|
/^\s*\n/.test(node.raws.afterName)
|
|
? /^\s*\n\s*\n/.test(node.raws.afterName)
|
|
? concat([hardline, hardline])
|
|
: hardline
|
|
: " ",
|
|
path.call(print, "params")
|
|
])
|
|
: "",
|
|
node.selector
|
|
? indent(concat([" ", path.call(print, "selector")]))
|
|
: "",
|
|
node.value
|
|
? group(
|
|
concat([
|
|
" ",
|
|
path.call(print, "value"),
|
|
isSCSSControlDirectiveNode(node)
|
|
? hasParensAroundNode(node)
|
|
? " "
|
|
: line
|
|
: ""
|
|
])
|
|
)
|
|
: node.name === "else"
|
|
? " "
|
|
: "",
|
|
node.nodes
|
|
? concat([
|
|
isSCSSControlDirectiveNode(node) ? "" : " ",
|
|
"{",
|
|
indent(
|
|
concat([
|
|
node.nodes.length > 0 ? softline : "",
|
|
printNodeSequence(path, options, print)
|
|
])
|
|
),
|
|
softline,
|
|
"}"
|
|
])
|
|
: isTemplatePlaceholderNode(node) &&
|
|
!parentNode.raws.semicolon &&
|
|
options.originalText[options.locEnd(node) - 1] !== ";"
|
|
? ""
|
|
: ";"
|
|
]);
|
|
}
|
|
// postcss-media-query-parser
|
|
case "media-query-list": {
|
|
const parts = [];
|
|
path.each(childPath => {
|
|
const node = childPath.getValue();
|
|
if (node.type === "media-query" && node.value === "") {
|
|
return;
|
|
}
|
|
parts.push(childPath.call(print));
|
|
}, "nodes");
|
|
|
|
return group(indent(join(line, parts)));
|
|
}
|
|
case "media-query": {
|
|
return concat([
|
|
join(" ", path.map(print, "nodes")),
|
|
isLastNode(path, node) ? "" : ","
|
|
]);
|
|
}
|
|
case "media-type": {
|
|
return adjustNumbers(adjustStrings(node.value, options));
|
|
}
|
|
case "media-feature-expression": {
|
|
if (!node.nodes) {
|
|
return node.value;
|
|
}
|
|
return concat(["(", concat(path.map(print, "nodes")), ")"]);
|
|
}
|
|
case "media-feature": {
|
|
return maybeToLowerCase(
|
|
adjustStrings(node.value.replace(/ +/g, " "), options)
|
|
);
|
|
}
|
|
case "media-colon": {
|
|
return concat([node.value, " "]);
|
|
}
|
|
case "media-value": {
|
|
return adjustNumbers(adjustStrings(node.value, options));
|
|
}
|
|
case "media-keyword": {
|
|
return adjustStrings(node.value, options);
|
|
}
|
|
case "media-url": {
|
|
return adjustStrings(
|
|
node.value.replace(/^url\(\s+/gi, "url(").replace(/\s+\)$/gi, ")"),
|
|
options
|
|
);
|
|
}
|
|
case "media-unknown": {
|
|
return node.value;
|
|
}
|
|
// postcss-selector-parser
|
|
case "selector-root": {
|
|
return group(
|
|
concat([
|
|
insideAtRuleNode(path, "custom-selector")
|
|
? concat([getAncestorNode(path, "css-atrule").customSelector, line])
|
|
: "",
|
|
join(
|
|
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;
|
|
}
|
|
case "selector-string": {
|
|
return adjustStrings(node.value, options);
|
|
}
|
|
case "selector-tag": {
|
|
const parentNode = path.getParentNode();
|
|
const index = parentNode && parentNode.nodes.indexOf(node);
|
|
const prevNode = index && parentNode.nodes[index - 1];
|
|
|
|
return concat([
|
|
node.namespace
|
|
? concat([node.namespace === true ? "" : node.namespace.trim(), "|"])
|
|
: "",
|
|
prevNode.type === "selector-nesting"
|
|
? node.value
|
|
: adjustNumbers(
|
|
isHTMLTag(node.value) ||
|
|
isKeyframeAtRuleKeywords(path, node.value)
|
|
? node.value.toLowerCase()
|
|
: node.value
|
|
)
|
|
]);
|
|
}
|
|
case "selector-id": {
|
|
return concat(["#", node.value]);
|
|
}
|
|
case "selector-class": {
|
|
return concat([".", adjustNumbers(adjustStrings(node.value, options))]);
|
|
}
|
|
case "selector-attribute": {
|
|
return concat([
|
|
"[",
|
|
node.namespace
|
|
? concat([node.namespace === true ? "" : node.namespace.trim(), "|"])
|
|
: "",
|
|
node.attribute.trim(),
|
|
node.operator ? node.operator : "",
|
|
node.value
|
|
? quoteAttributeValue(
|
|
adjustStrings(node.value.trim(), options),
|
|
options
|
|
)
|
|
: "",
|
|
node.insensitive ? " i" : "",
|
|
"]"
|
|
]);
|
|
}
|
|
case "selector-combinator": {
|
|
if (
|
|
node.value === "+" ||
|
|
node.value === ">" ||
|
|
node.value === "~" ||
|
|
node.value === ">>>"
|
|
) {
|
|
const parentNode = path.getParentNode();
|
|
const leading =
|
|
parentNode.type === "selector-selector" &&
|
|
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": {
|
|
return concat([
|
|
node.namespace
|
|
? concat([node.namespace === true ? "" : node.namespace.trim(), "|"])
|
|
: "",
|
|
node.value
|
|
]);
|
|
}
|
|
case "selector-pseudo": {
|
|
return concat([
|
|
maybeToLowerCase(node.value),
|
|
node.nodes && node.nodes.length > 0
|
|
? concat(["(", join(", ", path.map(print, "nodes")), ")"])
|
|
: ""
|
|
]);
|
|
}
|
|
case "selector-nesting": {
|
|
return node.value;
|
|
}
|
|
case "selector-unknown": {
|
|
const ruleAncestorNode = getAncestorNode(path, "css-rule");
|
|
|
|
// Nested SCSS property
|
|
if (ruleAncestorNode && ruleAncestorNode.isSCSSNesterProperty) {
|
|
return adjustNumbers(
|
|
adjustStrings(maybeToLowerCase(node.value), options)
|
|
);
|
|
}
|
|
|
|
return node.value;
|
|
}
|
|
// postcss-values-parser
|
|
case "value-value":
|
|
case "value-root": {
|
|
return path.call(print, "group");
|
|
}
|
|
case "value-comment": {
|
|
return concat([
|
|
node.inline ? "//" : "/*",
|
|
node.value,
|
|
node.inline ? "" : "*/"
|
|
]);
|
|
}
|
|
case "value-comma_group": {
|
|
const parentNode = path.getParentNode();
|
|
const parentParentNode = path.getParentNode(1);
|
|
const declAncestorProp = getPropOfDeclNode(path);
|
|
const isGridValue =
|
|
declAncestorProp &&
|
|
parentNode.type === "value-value" &&
|
|
(declAncestorProp === "grid" ||
|
|
declAncestorProp.startsWith("grid-template"));
|
|
const atRuleAncestorNode = getAncestorNode(path, "css-atrule");
|
|
const isControlDirective =
|
|
atRuleAncestorNode && isSCSSControlDirectiveNode(atRuleAncestorNode);
|
|
|
|
const printed = path.map(print, "groups");
|
|
const parts = [];
|
|
const insideURLFunction = insideValueFunctionNode(path, "url");
|
|
|
|
let insideSCSSInterpolationInString = false;
|
|
let didBreak = false;
|
|
for (let i = 0; i < node.groups.length; ++i) {
|
|
parts.push(printed[i]);
|
|
|
|
// Ignore value inside `url()`
|
|
if (insideURLFunction) {
|
|
continue;
|
|
}
|
|
|
|
const iPrevNode = node.groups[i - 1];
|
|
const iNode = node.groups[i];
|
|
const iNextNode = node.groups[i + 1];
|
|
const iNextNextNode = node.groups[i + 2];
|
|
|
|
// Ignore after latest node (i.e. before semicolon)
|
|
if (!iNextNode) {
|
|
continue;
|
|
}
|
|
|
|
// Ignore spaces before/after string interpolation (i.e. `"#{my-fn("_")}"`)
|
|
const isStartSCSSinterpolationInString =
|
|
iNode.type === "value-string" && iNode.value.startsWith("#{");
|
|
const isEndingSCSSinterpolationInString =
|
|
insideSCSSInterpolationInString &&
|
|
iNextNode.type === "value-string" &&
|
|
iNextNode.value.endsWith("}");
|
|
|
|
if (
|
|
isStartSCSSinterpolationInString ||
|
|
isEndingSCSSinterpolationInString
|
|
) {
|
|
insideSCSSInterpolationInString = !insideSCSSInterpolationInString;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (insideSCSSInterpolationInString) {
|
|
continue;
|
|
}
|
|
|
|
// Ignore colon (i.e. `:`)
|
|
if (isColonNode(iNode) || isColonNode(iNextNode)) {
|
|
continue;
|
|
}
|
|
|
|
// Ignore `@` in Less (i.e. `@@var;`)
|
|
if (iNode.type === "value-atword" && iNode.value === "") {
|
|
continue;
|
|
}
|
|
|
|
// Ignore `~` in Less (i.e. `content: ~"^//* some horrible but needed css hack";`)
|
|
if (iNode.value === "~") {
|
|
continue;
|
|
}
|
|
|
|
// Ignore `\` (i.e. `$variable: \@small;`)
|
|
if (iNode.value === "\\") {
|
|
continue;
|
|
}
|
|
|
|
// Ignore `$$` (i.e. `background-color: $$(style)Color;`)
|
|
if (isPostcssSimpleVarNode(iNode, iNextNode)) {
|
|
continue;
|
|
}
|
|
|
|
// Ignore spaces after `#` and after `{` and before `}` in SCSS interpolation (i.e. `#{variable}`)
|
|
if (
|
|
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 === "--" && isHashNode(iNextNode)) {
|
|
continue;
|
|
}
|
|
|
|
// Formatting math operations
|
|
const isMathOperator = isMathOperatorNode(iNode);
|
|
const isNextMathOperator = isMathOperatorNode(iNextNode);
|
|
|
|
// Print spaces before and after math operators beside SCSS interpolation as is
|
|
// (i.e. `#{$var}+5`, `#{$var} +5`, `#{$var}+ 5`, `#{$var} + 5`)
|
|
// (i.e. `5+#{$var}`, `5 +#{$var}`, `5+ #{$var}`, `5 + #{$var}`)
|
|
if (
|
|
((isMathOperator && isHashNode(iNextNode)) ||
|
|
(isNextMathOperator && isRightCurlyBraceNode(iNode))) &&
|
|
hasEmptyRawBefore(iNextNode)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
// Print spaces before and after addition and subtraction math operators as is in `calc` function
|
|
// due to the fact that it is not valid syntax
|
|
// (i.e. `calc(1px+1px)`, `calc(1px+ 1px)`, `calc(1px +1px)`, `calc(1px + 1px)`)
|
|
if (
|
|
insideValueFunctionNode(path, "calc") &&
|
|
(isAdditionNode(iNode) ||
|
|
isAdditionNode(iNextNode) ||
|
|
isSubtractionNode(iNode) ||
|
|
isSubtractionNode(iNextNode)) &&
|
|
hasEmptyRawBefore(iNextNode)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
// Print spaces after `+` and `-` in color adjuster functions as is (e.g. `color(red l(+ 20%))`)
|
|
// Adjusters with signed numbers (e.g. `color(red l(+20%))`) output as-is.
|
|
const isColorAdjusterNode =
|
|
(isAdditionNode(iNode) || isSubtractionNode(iNode)) &&
|
|
i === 0 &&
|
|
(iNextNode.type === "value-number" || iNextNode.isHex) &&
|
|
(parentParentNode && isColorAdjusterFuncNode(parentParentNode)) &&
|
|
!hasEmptyRawBefore(iNextNode);
|
|
|
|
const requireSpaceBeforeOperator =
|
|
(iNextNextNode && iNextNextNode.type === "value-func") ||
|
|
(iNextNextNode && isWordNode(iNextNextNode)) ||
|
|
iNode.type === "value-func" ||
|
|
isWordNode(iNode);
|
|
const requireSpaceAfterOperator =
|
|
iNextNode.type === "value-func" ||
|
|
isWordNode(iNextNode) ||
|
|
(iPrevNode && iPrevNode.type === "value-func") ||
|
|
(iPrevNode && isWordNode(iPrevNode));
|
|
|
|
// Formatting `/`, `+`, `-` sign
|
|
if (
|
|
!(isMultiplicationNode(iNextNode) || isMultiplicationNode(iNode)) &&
|
|
!insideValueFunctionNode(path, "calc") &&
|
|
!isColorAdjusterNode &&
|
|
((isDivisionNode(iNextNode) && !requireSpaceBeforeOperator) ||
|
|
(isDivisionNode(iNode) && !requireSpaceAfterOperator) ||
|
|
(isAdditionNode(iNextNode) && !requireSpaceBeforeOperator) ||
|
|
(isAdditionNode(iNode) && !requireSpaceAfterOperator) ||
|
|
isSubtractionNode(iNextNode) ||
|
|
isSubtractionNode(iNode)) &&
|
|
(hasEmptyRawBefore(iNextNode) ||
|
|
(isMathOperator &&
|
|
(!iPrevNode || (iPrevNode && isMathOperatorNode(iPrevNode)))))
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
// 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 &&
|
|
iNextNode.source &&
|
|
iNode.source.start.line !== iNextNode.source.start.line
|
|
) {
|
|
parts.push(hardline);
|
|
|
|
didBreak = true;
|
|
} else {
|
|
parts.push(" ");
|
|
}
|
|
|
|
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) {
|
|
parts.unshift(hardline);
|
|
}
|
|
|
|
if (isControlDirective) {
|
|
return group(indent(concat(parts)));
|
|
}
|
|
|
|
// Indent is not needed for import url when url is very long
|
|
// and node has two groups
|
|
// when type is value-comma_group
|
|
// example @import url("verylongurl") projection,tv
|
|
if (insideURLFunctionInImportAtRuleNode(path)) {
|
|
return group(fill(parts));
|
|
}
|
|
|
|
return group(indent(fill(parts)));
|
|
}
|
|
case "value-paren_group": {
|
|
const parentNode = path.getParentNode();
|
|
|
|
if (
|
|
parentNode &&
|
|
isURLFunctionNode(parentNode) &&
|
|
(node.groups.length === 1 ||
|
|
(node.groups.length > 0 &&
|
|
node.groups[0].type === "value-comma_group" &&
|
|
node.groups[0].groups.length > 0 &&
|
|
node.groups[0].groups[0].type === "value-word" &&
|
|
node.groups[0].groups[0].value.startsWith("data:")))
|
|
) {
|
|
return concat([
|
|
node.open ? path.call(print, "open") : "",
|
|
join(",", path.map(print, "groups")),
|
|
node.close ? path.call(print, "close") : ""
|
|
]);
|
|
}
|
|
|
|
if (!node.open) {
|
|
const printed = path.map(print, "groups");
|
|
const res = [];
|
|
|
|
for (let i = 0; i < printed.length; i++) {
|
|
if (i !== 0) {
|
|
res.push(concat([",", line]));
|
|
}
|
|
res.push(printed[i]);
|
|
}
|
|
|
|
return group(indent(fill(res)));
|
|
}
|
|
|
|
const isSCSSMapItem = isSCSSMapItemNode(path);
|
|
|
|
return group(
|
|
concat([
|
|
node.open ? path.call(print, "open") : "",
|
|
indent(
|
|
concat([
|
|
softline,
|
|
join(
|
|
concat([",", line]),
|
|
path.map(childPath => {
|
|
const node = childPath.getValue();
|
|
const printed = print(childPath);
|
|
|
|
// Key/Value pair in open paren already indented
|
|
if (
|
|
isKeyValuePairNode(node) &&
|
|
node.type === "value-comma_group" &&
|
|
node.groups &&
|
|
node.groups[2] &&
|
|
node.groups[2].type === "value-paren_group"
|
|
) {
|
|
printed.contents.contents.parts[1] = group(
|
|
printed.contents.contents.parts[1]
|
|
);
|
|
|
|
return group(dedent(printed));
|
|
}
|
|
|
|
return printed;
|
|
}, "groups")
|
|
)
|
|
])
|
|
),
|
|
ifBreak(
|
|
isSCSS(options.parser, options.originalText) &&
|
|
isSCSSMapItem &&
|
|
shouldPrintComma(options)
|
|
? ","
|
|
: ""
|
|
),
|
|
softline,
|
|
node.close ? path.call(print, "close") : ""
|
|
]),
|
|
{
|
|
shouldBreak: isSCSSMapItem
|
|
}
|
|
);
|
|
}
|
|
case "value-func": {
|
|
return concat([
|
|
node.value,
|
|
insideAtRuleNode(path, "supports") && isMediaAndSupportsKeywords(node)
|
|
? " "
|
|
: "",
|
|
path.call(print, "group")
|
|
]);
|
|
}
|
|
case "value-paren": {
|
|
return node.value;
|
|
}
|
|
case "value-number": {
|
|
return concat([printCssNumber(node.value), maybeToLowerCase(node.unit)]);
|
|
}
|
|
case "value-operator": {
|
|
return node.value;
|
|
}
|
|
case "value-word": {
|
|
if ((node.isColor && node.isHex) || isWideKeywords(node.value)) {
|
|
return node.value.toLowerCase();
|
|
}
|
|
|
|
return node.value;
|
|
}
|
|
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
|
|
]);
|
|
}
|
|
case "value-comma": {
|
|
return concat([node.value, " "]);
|
|
}
|
|
case "value-string": {
|
|
return printString(
|
|
node.raws.quote + node.value + node.raws.quote,
|
|
options
|
|
);
|
|
}
|
|
case "value-atword": {
|
|
return concat(["@", node.value]);
|
|
}
|
|
case "value-unicode-range": {
|
|
return node.value;
|
|
}
|
|
case "value-unknown": {
|
|
return node.value;
|
|
}
|
|
default:
|
|
/* istanbul ignore next */
|
|
throw new Error(`Unknown postcss type ${JSON.stringify(node.type)}`);
|
|
}
|
|
}
|
|
|
|
function printNodeSequence(path, options, print) {
|
|
const node = path.getValue();
|
|
const parts = [];
|
|
let i = 0;
|
|
path.map(pathChild => {
|
|
const prevNode = node.nodes[i - 1];
|
|
if (
|
|
prevNode &&
|
|
prevNode.type === "css-comment" &&
|
|
prevNode.text.trim() === "prettier-ignore"
|
|
) {
|
|
const childNode = pathChild.getValue();
|
|
parts.push(
|
|
options.originalText.slice(
|
|
options.locStart(childNode),
|
|
options.locEnd(childNode)
|
|
)
|
|
);
|
|
} else {
|
|
parts.push(pathChild.call(print));
|
|
}
|
|
|
|
if (i !== node.nodes.length - 1) {
|
|
if (
|
|
(node.nodes[i + 1].type === "css-comment" &&
|
|
!hasNewline(
|
|
options.originalText,
|
|
options.locStart(node.nodes[i + 1]),
|
|
{ backwards: true }
|
|
) &&
|
|
node.nodes[i].type !== "yaml" &&
|
|
node.nodes[i].type !== "toml") ||
|
|
(node.nodes[i + 1].type === "css-atrule" &&
|
|
node.nodes[i + 1].name === "else" &&
|
|
node.nodes[i].type !== "css-comment")
|
|
) {
|
|
parts.push(" ");
|
|
} else {
|
|
parts.push(hardline);
|
|
if (
|
|
isNextLineEmpty(
|
|
options.originalText,
|
|
pathChild.getValue(),
|
|
options
|
|
) &&
|
|
node.nodes[i].type !== "yaml" &&
|
|
node.nodes[i].type !== "toml"
|
|
) {
|
|
parts.push(hardline);
|
|
}
|
|
}
|
|
}
|
|
i++;
|
|
}, "nodes");
|
|
|
|
return concat(parts);
|
|
}
|
|
|
|
const STRING_REGEX = /(['"])(?:(?!\1)[^\\]|\\[\s\S])*\1/g;
|
|
const NUMBER_REGEX = /(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g;
|
|
const STANDARD_UNIT_REGEX = /[a-zA-Z]+/g;
|
|
const WORD_PART_REGEX = /[$@]?[a-zA-Z_\u0080-\uFFFF][\w\-\u0080-\uFFFF]*/g;
|
|
const ADJUST_NUMBERS_REGEX = RegExp(
|
|
STRING_REGEX.source +
|
|
`|` +
|
|
`(${WORD_PART_REGEX.source})?` +
|
|
`(${NUMBER_REGEX.source})` +
|
|
`(${STANDARD_UNIT_REGEX.source})?`,
|
|
"g"
|
|
);
|
|
|
|
function adjustStrings(value, options) {
|
|
return value.replace(STRING_REGEX, match => printString(match, options));
|
|
}
|
|
|
|
function quoteAttributeValue(value, options) {
|
|
const quote = options.singleQuote ? "'" : '"';
|
|
return value.includes('"') || value.includes("'")
|
|
? value
|
|
: quote + value + quote;
|
|
}
|
|
|
|
function adjustNumbers(value) {
|
|
return value.replace(
|
|
ADJUST_NUMBERS_REGEX,
|
|
(match, quote, wordPart, number, unit) =>
|
|
!wordPart && number
|
|
? (wordPart || "") +
|
|
printCssNumber(number) +
|
|
maybeToLowerCase(unit || "")
|
|
: match
|
|
);
|
|
}
|
|
|
|
function printCssNumber(rawNumber) {
|
|
return (
|
|
printNumber(rawNumber)
|
|
// Remove trailing `.0`.
|
|
.replace(/\.0(?=$|e)/, "")
|
|
);
|
|
}
|
|
|
|
module.exports = {
|
|
print: genericPrint,
|
|
embed,
|
|
insertPragma,
|
|
hasPrettierIgnore: hasIgnoreComment,
|
|
massageAstNode: clean
|
|
};
|