Files
test-repo/src/language-css/parse/parse-value.js
T
renovate[bot]andfisker 1a4f6fe1fd Update ESLint related dependencies (major) (#18907)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: fisker <lionkay@gmail.com>
2026-03-04 23:22:14 +08:00

199 lines
5.3 KiB
JavaScript

import PostcssValuesParser from "postcss-values-parser/lib/parser.js";
import isObject from "../../utilities/is-object.js";
import getFunctionArgumentsText from "../utilities/get-function-arguments-text.js";
import getValueRoot from "../utilities/get-value-root.js";
import hasSCSSInterpolation from "../utilities/has-scss-interpolation.js";
import hasStringOrFunction from "../utilities/has-string-or-function.js";
import isSCSSVariable from "../utilities/is-scss-variable.js";
import parseSelector from "./parse-selector.js";
import { addTypePrefix } from "./utilities.js";
const isClosingParenthesis = (node) =>
node.type === "paren" && node.value === ")";
function parseValueNode(valueNode, options) {
const { nodes } = valueNode;
let parenGroup = {
open: null,
close: null,
groups: [],
type: "paren_group",
};
const parenGroupStack = [parenGroup];
const rootParenGroup = parenGroup;
let commaGroup = {
groups: [],
type: "comma_group",
};
const commaGroupStack = [commaGroup];
for (let i = 0; i < nodes.length; ++i) {
const node = nodes[i];
if (
options.parser === "scss" &&
node.type === "number" &&
node.unit === ".." &&
node.value.endsWith(".")
) {
// Work around postcss bug parsing `50...` as `50.` with unit `..`
// Set the unit to `...` to "accidentally" have arbitrary arguments work in the same way that cases where the node already had a unit work.
// For example, 50px... is parsed as `50` with unit `px...` already by postcss-values-parser.
node.value = node.value.slice(0, -1);
node.unit = "...";
}
if (node.type === "func" && node.value === "selector") {
node.group.groups = [
parseSelector(
getValueRoot(valueNode).text.slice(
node.group.open.sourceIndex + 1,
node.group.close.sourceIndex,
),
),
];
}
if (node.type === "func" && node.value === "url") {
const groups = node.group?.groups ?? [];
// Create a view with any top-level comma groups flattened.
let groupList = [];
for (let i = 0; i < groups.length; i++) {
const group = groups[i];
if (group.type === "comma_group") {
groupList = [...groupList, ...group.groups];
} else {
groupList.push(group);
}
}
// Stringify if the value parser can't handle the content.
if (
hasSCSSInterpolation(groupList) ||
(!hasStringOrFunction(groupList) &&
!isSCSSVariable(groupList[0], options))
) {
node.group.groups = [getFunctionArgumentsText(node)];
}
}
if (node.type === "paren" && node.value === "(") {
parenGroup = {
open: node,
close: null,
groups: [],
type: "paren_group",
};
parenGroupStack.push(parenGroup);
commaGroup = {
groups: [],
type: "comma_group",
};
commaGroupStack.push(commaGroup);
} else if (isClosingParenthesis(node)) {
if (commaGroup.groups.length > 0) {
parenGroup.groups.push(commaGroup);
}
parenGroup.close = node;
/* c8 ignore next 3 */
if (commaGroupStack.length === 1) {
throw new Error("Unbalanced parenthesis");
}
commaGroupStack.pop();
commaGroup = commaGroupStack.at(-1);
commaGroup.groups.push(parenGroup);
parenGroupStack.pop();
parenGroup = parenGroupStack.at(-1);
} else if (node.type === "comma") {
// Trialing comma
if (
i === nodes.length - 3 &&
nodes[i + 1].type === "comment" &&
isClosingParenthesis(nodes[i + 2])
) {
continue;
}
parenGroup.groups.push(commaGroup);
commaGroup = {
groups: [],
type: "comma_group",
};
commaGroupStack[commaGroupStack.length - 1] = commaGroup;
} else {
commaGroup.groups.push(node);
}
}
if (commaGroup.groups.length > 0) {
parenGroup.groups.push(commaGroup);
}
return rootParenGroup;
}
function flattenGroups(node) {
if (
node.type === "paren_group" &&
!node.open &&
!node.close &&
node.groups.length === 1
) {
return flattenGroups(node.groups[0]);
}
if (node.type === "comma_group" && node.groups.length === 1) {
return flattenGroups(node.groups[0]);
}
if (node.type === "paren_group" || node.type === "comma_group") {
return { ...node, groups: node.groups.map(flattenGroups) };
}
return node;
}
function parseNestedValue(node, options) {
if (isObject(node)) {
for (const key in node) {
if (key !== "parent") {
parseNestedValue(node[key], options);
if (key === "nodes") {
node.group = flattenGroups(parseValueNode(node, options));
delete node[key];
}
}
}
}
return node;
}
function parseValue(value, options) {
// Inline javascript in Less
if (options.parser === "less" && value.startsWith("~`")) {
return { type: "value-unknown", value };
}
let result;
try {
result = new PostcssValuesParser(value, { loose: true }).parse();
} catch {
return {
type: "value-unknown",
value,
};
}
result.text = value;
const parsedResult = parseNestedValue(result, options);
return addTypePrefix(parsedResult, "value-", /^selector-/);
}
export default parseValue;