2022-02-21 14:22:51 +08:00
|
|
|
import createError from "../common/parser-create-error.js";
|
|
|
|
|
import parseFrontMatter from "../utils/front-matter/parse.js";
|
|
|
|
|
import { hasPragma } from "./pragma.js";
|
|
|
|
|
import { locStart, locEnd } from "./loc.js";
|
|
|
|
|
import { calculateLoc, replaceQuotesInInlineComments } from "./loc.js";
|
|
|
|
|
import hasSCSSInterpolation from "./utils/has-scss-interpolation.js";
|
|
|
|
|
import hasStringOrFunction from "./utils/has-string-or-function.js";
|
|
|
|
|
import isSCSSNestedPropertyNode from "./utils/is-scss-nested-property-node.js";
|
|
|
|
|
import isSCSSVariable from "./utils/is-scss-variable.js";
|
|
|
|
|
import stringifyNode from "./utils/stringify-node.js";
|
|
|
|
|
import isModuleRuleName from "./utils/is-module-rule-name.js";
|
2017-05-28 21:06:09 -07:00
|
|
|
|
2020-07-03 03:24:27 +02:00
|
|
|
const getHighestAncestor = (node) => {
|
|
|
|
|
while (node.parent) {
|
|
|
|
|
node = node.parent;
|
|
|
|
|
}
|
|
|
|
|
return node;
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
async function parseValueNode(valueNode, options) {
|
2020-07-03 03:24:27 +02:00
|
|
|
const { nodes } = valueNode;
|
2017-05-28 21:06:09 -07:00
|
|
|
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) {
|
2017-06-02 23:11:05 +02:00
|
|
|
const node = nodes[i];
|
2018-02-02 20:39:53 +03:00
|
|
|
|
2020-06-18 11:12:31 +02:00
|
|
|
if (
|
2020-11-09 16:47:12 +08:00
|
|
|
options.parser === "scss" &&
|
2020-06-18 11:12:31 +02:00
|
|
|
node.type === "number" &&
|
|
|
|
|
node.unit === ".." &&
|
2022-09-18 17:50:14 +08:00
|
|
|
node.value.endsWith(".")
|
2020-06-18 11:12:31 +02:00
|
|
|
) {
|
|
|
|
|
// 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 = "...";
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-03 03:24:27 +02:00
|
|
|
if (node.type === "func" && node.value === "selector") {
|
|
|
|
|
node.group.groups = [
|
2022-08-15 22:30:46 +09:00
|
|
|
await parseSelector(
|
2020-07-03 03:24:27 +02:00
|
|
|
getHighestAncestor(valueNode).text.slice(
|
|
|
|
|
node.group.open.sourceIndex + 1,
|
|
|
|
|
node.group.close.sourceIndex
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-29 06:09:15 -07:00
|
|
|
if (node.type === "func" && node.value === "url") {
|
|
|
|
|
const groups = (node.group && 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") {
|
2021-01-26 09:46:14 +08:00
|
|
|
groupList = [...groupList, ...group.groups];
|
2020-05-29 06:09:15 -07:00
|
|
|
} else {
|
|
|
|
|
groupList.push(group);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stringify if the value parser can't handle the content.
|
|
|
|
|
if (
|
|
|
|
|
hasSCSSInterpolation(groupList) ||
|
2020-11-09 16:47:12 +08:00
|
|
|
(!hasStringOrFunction(groupList) &&
|
|
|
|
|
!isSCSSVariable(groupList[0], options))
|
2020-05-29 06:09:15 -07:00
|
|
|
) {
|
|
|
|
|
const stringifiedContent = stringifyNode({
|
|
|
|
|
groups: node.group.groups,
|
|
|
|
|
});
|
|
|
|
|
node.group.groups = [stringifiedContent.trim()];
|
|
|
|
|
}
|
2018-02-02 20:39:53 +03:00
|
|
|
}
|
2017-06-02 23:11:05 +02:00
|
|
|
if (node.type === "paren" && node.value === "(") {
|
2017-05-28 21:06:09 -07:00
|
|
|
parenGroup = {
|
2017-06-02 23:11:05 +02:00
|
|
|
open: node,
|
2017-05-28 21:06:09 -07:00
|
|
|
close: null,
|
|
|
|
|
groups: [],
|
|
|
|
|
type: "paren_group",
|
|
|
|
|
};
|
|
|
|
|
parenGroupStack.push(parenGroup);
|
|
|
|
|
|
|
|
|
|
commaGroup = {
|
|
|
|
|
groups: [],
|
|
|
|
|
type: "comma_group",
|
|
|
|
|
};
|
|
|
|
|
commaGroupStack.push(commaGroup);
|
2017-06-02 23:11:05 +02:00
|
|
|
} else if (node.type === "paren" && node.value === ")") {
|
2021-01-04 10:11:57 +08:00
|
|
|
if (commaGroup.groups.length > 0) {
|
2017-05-28 21:06:09 -07:00
|
|
|
parenGroup.groups.push(commaGroup);
|
|
|
|
|
}
|
2017-06-02 23:11:05 +02:00
|
|
|
parenGroup.close = node;
|
2017-05-28 21:06:09 -07:00
|
|
|
|
2020-08-21 09:43:24 +08:00
|
|
|
/* istanbul ignore next */
|
2017-05-28 21:06:09 -07:00
|
|
|
if (commaGroupStack.length === 1) {
|
|
|
|
|
throw new Error("Unbalanced parenthesis");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commaGroupStack.pop();
|
2022-09-18 17:50:14 +08:00
|
|
|
commaGroup = commaGroupStack.at(-1);
|
2017-05-28 21:06:09 -07:00
|
|
|
commaGroup.groups.push(parenGroup);
|
|
|
|
|
|
|
|
|
|
parenGroupStack.pop();
|
2022-09-18 17:50:14 +08:00
|
|
|
parenGroup = parenGroupStack.at(-1);
|
2017-06-02 23:11:05 +02:00
|
|
|
} else if (node.type === "comma") {
|
2017-05-28 21:06:09 -07:00
|
|
|
parenGroup.groups.push(commaGroup);
|
|
|
|
|
commaGroup = {
|
|
|
|
|
groups: [],
|
|
|
|
|
type: "comma_group",
|
|
|
|
|
};
|
|
|
|
|
commaGroupStack[commaGroupStack.length - 1] = commaGroup;
|
|
|
|
|
} else {
|
2017-06-02 23:11:05 +02:00
|
|
|
commaGroup.groups.push(node);
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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") {
|
2020-01-19 07:20:58 +08:00
|
|
|
return { ...node, groups: node.groups.map(flattenGroups) };
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-03 03:24:27 +02:00
|
|
|
function addTypePrefix(node, prefix, skipPrefix) {
|
2017-05-28 21:06:09 -07:00
|
|
|
if (node && typeof node === "object") {
|
|
|
|
|
delete node.parent;
|
|
|
|
|
for (const key in node) {
|
2020-07-03 03:24:27 +02:00
|
|
|
addTypePrefix(node[key], prefix, skipPrefix);
|
2022-09-29 05:25:54 +03:00
|
|
|
if (
|
|
|
|
|
key === "type" &&
|
|
|
|
|
typeof node[key] === "string" &&
|
|
|
|
|
!node[key].startsWith(prefix) &&
|
|
|
|
|
(!skipPrefix || !skipPrefix.test(node[key]))
|
|
|
|
|
) {
|
|
|
|
|
node[key] = prefix + node[key];
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addMissingType(node) {
|
|
|
|
|
if (node && typeof node === "object") {
|
|
|
|
|
delete node.parent;
|
|
|
|
|
for (const key in node) {
|
|
|
|
|
addMissingType(node[key]);
|
|
|
|
|
}
|
|
|
|
|
if (!Array.isArray(node) && node.value && !node.type) {
|
|
|
|
|
node.type = "unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
async function parseNestedValue(node, options) {
|
2017-05-28 21:06:09 -07:00
|
|
|
if (node && typeof node === "object") {
|
|
|
|
|
for (const key in node) {
|
2020-07-03 03:24:27 +02:00
|
|
|
if (key !== "parent") {
|
2022-08-15 22:30:46 +09:00
|
|
|
await parseNestedValue(node[key], options);
|
2020-07-03 03:24:27 +02:00
|
|
|
if (key === "nodes") {
|
2022-08-15 22:30:46 +09:00
|
|
|
node.group = flattenGroups(await parseValueNode(node, options));
|
2020-07-03 03:24:27 +02:00
|
|
|
delete node[key];
|
|
|
|
|
}
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-03 03:24:27 +02:00
|
|
|
delete node.parent;
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
async function parseValue(value, options) {
|
|
|
|
|
const Parser = await import("postcss-values-parser/lib/parser.js").then(
|
|
|
|
|
(m) => m.default
|
|
|
|
|
);
|
|
|
|
|
const valueParser = (source, options) => new Parser(source, options);
|
2022-03-28 09:41:08 +08:00
|
|
|
|
2018-05-15 00:00:45 +03:00
|
|
|
let result = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
result = valueParser(value, { loose: true }).parse();
|
2021-02-25 18:28:55 +08:00
|
|
|
} catch {
|
2018-05-15 00:00:45 +03:00
|
|
|
return {
|
|
|
|
|
type: "value-unknown",
|
2020-01-14 19:25:24 +08:00
|
|
|
value,
|
2018-05-15 00:00:45 +03:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-16 02:48:25 +03:00
|
|
|
result.text = value;
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
const parsedResult = await parseNestedValue(result, options);
|
2018-05-15 00:00:45 +03:00
|
|
|
|
2020-07-03 03:24:27 +02:00
|
|
|
return addTypePrefix(parsedResult, "value-", /^selector-/);
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
async function parseSelector(selector) {
|
2018-05-15 00:00:45 +03:00
|
|
|
// If there's a comment inside of a selector, the parser tries to parse
|
|
|
|
|
// the content of the comment as selectors which turns it into complete
|
|
|
|
|
// garbage. Better to print the whole selector as-is and not try to parse
|
|
|
|
|
// and reformat it.
|
2020-03-10 07:14:34 +08:00
|
|
|
if (/\/\/|\/\*/.test(selector)) {
|
2018-05-15 00:00:45 +03:00
|
|
|
return {
|
|
|
|
|
type: "selector-unknown",
|
2020-03-10 07:14:34 +08:00
|
|
|
value: selector.trim(),
|
2018-05-15 00:00:45 +03:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
const SelectorProcessor = await import(
|
|
|
|
|
"postcss-selector-parser/dist/processor.js"
|
|
|
|
|
).then((m) => m.default);
|
|
|
|
|
const selectorParser = (processor) => new SelectorProcessor(processor);
|
2022-03-28 09:41:08 +08:00
|
|
|
|
2018-05-15 00:00:45 +03:00
|
|
|
let result = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
selectorParser((result_) => {
|
|
|
|
|
result = result_;
|
|
|
|
|
}).process(selector);
|
2021-02-25 18:28:55 +08:00
|
|
|
} catch {
|
2018-05-15 00:00:45 +03:00
|
|
|
// Fail silently. It's better to print it as is than to try and parse it
|
|
|
|
|
// Note: A common failure is for SCSS nested properties. `background:
|
|
|
|
|
// none { color: red; }` is parsed as a NestedDeclaration by
|
|
|
|
|
// postcss-scss, while `background: { color: red; }` is parsed as a Rule
|
|
|
|
|
// with a selector ending with a colon. See:
|
|
|
|
|
// https://github.com/postcss/postcss-scss/issues/39
|
|
|
|
|
return {
|
|
|
|
|
type: "selector-unknown",
|
|
|
|
|
value: selector,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addTypePrefix(result, "selector-");
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
async function parseMediaQuery(params) {
|
|
|
|
|
const mediaParser = await import("postcss-media-query-parser").then(
|
|
|
|
|
(m) => m.default.default
|
|
|
|
|
);
|
2022-03-28 09:41:08 +08:00
|
|
|
|
2018-05-15 00:00:45 +03:00
|
|
|
let result = null;
|
|
|
|
|
|
|
|
|
|
try {
|
2022-08-15 22:30:46 +09:00
|
|
|
result = mediaParser(params);
|
2021-02-25 18:28:55 +08:00
|
|
|
} catch {
|
2018-05-15 00:00:45 +03:00
|
|
|
// Ignore bad media queries
|
2020-08-21 09:43:24 +08:00
|
|
|
/* istanbul ignore next */
|
2018-05-15 00:00:45 +03:00
|
|
|
return {
|
|
|
|
|
type: "selector-unknown",
|
|
|
|
|
value: params,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addTypePrefix(addMissingType(result), "media-");
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
|
2021-12-07 14:15:03 +08:00
|
|
|
const DEFAULT_SCSS_DIRECTIVE = /(\s*)(!default).*$/;
|
|
|
|
|
const GLOBAL_SCSS_DIRECTIVE = /(\s*)(!global).*$/;
|
2018-01-12 14:12:02 +03:00
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
async function parseNestedCSS(node, options) {
|
2017-05-28 21:06:09 -07:00
|
|
|
if (node && typeof node === "object") {
|
|
|
|
|
delete node.parent;
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2017-05-28 21:06:09 -07:00
|
|
|
for (const key in node) {
|
2022-08-15 22:30:46 +09:00
|
|
|
await parseNestedCSS(node[key], options);
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2018-04-13 22:12:59 +03:00
|
|
|
if (!node.type) {
|
|
|
|
|
return node;
|
|
|
|
|
}
|
2017-05-28 21:06:09 -07:00
|
|
|
|
2020-08-21 09:43:24 +08:00
|
|
|
/* istanbul ignore next */
|
2018-04-13 22:12:59 +03:00
|
|
|
if (!node.raws) {
|
|
|
|
|
node.raws = {};
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-09 16:47:12 +08:00
|
|
|
// Custom properties looks like declarations
|
|
|
|
|
if (
|
|
|
|
|
node.type === "css-decl" &&
|
|
|
|
|
typeof node.prop === "string" &&
|
|
|
|
|
node.prop.startsWith("--") &&
|
|
|
|
|
typeof node.value === "string" &&
|
|
|
|
|
node.value.startsWith("{")
|
|
|
|
|
) {
|
|
|
|
|
let rules;
|
2022-05-16 10:28:29 +08:00
|
|
|
if (node.value.trimEnd().endsWith("}")) {
|
2020-11-09 16:47:12 +08:00
|
|
|
const textBefore = options.originalText.slice(
|
|
|
|
|
0,
|
|
|
|
|
node.source.start.offset
|
|
|
|
|
);
|
|
|
|
|
const nodeText =
|
|
|
|
|
"a".repeat(node.prop.length) +
|
|
|
|
|
options.originalText.slice(
|
|
|
|
|
node.source.start.offset + node.prop.length,
|
|
|
|
|
node.source.end.offset + 1
|
|
|
|
|
);
|
|
|
|
|
const fakeContent = textBefore.replace(/[^\n]/g, " ") + nodeText;
|
|
|
|
|
let parse;
|
|
|
|
|
if (options.parser === "scss") {
|
|
|
|
|
parse = parseScss;
|
|
|
|
|
} else if (options.parser === "less") {
|
|
|
|
|
parse = parseLess;
|
|
|
|
|
} else {
|
|
|
|
|
parse = parseCss;
|
|
|
|
|
}
|
|
|
|
|
let ast;
|
|
|
|
|
try {
|
2022-08-16 09:52:27 +08:00
|
|
|
ast = await parse(fakeContent, { ...options });
|
2021-03-08 18:35:48 +08:00
|
|
|
} catch {
|
2020-11-09 16:47:12 +08:00
|
|
|
// noop
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
ast &&
|
|
|
|
|
ast.nodes &&
|
|
|
|
|
ast.nodes.length === 1 &&
|
|
|
|
|
ast.nodes[0].type === "css-rule"
|
|
|
|
|
) {
|
|
|
|
|
rules = ast.nodes[0].nodes;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rules) {
|
|
|
|
|
node.value = {
|
|
|
|
|
type: "css-rule",
|
|
|
|
|
nodes: rules,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
node.value = {
|
|
|
|
|
type: "value-unknown",
|
|
|
|
|
value: node.raws.value.raw,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 22:12:59 +03:00
|
|
|
let selector = "";
|
|
|
|
|
|
|
|
|
|
if (typeof node.selector === "string") {
|
2018-05-30 23:22:18 +03:00
|
|
|
selector = node.raws.selector
|
2022-07-09 15:03:46 +08:00
|
|
|
? node.raws.selector.scss ?? node.raws.selector.raw
|
2018-05-30 23:22:18 +03:00
|
|
|
: node.selector;
|
2018-04-13 22:12:59 +03:00
|
|
|
|
|
|
|
|
if (node.raws.between && node.raws.between.trim().length > 0) {
|
2018-02-07 13:20:13 +03:00
|
|
|
selector += node.raws.between;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 22:12:59 +03:00
|
|
|
node.raws.selector = selector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let value = "";
|
|
|
|
|
|
|
|
|
|
if (typeof node.value === "string") {
|
2018-05-30 23:22:18 +03:00
|
|
|
value = node.raws.value
|
2022-07-09 15:03:46 +08:00
|
|
|
? node.raws.value.scss ?? node.raws.value.raw
|
2018-05-30 23:22:18 +03:00
|
|
|
: node.value;
|
2018-04-13 22:12:59 +03:00
|
|
|
|
|
|
|
|
value = value.trim();
|
|
|
|
|
|
2020-03-20 19:09:07 +03:00
|
|
|
node.raws.value = value;
|
2018-04-13 22:12:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let params = "";
|
|
|
|
|
|
|
|
|
|
if (typeof node.params === "string") {
|
2018-05-30 23:22:18 +03:00
|
|
|
params = node.raws.params
|
2022-07-09 15:03:46 +08:00
|
|
|
? node.raws.params.scss ?? node.raws.params.raw
|
2018-05-30 23:22:18 +03:00
|
|
|
: node.params;
|
2018-04-13 22:12:59 +03:00
|
|
|
|
|
|
|
|
if (node.raws.afterName && node.raws.afterName.trim().length > 0) {
|
|
|
|
|
params = node.raws.afterName + params;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.raws.between && node.raws.between.trim().length > 0) {
|
|
|
|
|
params = params + node.raws.between;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
params = params.trim();
|
|
|
|
|
|
|
|
|
|
node.raws.params = params;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ignore LESS mixin declaration
|
|
|
|
|
if (selector.trim().length > 0) {
|
2020-08-21 09:43:24 +08:00
|
|
|
// TODO: confirm this code is dead
|
|
|
|
|
/* istanbul ignore next */
|
2018-01-30 14:16:07 +03:00
|
|
|
if (selector.startsWith("@") && selector.endsWith(":")) {
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-21 09:43:24 +08:00
|
|
|
// TODO: confirm this code is dead
|
|
|
|
|
/* istanbul ignore next */
|
2018-04-13 22:12:59 +03:00
|
|
|
// Ignore LESS mixins
|
|
|
|
|
if (node.mixin) {
|
2022-08-15 22:30:46 +09:00
|
|
|
node.selector = await parseValue(selector, options);
|
2018-04-13 22:12:59 +03:00
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-15 00:00:45 +03:00
|
|
|
// Check on SCSS nested property
|
2020-11-09 16:47:12 +08:00
|
|
|
if (isSCSSNestedPropertyNode(node, options)) {
|
2018-05-15 00:00:45 +03:00
|
|
|
node.isSCSSNesterProperty = true;
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
node.selector = await parseSelector(selector);
|
2018-05-15 00:00:45 +03:00
|
|
|
|
2018-02-02 18:06:47 +03:00
|
|
|
return node;
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2018-06-25 12:57:41 +03:00
|
|
|
if (value.length > 0) {
|
2018-05-15 00:00:45 +03:00
|
|
|
const defaultSCSSDirectiveIndex = value.match(DEFAULT_SCSS_DIRECTIVE);
|
2018-02-06 18:09:42 +03:00
|
|
|
|
2018-05-15 00:00:45 +03:00
|
|
|
if (defaultSCSSDirectiveIndex) {
|
2020-01-23 20:15:28 +08:00
|
|
|
value = value.slice(0, defaultSCSSDirectiveIndex.index);
|
2018-05-15 00:00:45 +03:00
|
|
|
node.scssDefault = true;
|
2018-02-06 18:09:42 +03:00
|
|
|
|
2018-05-15 00:00:45 +03:00
|
|
|
if (defaultSCSSDirectiveIndex[0].trim() !== "!default") {
|
|
|
|
|
node.raws.scssDefault = defaultSCSSDirectiveIndex[0];
|
2018-01-12 14:12:02 +03:00
|
|
|
}
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2018-05-15 00:00:45 +03:00
|
|
|
const globalSCSSDirectiveIndex = value.match(GLOBAL_SCSS_DIRECTIVE);
|
|
|
|
|
|
|
|
|
|
if (globalSCSSDirectiveIndex) {
|
2020-01-23 20:15:28 +08:00
|
|
|
value = value.slice(0, globalSCSSDirectiveIndex.index);
|
2018-05-15 00:00:45 +03:00
|
|
|
node.scssGlobal = true;
|
|
|
|
|
|
|
|
|
|
if (globalSCSSDirectiveIndex[0].trim() !== "!global") {
|
|
|
|
|
node.raws.scssGlobal = globalSCSSDirectiveIndex[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value.startsWith("progid:")) {
|
|
|
|
|
return {
|
|
|
|
|
type: "value-unknown",
|
2020-01-14 19:25:24 +08:00
|
|
|
value,
|
2018-05-15 00:00:45 +03:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
node.value = await parseValue(value, options);
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2020-04-08 23:02:13 +08:00
|
|
|
if (
|
2020-11-09 16:47:12 +08:00
|
|
|
options.parser === "less" &&
|
2020-04-08 23:02:13 +08:00
|
|
|
node.type === "css-decl" &&
|
|
|
|
|
value.startsWith("extend(")
|
|
|
|
|
) {
|
2020-04-30 16:58:21 +08:00
|
|
|
// extend is missing
|
|
|
|
|
if (!node.extend) {
|
|
|
|
|
node.extend = node.raws.between === ":";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `:extend()` is parsed as value
|
|
|
|
|
if (node.extend && !node.selector) {
|
|
|
|
|
delete node.value;
|
2022-08-15 22:30:46 +09:00
|
|
|
node.selector = await parseSelector(value.slice("extend(".length, -1));
|
2020-04-30 16:58:21 +08:00
|
|
|
}
|
2020-04-08 23:02:13 +08:00
|
|
|
}
|
|
|
|
|
|
2020-03-10 07:14:34 +08:00
|
|
|
if (node.type === "css-atrule") {
|
2020-11-09 16:47:12 +08:00
|
|
|
if (options.parser === "less") {
|
2020-03-10 07:14:34 +08:00
|
|
|
// mixin
|
|
|
|
|
if (node.mixin) {
|
|
|
|
|
const source =
|
|
|
|
|
node.raws.identifier +
|
|
|
|
|
node.name +
|
|
|
|
|
node.raws.afterName +
|
|
|
|
|
node.raws.params;
|
2022-08-15 22:30:46 +09:00
|
|
|
node.selector = await parseSelector(source);
|
2020-03-10 07:14:34 +08:00
|
|
|
delete node.params;
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// function
|
|
|
|
|
if (node.function) {
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// only css support custom-selector
|
|
|
|
|
if (options.parser === "css" && node.name === "custom-selector") {
|
2021-12-07 14:15:03 +08:00
|
|
|
const customSelector = node.params.match(/:--\S+\s+/)[0].trim();
|
2020-03-10 07:14:34 +08:00
|
|
|
node.customSelector = customSelector;
|
2022-08-15 22:30:46 +09:00
|
|
|
node.selector = await parseSelector(
|
2020-03-10 07:14:34 +08:00
|
|
|
node.params.slice(customSelector.length).trim()
|
|
|
|
|
);
|
|
|
|
|
delete node.params;
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-09 16:47:12 +08:00
|
|
|
if (options.parser === "less") {
|
2020-10-12 04:16:36 +03:00
|
|
|
// postcss-less doesn't recognize variables in some cases.
|
|
|
|
|
// `@color: blue;` is recognized fine, but the cases below aren't:
|
|
|
|
|
|
|
|
|
|
// `@color:blue;`
|
2020-03-10 07:14:34 +08:00
|
|
|
if (node.name.includes(":") && !node.params) {
|
|
|
|
|
node.variable = true;
|
|
|
|
|
const parts = node.name.split(":");
|
|
|
|
|
node.name = parts[0];
|
2022-08-15 22:30:46 +09:00
|
|
|
node.value = await parseValue(parts.slice(1).join(":"), options);
|
2020-03-10 07:14:34 +08:00
|
|
|
}
|
|
|
|
|
|
2020-10-12 04:16:36 +03:00
|
|
|
// `@color :blue;`
|
2020-03-10 07:14:34 +08:00
|
|
|
if (
|
2021-04-25 18:13:40 -04:00
|
|
|
!["page", "nest", "keyframes"].includes(node.name) &&
|
2020-03-10 07:14:34 +08:00
|
|
|
node.params &&
|
|
|
|
|
node.params[0] === ":"
|
|
|
|
|
) {
|
|
|
|
|
node.variable = true;
|
2022-08-15 22:30:46 +09:00
|
|
|
node.value = await parseValue(node.params.slice(1), options);
|
2020-10-12 04:16:36 +03:00
|
|
|
node.raws.afterName += ":";
|
2020-03-10 07:14:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Less variable
|
|
|
|
|
if (node.variable) {
|
|
|
|
|
delete node.params;
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 22:12:59 +03:00
|
|
|
if (node.type === "css-atrule" && params.length > 0) {
|
2020-01-14 23:30:18 +08:00
|
|
|
const { name } = node;
|
2018-02-22 12:49:03 +03:00
|
|
|
const lowercasedName = node.name.toLowerCase();
|
2018-02-21 21:36:09 +03:00
|
|
|
|
2018-02-22 12:49:03 +03:00
|
|
|
if (name === "warn" || name === "error") {
|
2018-01-20 03:00:06 +03:00
|
|
|
node.params = {
|
|
|
|
|
type: "media-unknown",
|
2018-02-21 21:36:09 +03:00
|
|
|
value: params,
|
2018-01-20 03:00:06 +03:00
|
|
|
};
|
2018-02-02 18:06:47 +03:00
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:49:03 +03:00
|
|
|
if (name === "extend" || name === "nest") {
|
2022-08-15 22:30:46 +09:00
|
|
|
node.selector = await parseSelector(params);
|
2018-02-02 18:06:47 +03:00
|
|
|
delete node.params;
|
|
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:49:03 +03:00
|
|
|
if (name === "at-root") {
|
2021-09-08 11:18:07 +08:00
|
|
|
if (/^\(\s*(?:without|with)\s*:.+\)$/s.test(params)) {
|
2022-08-15 22:30:46 +09:00
|
|
|
node.params = await parseValue(params, options);
|
2018-02-02 18:06:47 +03:00
|
|
|
} else {
|
2022-08-15 22:30:46 +09:00
|
|
|
node.selector = await parseSelector(params);
|
2018-02-02 18:06:47 +03:00
|
|
|
delete node.params;
|
2018-01-24 12:31:30 +03:00
|
|
|
}
|
|
|
|
|
|
2018-02-02 18:06:47 +03:00
|
|
|
return node;
|
2018-01-20 03:00:06 +03:00
|
|
|
}
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2021-09-09 06:18:12 +02:00
|
|
|
if (isModuleRuleName(lowercasedName)) {
|
2020-03-10 07:14:34 +08:00
|
|
|
node.import = true;
|
|
|
|
|
delete node.filename;
|
2022-08-15 22:30:46 +09:00
|
|
|
node.params = await parseValue(params, options);
|
2018-05-15 01:24:34 +05:30
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-02 18:06:47 +03:00
|
|
|
if (
|
2018-02-22 12:49:03 +03:00
|
|
|
[
|
2018-04-25 21:27:12 +03:00
|
|
|
"namespace",
|
|
|
|
|
"supports",
|
2018-02-22 12:49:03 +03:00
|
|
|
"if",
|
|
|
|
|
"else",
|
|
|
|
|
"for",
|
|
|
|
|
"each",
|
|
|
|
|
"while",
|
|
|
|
|
"debug",
|
|
|
|
|
"mixin",
|
|
|
|
|
"include",
|
|
|
|
|
"function",
|
|
|
|
|
"return",
|
|
|
|
|
"define-mixin",
|
2018-04-25 21:27:12 +03:00
|
|
|
"add-mixin",
|
2019-11-17 18:06:14 +08:00
|
|
|
].includes(name)
|
2018-02-02 18:06:47 +03:00
|
|
|
) {
|
|
|
|
|
// Remove unnecessary spaces in SCSS variable arguments
|
2021-12-06 21:31:51 +08:00
|
|
|
// Move spaces after the `...`, so we can keep the range correct
|
|
|
|
|
params = params.replace(/(\$\S+?)(\s+)?\.{3}/, "$1...$2");
|
2018-02-02 18:06:47 +03:00
|
|
|
// Remove unnecessary spaces before SCSS control, mixin and function directives
|
2021-12-06 21:31:51 +08:00
|
|
|
// Move spaces after the `(`, so we can keep the range correct
|
|
|
|
|
params = params.replace(/^(?!if)(\S+)(\s+)\(/, "$1($2");
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
node.value = await parseValue(params, options);
|
2018-02-02 18:06:47 +03:00
|
|
|
delete node.params;
|
|
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-17 18:06:14 +08:00
|
|
|
if (["media", "custom-media"].includes(lowercasedName)) {
|
2018-02-22 12:49:03 +03:00
|
|
|
if (params.includes("#{")) {
|
|
|
|
|
// Workaround for media at rule with scss interpolation
|
|
|
|
|
return {
|
|
|
|
|
type: "media-unknown",
|
|
|
|
|
value: params,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
node.params = await parseMediaQuery(params);
|
2018-02-22 12:49:03 +03:00
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node.params = params;
|
2018-02-02 18:06:47 +03:00
|
|
|
|
|
|
|
|
return node;
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
}
|
2018-02-02 18:06:47 +03:00
|
|
|
|
2017-05-28 21:06:09 -07:00
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 22:30:46 +09:00
|
|
|
async function parseWithParser(parse, text, options) {
|
2018-05-21 10:02:09 -04:00
|
|
|
const parsed = parseFrontMatter(text);
|
2018-07-18 02:31:44 +05:30
|
|
|
const { frontMatter } = parsed;
|
2018-05-14 22:17:15 -03:00
|
|
|
text = parsed.content;
|
|
|
|
|
|
2017-05-28 21:06:09 -07:00
|
|
|
let result;
|
2018-04-28 13:53:30 +02:00
|
|
|
|
2017-05-28 21:06:09 -07:00
|
|
|
try {
|
2021-03-09 13:02:12 +08:00
|
|
|
result = parse(text, {
|
|
|
|
|
// Prevent file access https://github.com/postcss/postcss/blob/4f4e2932fc97e2c117e1a4b15f0272ed551ed59d/lib/previous-map.js#L18
|
|
|
|
|
map: false,
|
|
|
|
|
});
|
2020-10-16 10:39:05 +08:00
|
|
|
} catch (error) {
|
|
|
|
|
const { name, reason, line, column } = error;
|
2020-08-21 09:43:24 +08:00
|
|
|
/* istanbul ignore next */
|
2020-10-16 10:39:05 +08:00
|
|
|
if (typeof line !== "number") {
|
|
|
|
|
throw error;
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
2022-08-30 10:27:11 +08:00
|
|
|
|
|
|
|
|
throw createError(`${name}: ${reason}`, {
|
|
|
|
|
loc: { start: { line, column } },
|
|
|
|
|
cause: error,
|
|
|
|
|
});
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
2018-01-24 19:44:09 +03:00
|
|
|
|
2020-11-09 16:47:12 +08:00
|
|
|
options.originalText = text;
|
2022-08-15 22:30:46 +09:00
|
|
|
result = await parseNestedCSS(addTypePrefix(result, "css-"), options);
|
2018-05-14 22:17:15 -03:00
|
|
|
|
2020-03-20 19:09:07 +03:00
|
|
|
calculateLoc(result, text);
|
|
|
|
|
|
2018-05-21 10:02:09 -04:00
|
|
|
if (frontMatter) {
|
2020-11-05 19:26:05 +08:00
|
|
|
frontMatter.source = {
|
|
|
|
|
startOffset: 0,
|
|
|
|
|
endOffset: frontMatter.raw.length,
|
|
|
|
|
};
|
2022-08-30 11:00:12 +08:00
|
|
|
result.frontMatter = frontMatter;
|
2018-01-24 19:44:09 +03:00
|
|
|
}
|
|
|
|
|
|
2018-05-14 22:17:15 -03:00
|
|
|
return result;
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
|
2022-08-16 09:52:27 +08:00
|
|
|
async function parseCss(text, options = {}) {
|
2022-08-15 22:30:46 +09:00
|
|
|
const parse = await import("postcss/lib/parse").then((m) => m.default);
|
|
|
|
|
return parseWithParser(parse, text, options);
|
2020-04-03 12:41:48 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-16 09:52:27 +08:00
|
|
|
async function parseLess(text, options = {}) {
|
2022-08-15 22:30:46 +09:00
|
|
|
const less = await import("postcss-less");
|
2022-03-28 09:41:08 +08:00
|
|
|
|
2020-04-03 12:41:48 +08:00
|
|
|
return parseWithParser(
|
2020-03-20 19:09:07 +03:00
|
|
|
// Workaround for https://github.com/shellscape/postcss-less/issues/145
|
|
|
|
|
// See comments for `replaceQuotesInInlineComments` in `loc.js`.
|
2022-02-21 14:22:51 +08:00
|
|
|
(text) => less.parse(replaceQuotesInInlineComments(text)),
|
2020-04-03 12:41:48 +08:00
|
|
|
text,
|
|
|
|
|
options
|
|
|
|
|
);
|
2017-05-28 21:49:41 -07:00
|
|
|
}
|
|
|
|
|
|
2022-08-16 09:52:27 +08:00
|
|
|
async function parseScss(text, options = {}) {
|
2022-09-18 21:04:14 +08:00
|
|
|
const { default: parse } = await import("postcss-scss/lib/scss-parse");
|
|
|
|
|
return parseWithParser(parse, text, options);
|
2017-05-28 21:06:09 -07:00
|
|
|
}
|
|
|
|
|
|
2020-04-03 12:41:48 +08:00
|
|
|
const postCssParser = {
|
2018-05-24 15:30:45 -03:00
|
|
|
astFormat: "postcss",
|
2018-06-14 09:51:18 +08:00
|
|
|
hasPragma,
|
2020-11-05 19:26:05 +08:00
|
|
|
locStart,
|
|
|
|
|
locEnd,
|
2018-05-24 15:30:45 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Export as a plugin so we can reuse the same bundle for UMD loading
|
2022-02-21 14:22:51 +08:00
|
|
|
const postcssParser = {
|
2018-05-24 15:30:45 -03:00
|
|
|
parsers: {
|
2020-04-03 12:41:48 +08:00
|
|
|
css: {
|
|
|
|
|
...postCssParser,
|
|
|
|
|
parse: parseCss,
|
|
|
|
|
},
|
|
|
|
|
less: {
|
|
|
|
|
...postCssParser,
|
|
|
|
|
parse: parseLess,
|
|
|
|
|
},
|
|
|
|
|
scss: {
|
|
|
|
|
...postCssParser,
|
|
|
|
|
parse: parseScss,
|
|
|
|
|
},
|
2018-05-24 15:30:45 -03:00
|
|
|
},
|
|
|
|
|
};
|
2022-02-21 14:22:51 +08:00
|
|
|
|
|
|
|
|
export default postcssParser;
|