@lynx-js/web-core
Version:
This is an internal experimental package, do not use
111 lines • 5.42 kB
JavaScript
import * as CSS from '@lynx-js/css-serializer';
import { RawStyleInfo, Rule, Selector, RulePrelude,
// @ts-ignore
} from '../../binary/encode/encode.js';
// @ts-ignore
export * from '../../binary/encode/encode.js';
function restoreCSSVarValue(decl) {
return decl.value.replaceAll(/\{\{(--[^}]+)\}\}/g, (_, varName) => {
const isCSSVarDecl = 'type' in decl && decl.type === 'css_var';
if (!isCSSVarDecl) {
return `var(${varName})`;
}
const fallback = decl.defaultValueMap?.[varName];
return fallback ? `var(${varName}, ${fallback})` : `var(${varName})`;
});
}
export function encodeCSS(cssMap) {
const rawStyleInfo = new RawStyleInfo();
for (const [cssId, nodes] of Object.entries(cssMap)) {
const parsedCssId = Number(cssId);
if (isNaN(parsedCssId)) {
throw new Error(`Invalid cssId: ${cssId}. cssId should be a valid number string.`);
}
for (const node of nodes) {
if (node.type === 'ImportRule') {
const href = node.href.startsWith('/') ? node.href.slice(1) : node.href;
const importCssId = Number(href);
if (isNaN(importCssId)) {
throw new Error(`Invalid importCssId: ${node.href}. importCssId should be a valid number string.`);
}
else {
rawStyleInfo.append_import(parsedCssId, importCssId);
}
}
else if (node.type === 'KeyframesRule') {
const rule = new Rule('KeyframesRule');
const keyframeNamePrelude = new RulePrelude();
const keyFrameNameSelector = new Selector();
const keyFrameName = node.name.value;
keyFrameNameSelector.push_one_selector_section('UnknownText', keyFrameName);
keyframeNamePrelude.push_selector(keyFrameNameSelector);
rule.set_prelude(keyframeNamePrelude);
for (const keyframesStyle of node.styles) {
const keyFrameChildrenRule = new Rule('StyleRule');
const prelude = new RulePrelude();
const selector = new Selector();
selector.push_one_selector_section('UnknownText', keyframesStyle.keyText.value);
prelude.push_selector(selector);
keyFrameChildrenRule.set_prelude(prelude);
for (const [key, value] of Object.entries(keyframesStyle.variables ?? {})) {
keyFrameChildrenRule.push_declaration(key, value);
}
for (const decl of keyframesStyle.style) {
keyFrameChildrenRule.push_declaration(decl.name, restoreCSSVarValue(decl));
}
rule.push_rule_children(keyFrameChildrenRule);
}
rawStyleInfo.push_rule(parsedCssId, rule);
}
else if (node.type === 'FontFaceRule') {
const rule = new Rule('FontFaceRule');
for (const decl of node.style) {
rule.push_declaration(decl.name, restoreCSSVarValue(decl));
}
rawStyleInfo.push_rule(parsedCssId, rule);
}
else if (node.type === 'StyleRule') {
const rule = new Rule('StyleRule');
const prelude = new RulePrelude();
// Parse selectors
const ast = CSS.csstree.parse(`${node.selectorText.value}{ --mocked-declaration:1;}`);
const selectorList = ast.children.first
.prelude;
for (const selectorNode of selectorList.children
.toArray()) {
const selector = new Selector();
for (const child of selectorNode.children.toArray()) {
if (child.type === 'AttributeSelector') {
selector.push_one_selector_section(child.type, CSS.csstree.generate(child));
continue;
}
if (child.type === 'PseudoClassSelector') {
selector.push_one_selector_section(child.type, CSS.csstree.generate(child).slice(1));
continue;
}
// @ts-expect-error
if (!child.name) {
throw new Error(`Selector section of type ${child.type} is missing a name/value.`);
}
selector.push_one_selector_section(child.type,
// @ts-expect-error
child.name);
}
prelude.push_selector(selector);
}
rule.set_prelude(prelude);
// Declarations
for (const decl of node.style) {
rule.push_declaration(decl.name, restoreCSSVarValue(decl));
}
// Variables
for (const [name, value] of Object.entries(node.variables)) {
rule.push_declaration(name, value);
}
rawStyleInfo.push_rule(parsedCssId, rule);
}
}
}
return rawStyleInfo.encode();
}
//# sourceMappingURL=encodeCSS.js.map