mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
159 lines
3.4 KiB
TypeScript
159 lines
3.4 KiB
TypeScript
import { parse } from "css-tree";
|
|
|
|
function mapSelectors(selector: string): string[] {
|
|
if (!selector) {
|
|
return [];
|
|
}
|
|
|
|
return selector
|
|
.split(/\s*(?![^(]*\)),\s*/)
|
|
.map((s) => s.replace(/\u200C/g, ","));
|
|
}
|
|
|
|
function mapPosition(node, css) {
|
|
let res: any = {
|
|
start: {
|
|
line: node.loc.start.line,
|
|
column: node.loc.start.column,
|
|
},
|
|
end: {
|
|
line: node.loc.end.line,
|
|
column: node.loc.end.column,
|
|
},
|
|
content: css,
|
|
};
|
|
|
|
if (node.loc.source && node.loc.source !== "<unknown>") {
|
|
res.source = node.loc.source;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
function transformAst(node, css, type = null) {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
|
|
if (node.type === "StyleSheet") {
|
|
return {
|
|
type: "stylesheet",
|
|
stylesheet: {
|
|
rules: node.children
|
|
.map((child) => transformAst(child, css))
|
|
.filter((child) => child !== null)
|
|
.toArray(),
|
|
parsingErrors: [],
|
|
},
|
|
};
|
|
}
|
|
|
|
if (node.type === "Atrule") {
|
|
let atrule: any = {
|
|
type: node.name,
|
|
};
|
|
|
|
if (node.name === "supports" || node.name === "media") {
|
|
atrule[node.name] = node.prelude.value;
|
|
atrule.rules = transformAst(node.block, css);
|
|
} else if (node.name === "page") {
|
|
atrule.selectors = node.prelude
|
|
? mapSelectors(node.prelude.value)
|
|
: [];
|
|
atrule.declarations = transformAst(node.block, css);
|
|
} else if (node.name === "document") {
|
|
atrule.document = node.prelude ? node.prelude.value : "";
|
|
atrule.vendor = "";
|
|
atrule.rules = transformAst(node.block, css);
|
|
} else if (node.name === "font-face") {
|
|
atrule.declarations = transformAst(node.block, css);
|
|
} else if (
|
|
node.name === "import" ||
|
|
node.name === "charset" ||
|
|
node.name === "namespace"
|
|
) {
|
|
atrule[node.name] = node.prelude ? node.prelude.value : "";
|
|
} else if (node.name === "keyframes") {
|
|
atrule.name = node.prelude ? node.prelude.value : "";
|
|
atrule.keyframes = transformAst(node.block, css, "keyframe");
|
|
atrule.vendor = undefined;
|
|
} else {
|
|
atrule.rules = transformAst(node.block, css);
|
|
}
|
|
|
|
atrule.position = mapPosition(node, css);
|
|
|
|
return atrule;
|
|
}
|
|
|
|
if (node.type === "Block") {
|
|
return node.children
|
|
.map((child) => transformAst(child, css, type))
|
|
.filter((child) => child !== null)
|
|
.toArray();
|
|
}
|
|
|
|
if (node.type === "Rule") {
|
|
let value = node.prelude.value;
|
|
|
|
let res: any = {
|
|
type: type != null ? type : "rule",
|
|
declarations: transformAst(node.block, css),
|
|
position: mapPosition(node, css),
|
|
};
|
|
|
|
if (type === "keyframe") {
|
|
res.values = mapSelectors(value);
|
|
} else {
|
|
res.selectors = mapSelectors(value);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
if (node.type === "Comment") {
|
|
return {
|
|
type: "comment",
|
|
comment: node.value,
|
|
position: mapPosition(node, css),
|
|
};
|
|
}
|
|
|
|
if (node.type === "Declaration") {
|
|
return {
|
|
type: "declaration",
|
|
property: node.property,
|
|
value: node.value.value ? node.value.value.trim() : "",
|
|
position: mapPosition(node, css),
|
|
};
|
|
}
|
|
|
|
if (node.type === "Raw") {
|
|
return null;
|
|
}
|
|
|
|
throw Error(`Unknown node type ${node.type}`);
|
|
}
|
|
|
|
export function cssTreeParse(css, source): any {
|
|
let errors = [];
|
|
let ast = parse(css, {
|
|
parseValue: false,
|
|
parseAtrulePrelude: false,
|
|
parseRulePrelude: false,
|
|
positions: true,
|
|
filename: source,
|
|
onParseError: (error) => {
|
|
errors.push(
|
|
`${source}:${error.line}:${error.column}: ${error.formattedMessage}`
|
|
);
|
|
},
|
|
});
|
|
|
|
if (errors.length > 0) {
|
|
throw new Error(errors[0]);
|
|
}
|
|
|
|
return transformAst(ast, css);
|
|
}
|