diff --git a/package.json b/package.json index 444697197f3b..920176a450f7 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "remark-footnotes": "2.0.0", "remark-math": "3.0.1", "remark-parse": "8.0.3", + "sass-parser": "0.4.21", "sdbm": "2.0.0", "search-closest": "1.1.0", "smol-toml": "1.3.4", diff --git a/src/language-css/clean.js b/src/language-css/clean.js index b3d57d59c692..6cc6ce4938bf 100644 --- a/src/language-css/clean.js +++ b/src/language-css/clean.js @@ -17,8 +17,8 @@ function clean(original, cloned, parent) { } if ( - original.type === "css-comment" && - parent.type === "css-root" && + original.type === "comment" && + parent.type === "root" && parent.nodes.length > 0 ) { // --insert-pragma @@ -41,15 +41,11 @@ function clean(original, cloned, parent) { } // Last comment is not parsed, when omitting semicolon, #8675 - if (parent.type === "css-root" && parent.nodes.at(-1) === original) { + if (parent.type === "root" && parent.nodes.at(-1) === original) { return null; } } - if (original.type === "value-root") { - delete cloned.text; - } - if ( original.type === "media-query" || original.type === "media-query-list" || @@ -58,7 +54,7 @@ function clean(original, cloned, parent) { delete cloned.value; } - if (original.type === "css-rule") { + if (original.type === "rule") { delete cloned.params; } @@ -73,7 +69,7 @@ function clean(original, cloned, parent) { original.type === "selector-string" || original.type === "selector-class" || original.type === "selector-combinator" || - original.type === "value-string") && + original.type === "string") && original.value ) { cloned.value = cleanCSSStrings(original.value); @@ -99,16 +95,16 @@ function clean(original, cloned, parent) { ) { cloned.value = cloned.value.toLowerCase(); } - if (original.type === "css-decl") { + if (original.type === "decl") { cloned.prop = original.prop.toLowerCase(); } - if (original.type === "css-atrule" || original.type === "css-import") { + if (original.type === "atrule" || original.type === "import") { cloned.name = original.name.toLowerCase(); } - if (original.type === "value-number") { + if (original.type === "number") { cloned.unit = original.unit.toLowerCase(); } - if (original.type === "value-unknown") { + if (original.type === "unknown") { cloned.value = cloned.value.replaceAll(/;$/gu, ""); } @@ -128,7 +124,7 @@ function clean(original, cloned, parent) { if ( (original.type === "media-value" || original.type === "media-type" || - original.type === "value-number" || + original.type === "number" || original.type === "selector-root-invalid" || original.type === "selector-class" || original.type === "selector-combinator" || @@ -153,12 +149,12 @@ function clean(original, cloned, parent) { } // Workaround when `postcss-values-parser` parse `not`, `and` or `or` keywords as `value-func` - if ( - original.type === "css-atrule" && - original.name.toLowerCase() === "supports" - ) { - delete cloned.value; - } + // if ( + // original.type === "css-atrule" && + // original.name.toLowerCase() === "supports" + // ) { + // delete cloned.value; + // } // Workaround for SCSS nested properties if (original.type === "selector-unknown") { diff --git a/src/language-css/loc.js b/src/language-css/loc.js index 6b4ea4dc478a..a0a2a082ab9b 100644 --- a/src/language-css/loc.js +++ b/src/language-css/loc.js @@ -33,7 +33,7 @@ function calculateLocStart(node, text) { } function calculateLocEnd(node, text) { - if (node.type === "css-comment" && node.inline) { + if (node.type === "comment" && node.sassType === "sass-comment") { return skipEverythingButNewLine(text, node.source.startOffset); } @@ -72,51 +72,8 @@ function calculateLoc(node, text) { continue; } - if (child.type === "value-root" || child.type === "value-unknown") { - calculateValueNodeLoc( - child, - getValueRootOffset(node), - child.text || child.value, - ); - } else { - calculateLoc(child, text); - } - } -} - -function calculateValueNodeLoc(node, rootOffset, text) { - if (node.source) { - node.source.startOffset = calculateLocStart(node, text) + rootOffset; - node.source.endOffset = calculateLocEnd(node, text) + rootOffset; - } - - for (const key in node) { - const child = node[key]; - - if (key === "source" || !child || typeof child !== "object") { - continue; - } - - calculateValueNodeLoc(child, rootOffset, text); - } -} - -function getValueRootOffset(node) { - let result = node.source.startOffset; - if (typeof node.prop === "string") { - result += node.prop.length; + calculateLoc(child, text); } - - if (node.type === "css-atrule" && typeof node.name === "string") { - result += - 1 + node.name.length + node.raws.afterName.match(/^\s*:?\s*/u)[0].length; - } - - if (node.type !== "css-atrule" && typeof node.raws?.between === "string") { - result += node.raws.between.length; - } - - return result; } /** diff --git a/src/language-css/parse/parse-value.js b/src/language-css/parse/parse-value.js index ea628fe53f9f..340a43e87be6 100644 --- a/src/language-css/parse/parse-value.js +++ b/src/language-css/parse/parse-value.js @@ -108,7 +108,7 @@ function parseValueNode(valueNode, options) { parenGroupStack.pop(); parenGroup = parenGroupStack.at(-1); } else if (node.type === "comma") { - // Trialing comma + // Trailing comma if ( i === nodes.length - 3 && nodes[i + 1].type === "comment" && diff --git a/src/language-css/parser-postcss.js b/src/language-css/parser-postcss.js index c7923a53238a..40e84fb7c590 100644 --- a/src/language-css/parser-postcss.js +++ b/src/language-css/parser-postcss.js @@ -1,6 +1,7 @@ import postcssParse from "postcss/lib/parse"; import postcssLess from "postcss-less"; -import postcssScssParse from "postcss-scss/lib/scss-parse"; +// import postcssScssParse from "postcss-scss/lib/scss-parse"; +import { scss as postcssScssParse } from "sass-parser"; import createError from "../common/parser-create-error.js"; import parseFrontMatter from "../utils/front-matter/parse.js"; import { @@ -9,12 +10,11 @@ import { locStart, replaceQuotesInInlineComments, } from "./loc.js"; -import parseMediaQuery from "./parse/parse-media-query.js"; +// import parseMediaQuery from "./parse/parse-media-query.js"; import parseSelector from "./parse/parse-selector.js"; import parseValue from "./parse/parse-value.js"; -import { addTypePrefix } from "./parse/utils.js"; import { hasIgnorePragma, hasPragma } from "./pragma.js"; -import isModuleRuleName from "./utils/is-module-rule-name.js"; +// import isModuleRuleName from "./utils/is-module-rule-name.js"; import isSCSSNestedPropertyNode from "./utils/is-scss-nested-property-node.js"; const DEFAULT_SCSS_DIRECTIVE = /(\s*)(!default).*$/u; @@ -37,7 +37,7 @@ function parseNestedCSS(node, options) { // Custom properties looks like declarations if ( - node.type === "css-decl" && + node.type === "decl" && typeof node.prop === "string" && node.prop.startsWith("--") && typeof node.value === "string" && @@ -70,18 +70,18 @@ function parseNestedCSS(node, options) { } catch { // noop } - if (ast?.nodes?.length === 1 && ast.nodes[0].type === "css-rule") { + if (ast?.nodes?.length === 1 && ast.nodes[0].type === "rule") { rules = ast.nodes[0].nodes; } } if (rules) { node.value = { - type: "css-rule", + type: "rule", nodes: rules, }; } else { node.value = { - type: "value-unknown", + type: "unknown", value: node.raws.value.raw, }; } @@ -102,7 +102,7 @@ function parseNestedCSS(node, options) { node.raws.selector = selector; } - let value = ""; + let value = node.toString(); if (typeof node.value === "string") { value = node.raws.value @@ -112,6 +112,7 @@ function parseNestedCSS(node, options) { node.raws.value = value.trim(); } + // TODO: ImportRule.params can't be overwritten. let params = ""; if (typeof node.params === "string") { @@ -151,10 +152,11 @@ function parseNestedCSS(node, options) { // Check on SCSS nested property if (isSCSSNestedPropertyNode(node, options)) { - node.isSCSSNesterProperty = true; + node.isSCSSNestedProperty = true; } - node.selector = parseSelector(selector); + // TODO: node.selector cannot be overwritten + node.selectorTemp = parseSelector(selector); return node; } @@ -184,17 +186,18 @@ function parseNestedCSS(node, options) { if (value.startsWith("progid:")) { return { - type: "value-unknown", + type: "unknown", value, }; } - node.value = parseValue(value, options); + // TODO: skipping value parsing + // parseValue(node, options); } if ( options.parser === "less" && - node.type === "css-decl" && + node.type === "decl" && value.startsWith("extend(") ) { // extend is missing @@ -207,7 +210,7 @@ function parseNestedCSS(node, options) { } } - if (node.type === "css-atrule") { + if (node.type === "atrule") { if (options.parser === "less") { // mixin if (node.mixin) { @@ -276,93 +279,93 @@ function parseNestedCSS(node, options) { } } - if (node.type === "css-atrule" && params.length > 0) { - const { name } = node; - const lowercasedName = node.name.toLowerCase(); - - if (name === "warn" || name === "error") { - node.params = { - type: "media-unknown", - value: params, - }; - - return node; - } - - if (name === "extend" || name === "nest") { - node.selector = parseSelector(params); - delete node.params; - - return node; - } - - if (name === "at-root") { - if (/^\(\s*(?:without|with)\s*:.+\)$/su.test(params)) { - node.params = parseValue(params, options); - } else { - node.selector = parseSelector(params); - delete node.params; - } - - return node; - } - - if (isModuleRuleName(lowercasedName)) { - node.import = true; - delete node.filename; - node.params = parseValue(params, options); - return node; - } - - if ( - [ - "namespace", - "supports", - "if", - "else", - "for", - "each", - "while", - "debug", - "mixin", - "include", - "function", - "return", - "define-mixin", - "add-mixin", - ].includes(name) - ) { - // Remove unnecessary spaces in SCSS variable arguments - // Move spaces after the `...`, so we can keep the range correct - params = params.replace(/(\$\S+?)(\s+)?\.{3}/u, "$1...$2"); - // Remove unnecessary spaces before SCSS control, mixin and function directives - // Move spaces after the `(`, so we can keep the range correct - params = params.replace(/^(?!if)(\S+)(\s+)\(/u, "$1($2"); - - node.value = parseValue(params, options); - delete node.params; - - return node; - } - - if (["media", "custom-media"].includes(lowercasedName)) { - if (params.includes("#{")) { - // Workaround for media at rule with scss interpolation - return { - type: "media-unknown", - value: params, - }; - } - - node.params = parseMediaQuery(params); - - return node; - } - - node.params = params; - - return node; - } + // if (node.type === "atrule" && params.length > 0) { + // const { name } = node; + // const lowercasedName = node.name.toLowerCase(); + + // if (name === "warn" || name === "error") { + // node.params = { + // type: "media-unknown", + // value: params, + // }; + + // return node; + // } + + // if (name === "extend" || name === "nest") { + // node.selector = parseSelector(params); + // delete node.params; + + // return node; + // } + + // if (name === "at-root") { + // if (/^\(\s*(?:without|with)\s*:.+\)$/su.test(params)) { + // node.params = parseValue(params, options); + // } else { + // node.selector = parseSelector(params); + // delete node.params; + // } + + // return node; + // } + + // if (isModuleRuleName(lowercasedName)) { + // node.import = true; + // delete node.filename; + // node.params = parseValue(params, options); + // return node; + // } + + // if ( + // [ + // "namespace", + // "supports", + // "if", + // "else", + // "for", + // "each", + // "while", + // "debug", + // "mixin", + // "include", + // "function", + // "return", + // "define-mixin", + // "add-mixin", + // ].includes(name) + // ) { + // // Remove unnecessary spaces in SCSS variable arguments + // // Move spaces after the `...`, so we can keep the range correct + // params = params.replace(/(\$\S+?)(\s+)?\.{3}/u, "$1...$2"); + // // Remove unnecessary spaces before SCSS control, mixin and function directives + // // Move spaces after the `(`, so we can keep the range correct + // params = params.replace(/^(?!if)(\S+)(\s+)\(/u, "$1($2"); + + // node.value = parseValue(params, options); + // delete node.params; + + // return node; + // } + + // if (["media", "custom-media"].includes(lowercasedName)) { + // if (params.includes("#{")) { + // // Workaround for media at rule with scss interpolation + // return { + // type: "media-unknown", + // value: params, + // }; + // } + + // node.params = parseMediaQuery(params); + + // return node; + // } + + // node.params = params; + + // return node; + // } } return node; @@ -394,7 +397,7 @@ function parseWithParser(parse, text, options) { } options.originalText = text; - result = parseNestedCSS(addTypePrefix(result, "css-"), options); + result = parseNestedCSS(result, options); calculateLoc(result, text); @@ -424,7 +427,12 @@ function parseLess(text, options = {}) { } function parseScss(text, options = {}) { - return parseWithParser(postcssScssParse, text, options); + return parseWithParser( + (text, opts) => postcssScssParse.parse(text, opts), + text, + options, + ); + // return parseWithParser(postcssScssParse, text, options); } const postCssParser = { diff --git a/src/language-css/print/parenthesized-value-group.js b/src/language-css/print/parenthesized-value-group.js index 197b55fb35c2..a5561bf242fc 100644 --- a/src/language-css/print/parenthesized-value-group.js +++ b/src/language-css/print/parenthesized-value-group.js @@ -192,4 +192,4 @@ function chunk(array, size) { return result; } -export { printParenthesizedValueGroup, shouldBreakList }; +export { chunk, printParenthesizedValueGroup, shouldBreakList }; diff --git a/src/language-css/print/sequence.js b/src/language-css/print/sequence.js index e02afc8c1a01..72a29270766a 100644 --- a/src/language-css/print/sequence.js +++ b/src/language-css/print/sequence.js @@ -9,7 +9,7 @@ function printSequence(path, options, print) { path.each(() => { const { node, previous } = path; if ( - previous?.type === "css-comment" && + previous?.type === "comment" && previous.text.trim() === "prettier-ignore" ) { parts.push(options.originalText.slice(locStart(node), locEnd(node))); @@ -23,14 +23,14 @@ function printSequence(path, options, print) { const { next } = path; if ( - (next.type === "css-comment" && + (next.type === "comment" && !hasNewline(options.originalText, locStart(next), { backwards: true, }) && !isFrontMatter(node)) || - (next.type === "css-atrule" && + (next.type === "atrule" && next.name === "else" && - node.type !== "css-comment") + node.type !== "comment") ) { parts.push(" "); } else { diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index 25b5ecb0cc68..81073cebfb6b 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -1,6 +1,7 @@ import { breakParent, dedent, + fill, group, hardline, ifBreak, @@ -10,6 +11,7 @@ import { softline, } from "../document/builders.js"; import { removeLines } from "../document/utils.js"; +import { assertDocArray } from "../document/utils/assert-doc.js"; import isNonEmptyArray from "../utils/is-non-empty-array.js"; import printString from "../utils/print-string.js"; import UnexpectedNodeError from "../utils/unexpected-node-error.js"; @@ -18,7 +20,6 @@ import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; import { locEnd, locStart } from "./loc.js"; import { insertPragma } from "./pragma.js"; -import printCommaSeparatedValueGroup from "./print/comma-separated-value-group.js"; import { adjustNumbers, adjustStrings, @@ -26,24 +27,21 @@ import { printUnit, quoteAttributeValue, } from "./print/misc.js"; -import { - printParenthesizedValueGroup, - shouldBreakList, -} from "./print/parenthesized-value-group.js"; +import { chunk, shouldBreakList } from "./print/parenthesized-value-group.js"; import printSequence from "./print/sequence.js"; import { hasComposesNode, hasParensAroundNode, insideAtRuleNode, insideICSSRuleNode, - insideValueFunctionNode, isDetachedRulesetCallNode, isDetachedRulesetDeclarationNode, + isInlineValueCommentNode, isKeyframeAtRuleKeywords, - isMediaAndSupportsKeywords, isSCSSControlDirectiveNode, isTemplatePlaceholderNode, isTemplatePropNode, + isURLFunctionNode, isWideKeywords, lastLineHasInlineComment, maybeToLowerCase, @@ -52,12 +50,13 @@ import { function genericPrint(path, options, print) { const { node } = path; - switch (node.type) { + // TODO: Do we want to *just* check for `sassType`? + switch (node.type ?? node.sassType) { case "front-matter": return [node.raw, hardline]; - case "css-root": { + case "root": { const nodes = printSequence(path, options, print); - let after = node.raws.after.trim(); + let after = node.raws.after?.trim() ?? ""; if (after.startsWith(";")) { after = after.slice(1).trim(); } @@ -69,23 +68,21 @@ function genericPrint(path, options, print) { node.nodes.length > 0 ? hardline : "", ]; } - case "css-comment": { - const isInlineComment = node.inline || node.raws.inline; - + case "comment": { const text = options.originalText.slice(locStart(node), locEnd(node)); - return isInlineComment ? text.trimEnd() : text; + return isInlineValueCommentNode(node) ? text.trimEnd() : text; } - case "css-rule": + case "rule": return [ - print("selector"), + print("selectorTemp"), node.important ? " !important" : "", node.nodes ? [ - node.selector?.type === "selector-unknown" && - lastLineHasInlineComment(node.selector.value) + node.selectorTemp?.type === "selector-unknown" && + lastLineHasInlineComment(node.selectorTemp.value) ? line - : node.selector + : node.selectorTemp ? " " : "", "{", @@ -99,18 +96,18 @@ function genericPrint(path, options, print) { : ";", ]; - case "css-decl": { + case "decl": { const parentNode = path.parent; - const { between: rawBetween } = node.raws; - const trimmedBetween = rawBetween.trim(); + const trimmedBetween = rawBetween?.trim() ?? ":"; const isColon = trimmedBetween === ":"; + let value = node.expression ? print("expression") : node.value; const isValueAllSpace = - typeof node.value === "string" && /^ *$/u.test(node.value); - let value = typeof node.value === "string" ? node.value : print("value"); + typeof node.value === "string" && /^ *$/u.test(value); value = hasComposesNode(node) ? removeLines(value) : value; + // TODO: Haven't checked this path yet if ( !isColon && lastLineHasInlineComment(trimmedBetween) && @@ -123,24 +120,25 @@ function genericPrint(path, options, print) { } return [ - node.raws.before.replaceAll(/[\s;]/gu, ""), + node.raws.before?.replaceAll(/[\s;]/gu, "") ?? "", // Less variable - (parentNode.type === "css-atrule" && parentNode.variable) || + (parentNode.type === "atrule" && parentNode.variable) || insideICSSRuleNode(path) ? node.prop : maybeToLowerCase(node.prop), trimmedBetween.startsWith("//") ? " " : "", trimmedBetween, node.extend || isValueAllSpace ? "" : " ", - options.parser === "less" && node.extend && node.selector - ? ["extend(", print("selector"), ")"] + options.parser === "less" && node.extend && node.selectorTemp + ? ["extend(", print("selectorTemp"), ")"] : "", value, - node.raws.important - ? node.raws.important.replace(/\s*!\s*important/iu, " !important") - : node.important - ? " !important" - : "", + // TODO: using `node.important` throws an error, not yet implemented + // node.raws.important + // ? node.raws.important.replace(/\s*!\s*important/iu, " !important") + // : node.important + // ? " !important" + // : "", node.raws.scssDefault ? node.raws.scssDefault.replace(/\s*!default/iu, " !default") : node.scssDefault @@ -167,7 +165,7 @@ function genericPrint(path, options, print) { : ";", ]; } - case "css-atrule": { + case "atrule": { const parentNode = path.parent; const isTemplatePlaceholderNodeWithoutSemiColon = isTemplatePlaceholderNode(node) && @@ -177,7 +175,7 @@ function genericPrint(path, options, print) { if (options.parser === "less") { if (node.mixin) { return [ - print("selector"), + print("selectorTemp"), node.important ? " !important" : "", isTemplatePlaceholderNodeWithoutSemiColon ? "" : ";", ]; @@ -215,7 +213,7 @@ function genericPrint(path, options, print) { } const isImportUnknownValueEndsWithSemiColon = node.name === "import" && - node.params?.type === "value-unknown" && + node.params?.type === "unknown" && node.params.value.endsWith(";"); return [ @@ -246,7 +244,7 @@ function genericPrint(path, options, print) { typeof node.params === "string" ? node.params : print("params"), ] : "", - node.selector ? indent([" ", print("selector")]) : "", + node.selectorTemp ? indent([" ", print("selectorTemp")]) : "", node.value ? group([ " ", @@ -264,11 +262,11 @@ function genericPrint(path, options, print) { ? [ isSCSSControlDirectiveNode(node, options) ? "" - : (node.selector && - !node.selector.nodes && - typeof node.selector.value === "string" && - lastLineHasInlineComment(node.selector.value)) || - (!node.selector && + : (node.selectorTemp && + !node.selectorTemp.nodes && + typeof node.selectorTemp.value === "string" && + lastLineHasInlineComment(node.selectorTemp.value)) || + (!node.selectorTemp && typeof node.params === "string" && lastLineHasInlineComment(node.params)) ? line @@ -341,7 +339,7 @@ function genericPrint(path, options, print) { return group([ insideAtRuleNode(path, "custom-selector") ? [ - path.findAncestor((node) => node.type === "css-atrule") + path.findAncestor((node) => node.type === "atrule") .customSelector, line, ] @@ -457,11 +455,11 @@ function genericPrint(path, options, print) { case "selector-unknown": { const ruleAncestorNode = path.findAncestor( - (node) => node.type === "css-rule", + (node) => node.type === "rule", ); // Nested SCSS property - if (ruleAncestorNode?.isSCSSNesterProperty) { + if (ruleAncestorNode?.isSCSSNestedProperty) { return adjustNumbers( adjustStrings(maybeToLowerCase(node.value), options), ); @@ -469,9 +467,9 @@ function genericPrint(path, options, print) { // originalText has to be used for Less, see replaceQuotesInInlineComments in loc.js const parentNode = path.parent; - if (parentNode.raws?.selector) { + if (parentNode.raws?.selectorTemp) { const start = locStart(parentNode); - const end = start + parentNode.raws.selector.length; + const end = start + parentNode.raws.selectorTemp.length; return options.originalText.slice(start, end).trim(); } @@ -493,77 +491,155 @@ function genericPrint(path, options, print) { return node.value; } - // postcss-values-parser - case "value-value": - case "value-root": - return print("group"); - case "value-comment": - return options.originalText.slice(locStart(node), locEnd(node)); - - case "value-comma_group": - return printCommaSeparatedValueGroup(path, options, print); + // postcss-values-parser + // case "value": + // case "root": + // return print("group"); + + // case "comment": + // return options.originalText.slice(locStart(node), locEnd(node)); + + // case "comma_group": + // return printCommaSeparatedValueGroup(path, options, print); + + // case "paren_group": + // return printParenthesizedValueGroup(path, options, print); + + case "color": + return node.value.toString(); + + case "function-call": + return node.toString(); + + // case "func": + // return [ + // node.value, + // insideAtRuleNode(path, "supports") && isMediaAndSupportsKeywords(node) + // ? " " + // : "", + // print("group"), + // ]; + + case "list": { + // TODO: Every node needs a `type` + node.type = "list"; + // TODO: Need a way to determine if this list is wrapped in parens + const hasParens = false; + const parentNode = path.parent; + const nodes = path.map( + ({ node }) => (typeof node === "string" ? node : print()), + "nodes", + ); + // TODO: It looks like `url()` is just parsed as a `string` + 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 [hasParens ? "(" : "", join(",", nodes), hasParens ? ")" : ""]; + } + if (!hasParens) { + const forceHardLine = path.match( + (node) => + // TODO: The original `shouldBreakList()` checks for comma-groups + node.some((node) => + ["list", "binary-operation"].includes(node.sassType), + ), + (node, key) => + key === "expression" && + ((node.type === "decl" && !node.prop.startsWith("--")) || + (node.type === "atrule" && node.variable)), + ); + assertDocArray(nodes); + const separator = (node.separator ?? "").trim(); + const withSeparator = chunk(join(separator, nodes), 2); + const parts = join(forceHardLine ? hardline : line, withSeparator); + return indent( + forceHardLine + ? [hardline, parts] + : group([parentNode.type === "decl" ? softline : "", fill(parts)]), + ); + } + // TODO: We're not handling the logic for a paren-wrapped list + return null; + } - case "value-paren_group": - return printParenthesizedValueGroup(path, options, print); + // case "paren": + // return node.value; - case "value-func": + case "number": return [ - node.value, - insideAtRuleNode(path, "supports") && isMediaAndSupportsKeywords(node) - ? " " - : "", - print("group"), + printCssNumber(node.value.toString()), + printUnit(node.unit ?? ""), ]; - case "value-paren": - return node.value; - - case "value-number": - return [printCssNumber(node.value), printUnit(node.unit)]; - - case "value-operator": - return node.value; - - case "value-word": - if ((node.isColor && node.isHex) || isWideKeywords(node.value)) { - return node.value.toLowerCase(); + case "binary-operation": + node.type = "binary-operation"; + return [print("left"), " ", node.operator, " ", print("right")]; + + case "parenthesized": + node.type = "parenthesized"; + return group(["(", indent([softline, print("inParens")]), softline, ")"]); + + case "selector-expr": + case "variable": + return node.toString(); + + // case "word": + // if ((node.isColor && node.isHex) || isWideKeywords(node.value)) { + // return node.value.toLowerCase(); + // } + + // return node.value; + + // case "colon": { + // const { previous } = path; + // return group([ + // node.value, + // // Don't add spaces on escaped colon `:`, e.g: grid-template-rows: [row-1-00\:00] auto; + // (typeof previous?.value === "string" && + // previous.value.endsWith("\\")) || + // // Don't add spaces on `:` in `url` function (i.e. `url(fbglyph: cross-outline, fig-white)`) + // insideValueFunctionNode(path, "url") + // ? "" + // : line, + // ]); + // } + + case "string": { + const text = node.toString().trim(); + if (node.quotes) { + return printString(text, options); } - - return node.value; - - case "value-colon": { - const { previous } = path; - return group([ - node.value, - // Don't add spaces on escaped colon `:`, e.g: grid-template-rows: [row-1-00\:00] auto; - (typeof previous?.value === "string" && - previous.value.endsWith("\\")) || - // Don't add spaces on `:` in `url` function (i.e. `url(fbglyph: cross-outline, fig-white)`) - insideValueFunctionNode(path, "url") - ? "" - : line, - ]); + if (isWideKeywords(text)) { + return text.toLowerCase(); + } + return text; } - case "value-string": - return printString( - node.raws.quote + node.value + node.raws.quote, - options, - ); - case "value-atword": - return ["@", node.value]; + // case "atword": + // return ["@", node.value]; - case "value-unicode-range": - return node.value; + // case "unicode-range": + // return node.value; - case "value-unknown": - return node.value; + // case "unknown": + // return node.value; - case "value-comma": // Handled in `value-comma_group` + // case "comma": // Handled in `value-comma_group` default: /* c8 ignore next */ - throw new UnexpectedNodeError(node, "PostCSS"); + throw new UnexpectedNodeError( + node, + "PostCSS", + node.type ? "type" : "sassType", + ); } } diff --git a/src/language-css/utils/index.js b/src/language-css/utils/index.js index d78cab4a5ae9..39aa22ae0421 100644 --- a/src/language-css/utils/index.js +++ b/src/language-css/utils/index.js @@ -27,9 +27,7 @@ const colorAdjusterFunctions = new Set([ ]); function getPropOfDeclNode(path) { - return path - .findAncestor((node) => node.type === "css-decl") - ?.prop?.toLowerCase(); + return path.findAncestor((node) => node.type === "decl")?.prop?.toLowerCase(); } const wideKeywords = new Set(["initial", "inherit", "unset", "revert"]); @@ -39,7 +37,7 @@ function isWideKeywords(value) { function isKeyframeAtRuleKeywords(path, value) { const atRuleAncestorNode = path.findAncestor( - (node) => node.type === "css-atrule", + (node) => node.type === "atrule", ); return ( atRuleAncestorNode?.name?.toLowerCase().endsWith("keyframes") && @@ -67,9 +65,7 @@ function insideValueFunctionNode(path, functionName) { } function insideICSSRuleNode(path) { - const ruleAncestorNode = path.findAncestor( - (node) => node.type === "css-rule", - ); + const ruleAncestorNode = path.findAncestor((node) => node.type === "rule"); const selector = ruleAncestorNode?.raws?.selector; return ( @@ -83,7 +79,7 @@ function insideAtRuleNode(path, atRuleNameOrAtRuleNames) { ? atRuleNameOrAtRuleNames : [atRuleNameOrAtRuleNames]; const atRuleAncestorNode = path.findAncestor( - (node) => node.type === "css-atrule", + (node) => node.type === "atrule", ); return ( @@ -97,7 +93,7 @@ function insideURLFunctionInImportAtRuleNode(path) { return ( node.groups[0].value === "url" && node.groups.length === 2 && - path.findAncestor((node) => node.type === "css-atrule")?.name === "import" + path.findAncestor((node) => node.type === "atrule")?.name === "import" ); } @@ -185,7 +181,7 @@ function isRelationalOperatorNode(node) { function isSCSSControlDirectiveNode(node, options) { return ( options.parser === "scss" && - node.type === "css-atrule" && + node.type === "atrule" && ["if", "else", "for", "each", "while"].includes(node.name) ); } @@ -268,7 +264,7 @@ function isSCSSMapItemNode(path, options) { return false; } - const declNode = path.findAncestor((node) => node.type === "css-decl"); + const declNode = path.findAncestor((node) => node.type === "decl"); // SCSS map declaration (i.e. `$map: (key: value, other-key: other-value)`) if (declNode?.prop?.startsWith("$")) { @@ -289,7 +285,7 @@ function isSCSSMapItemNode(path, options) { } function isInlineValueCommentNode(node) { - return node.type === "value-comment" && node.inline; + return node.type === "comment" && node.sassType === "sass-comment"; } function isHashNode(node) { diff --git a/src/language-css/visitor-keys.js b/src/language-css/visitor-keys.js index 8e1ff40918b6..6122482f09c3 100644 --- a/src/language-css/visitor-keys.js +++ b/src/language-css/visitor-keys.js @@ -1,10 +1,10 @@ const visitorKeys = { "front-matter": [], - "css-root": ["frontMatter", "nodes"], - "css-comment": [], - "css-rule": ["selector", "nodes"], - "css-decl": ["value", "selector", "nodes"], - "css-atrule": ["selector", "params", "value", "nodes"], + root: ["frontMatter", "nodes"], + comment: [], + rule: ["selectorTemp", "nodes"], + decl: ["value", "selectorTemp", "nodes", "expression"], + atrule: ["selectorTemp", "params", "value", "nodes"], "media-query-list": ["nodes"], "media-query": ["nodes"], "media-type": [], @@ -28,22 +28,26 @@ const visitorKeys = { "selector-pseudo": ["nodes"], "selector-nesting": [], "selector-unknown": [], - "value-value": ["group"], - "value-root": ["group"], - "value-comment": [], - "value-comma_group": ["groups"], - "value-paren_group": ["open", "groups", "close"], - "value-func": ["group"], - "value-paren": [], - "value-number": [], - "value-operator": [], - "value-word": [], - "value-colon": [], - "value-comma": [], - "value-string": [], - "value-atword": [], - "value-unicode-range": [], - "value-unknown": [], + list: ["nodes"], + parenthesized: ["inParens"], + "binary-operation": ["left", "right"], + string: [], + // "value-value": ["group"], + // "value-root": ["group"], + // "value-comment": [], + // "value-comma_group": ["groups"], + // "value-paren_group": ["open", "groups", "close"], + // "value-func": ["group"], + // "value-paren": [], + // "value-number": [], + // "value-operator": [], + // "value-word": [], + // "value-colon": [], + // "value-comma": [], + // "value-string": [], + // "value-atword": [], + // "value-unicode-range": [], + // "value-unknown": [], }; export default visitorKeys; diff --git a/yarn.lock b/yarn.lock index 8ef4675428d6..d3b87a8ee59e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1938,6 +1938,150 @@ __metadata: languageName: node linkType: hard +"@parcel/watcher-android-arm64@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-android-arm64@npm:2.5.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-darwin-arm64@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-darwin-arm64@npm:2.5.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-darwin-x64@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-darwin-x64@npm:2.5.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher-freebsd-x64@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-freebsd-x64@npm:2.5.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm-glibc@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-linux-arm-glibc@npm:2.5.1" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm-musl@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-linux-arm-musl@npm:2.5.1" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm64-glibc@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.5.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm64-musl@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-linux-arm64-musl@npm:2.5.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-linux-x64-glibc@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-linux-x64-glibc@npm:2.5.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-x64-musl@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-linux-x64-musl@npm:2.5.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-win32-arm64@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-win32-arm64@npm:2.5.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-win32-ia32@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-win32-ia32@npm:2.5.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@parcel/watcher-win32-x64@npm:2.5.1": + version: 2.5.1 + resolution: "@parcel/watcher-win32-x64@npm:2.5.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher@npm:^2.4.1": + version: 2.5.1 + resolution: "@parcel/watcher@npm:2.5.1" + dependencies: + "@parcel/watcher-android-arm64": "npm:2.5.1" + "@parcel/watcher-darwin-arm64": "npm:2.5.1" + "@parcel/watcher-darwin-x64": "npm:2.5.1" + "@parcel/watcher-freebsd-x64": "npm:2.5.1" + "@parcel/watcher-linux-arm-glibc": "npm:2.5.1" + "@parcel/watcher-linux-arm-musl": "npm:2.5.1" + "@parcel/watcher-linux-arm64-glibc": "npm:2.5.1" + "@parcel/watcher-linux-arm64-musl": "npm:2.5.1" + "@parcel/watcher-linux-x64-glibc": "npm:2.5.1" + "@parcel/watcher-linux-x64-musl": "npm:2.5.1" + "@parcel/watcher-win32-arm64": "npm:2.5.1" + "@parcel/watcher-win32-ia32": "npm:2.5.1" + "@parcel/watcher-win32-x64": "npm:2.5.1" + detect-libc: "npm:^1.0.3" + is-glob: "npm:^4.0.3" + micromatch: "npm:^4.0.5" + node-addon-api: "npm:^7.0.0" + node-gyp: "npm:latest" + dependenciesMeta: + "@parcel/watcher-android-arm64": + optional: true + "@parcel/watcher-darwin-arm64": + optional: true + "@parcel/watcher-darwin-x64": + optional: true + "@parcel/watcher-freebsd-x64": + optional: true + "@parcel/watcher-linux-arm-glibc": + optional: true + "@parcel/watcher-linux-arm-musl": + optional: true + "@parcel/watcher-linux-arm64-glibc": + optional: true + "@parcel/watcher-linux-arm64-musl": + optional: true + "@parcel/watcher-linux-x64-glibc": + optional: true + "@parcel/watcher-linux-x64-musl": + optional: true + "@parcel/watcher-win32-arm64": + optional: true + "@parcel/watcher-win32-ia32": + optional: true + "@parcel/watcher-win32-x64": + optional: true + checksum: 10/2cc1405166fb3016b34508661902ab08b6dec59513708165c633c84a4696fff64f9b99ea116e747c121215e09619f1decab6f0350d1cb26c9210b98eb28a6a56 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -2964,6 +3108,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^4.0.0": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10/bf2a575ea5596000e88f5db95461a9d59ad2047e939d5a4aac59dd472d126be8f1c1ff3c7654b477cf532d18f42a97279ef80ee847972fd2a25410bf00b80b59 + languageName: node + linkType: hard + "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -3477,6 +3630,15 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^1.0.3": + version: 1.0.3 + resolution: "detect-libc@npm:1.0.3" + bin: + detect-libc: ./bin/detect-libc.js + checksum: 10/3849fe7720feb153e4ac9407086956e073f1ce1704488290ef0ca8aab9430a8d48c8a9f8351889e7cdc64e5b1128589501e4fef48f3a4a49ba92cd6d112d0757 + languageName: node + linkType: hard + "detect-newline@npm:^3.0.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" @@ -5170,6 +5332,13 @@ __metadata: languageName: node linkType: hard +"immutable@npm:^5.0.2": + version: 5.1.2 + resolution: "immutable@npm:5.1.2" + checksum: 10/2d538ccf97c3e573a351d7d8a0554791d7a6527029cafd9d22507ce16ff7cb90bd8f1294e7c5166d9d471f8bbc0861989c4fb05caf96d6e7ee42975482bec2a7 + languageName: node + linkType: hard + "import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.1": version: 3.3.1 resolution: "import-fresh@npm:3.3.1" @@ -6623,7 +6792,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:4.0.8, micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": +"micromatch@npm:4.0.8, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -6845,6 +7014,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^7.0.0": + version: 7.1.1 + resolution: "node-addon-api@npm:7.1.1" + dependencies: + node-gyp: "npm:latest" + checksum: 10/ee1e1ed6284a2f8cd1d59ac6175ecbabf8978dcf570345e9a8095a9d0a2b9ced591074ae77f9009287b00c402352b38aa9322a34f2199cdc9f567b842a636b94 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 11.2.0 resolution: "node-gyp@npm:11.2.0" @@ -7365,7 +7543,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.5.3": +"postcss@npm:8.5.3, postcss@npm:>=8.4.41 <8.6.0": version: 8.5.3 resolution: "postcss@npm:8.5.3" dependencies: @@ -7498,6 +7676,7 @@ __metadata: remark-math: "npm:3.0.1" remark-parse: "npm:8.0.3" rollup-plugin-license: "npm:3.6.0" + sass-parser: "npm:0.4.21" sdbm: "npm:2.0.0" search-closest: "npm:1.1.0" semver: "npm:7.7.2" @@ -7707,6 +7886,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^4.0.1": + version: 4.1.2 + resolution: "readdirp@npm:4.1.2" + checksum: 10/7b817c265940dba90bb9c94d82920d76c3a35ea2d67f9f9d8bd936adcfe02d50c802b14be3dd2e725e002dddbe2cc1c7a0edfb1bc3a365c9dfd5a61e612eea1e + languageName: node + linkType: hard + "refa@npm:^0.12.0, refa@npm:^0.12.1": version: 0.12.1 resolution: "refa@npm:0.12.1" @@ -7936,6 +8122,33 @@ __metadata: languageName: node linkType: hard +"sass-parser@npm:0.4.21": + version: 0.4.21 + resolution: "sass-parser@npm:0.4.21" + dependencies: + postcss: "npm:>=8.4.41 <8.6.0" + sass: "npm:^1.88.0" + checksum: 10/bfa8c55b12e2a4fdb98a7f08274a88e2476da003b3437756957c31b44508e9593d5832845d22707077307a1fa1736615ff95ef4fd9c23a678a34daba744b0f0a + languageName: node + linkType: hard + +"sass@npm:^1.88.0": + version: 1.88.0 + resolution: "sass@npm:1.88.0" + dependencies: + "@parcel/watcher": "npm:^2.4.1" + chokidar: "npm:^4.0.0" + immutable: "npm:^5.0.2" + source-map-js: "npm:>=0.6.2 <2.0.0" + dependenciesMeta: + "@parcel/watcher": + optional: true + bin: + sass: sass.js + checksum: 10/dfe4aec19409d7eabca793bb5b464d81b636c853be33157b1d8121959be3f066f22ebe1d233d0eaaf29fac21cd746ba29acb50ed9bbf6e0a3b892c8e5ed0daa5 + languageName: node + linkType: hard + "scslre@npm:^0.3.0": version: 0.3.0 resolution: "scslre@npm:0.3.0" @@ -8206,7 +8419,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.2.1": +"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 10/ff9d8c8bf096d534a5b7707e0382ef827b4dd360a577d3f34d2b9f48e12c9d230b5747974ee7c607f0df65113732711bb701fe9ece3c7edbd43cb2294d707df3