@tbela99/css-parser
Version:
CSS parser for node and the browser
137 lines (134 loc) • 5.36 kB
JavaScript
import { EnumToken } from '../types.js';
import { walkValues } from '../walk.js';
import { renderToken } from '../../renderer/render.js';
function replace(node, variableScope) {
for (const { value, parent: parentValue } of walkValues(node.val)) {
if (value?.typ == EnumToken.FunctionTokenType && value.val == 'var') {
if (value.chi.length == 1 && value.chi[0].typ == EnumToken.DashedIdenTokenType) {
const info = variableScope.get(value.chi[0].val);
if (info?.replaceable) {
if (parentValue != null) {
let i = 0;
for (; i < parentValue.chi.length; i++) {
if (parentValue.chi[i] == value) {
parentValue.chi.splice(i, 1, ...info.node.val);
break;
}
}
}
else {
node.val = info.node.val.slice();
}
}
}
}
}
}
class InlineCssVariablesFeature {
static get ordering() {
return 0;
}
static register(options) {
if (options.inlineCssVariables) {
for (const feature of options.features) {
if (feature instanceof InlineCssVariablesFeature) {
return;
}
}
// @ts-ignore
options.features.push(new InlineCssVariablesFeature());
}
}
run(ast, options = {}, parent, context) {
if (!('variableScope' in context)) {
context.variableScope = new Map;
}
const isRoot = parent.typ == EnumToken.StyleSheetNodeType && ast.typ == EnumToken.RuleNodeType && [':root', 'html'].includes(ast.sel);
const variableScope = context.variableScope;
// @ts-ignore
for (const node of ast.chi) {
if (node.typ == EnumToken.CDOCOMMNodeType || node.typ == EnumToken.CommentNodeType) {
continue;
}
if (node.typ != EnumToken.DeclarationNodeType) {
break;
}
// css variable
if (node.nam.startsWith('--')) {
if (!variableScope.has(node.nam)) {
const info = {
globalScope: isRoot,
// @ts-ignore
parent: new Set(),
declarationCount: 1,
replaceable: isRoot,
node: node
};
info.parent.add(ast);
variableScope.set(node.nam, info);
let recursive = false;
for (const { value, parent: parentValue } of walkValues(node.val)) {
if (value?.typ == EnumToken.FunctionTokenType && value.val == 'var') {
recursive = true;
break;
}
}
if (recursive) {
replace(node, variableScope);
}
}
else {
const info = variableScope.get(node.nam);
info.globalScope = isRoot;
if (!isRoot) {
++info.declarationCount;
}
if (info.replaceable) {
info.replaceable = isRoot && info.declarationCount == 1;
}
info.parent.add(ast);
info.node = node;
}
}
else {
replace(node, variableScope);
}
}
}
cleanup(ast, options = {}, context) {
const variableScope = context.variableScope;
if (variableScope == null) {
return;
}
for (const info of variableScope.values()) {
if (info.replaceable) {
let i;
// drop declarations from :root{}
for (const parent of info.parent) {
i = parent.chi?.length ?? 0;
while (i--) {
if (parent.chi[i].typ == EnumToken.DeclarationNodeType && parent.chi[i].nam == info.node.nam) {
// @ts-ignore
parent.chi.splice(i++, 1, {
typ: EnumToken.CommentTokenType,
val: `/* ${info.node.nam}: ${info.node.val.reduce((acc, curr) => acc + renderToken(curr), '')} */`
});
}
}
if (parent.chi?.length == 0 && 'parent' in parent) {
// @ts-ignore
for (i = 0; i < parent.parent.chi?.length; i++) {
// @ts-ignore
if (parent.parent.chi[i] == parent) {
// @ts-ignore
parent.parent.chi.splice(i, 1);
break;
}
}
}
}
}
}
}
}
export { InlineCssVariablesFeature };