visitors are used to alter the ast tree produced by the parser. for more information about the visitor object see the typescript definition
visitors can be called when the node is entered, visited or left.
import {AstAtRule, ParserOptions, transform, VisitorNodeMap, WalkerEvent} from "@tbela99/css-parser";
const options: ParserOptions = {
visitor: [
{
AtRule: [
(node: AstAtRule): AstAtRule => {
console.error(`> visiting '@${node.nam}' node at position ${node.loc!.sta.lin}:${node.loc!.sta.col}`);
return node
}, {
media: (node: AstAtRule): AstAtRule => {
console.error(`> visiting only '@${node.nam}' node at position ${node.loc!.sta.lin}:${node.loc!.sta.col}`);
return node
}
}, {
type: WalkerEvent.Leave,
handler: (node: AstAtRule): AstAtRule => {
console.error(`> leaving '@${node.nam}' node at position ${node.loc!.sta.lin}:${node.loc!.sta.col}`)
return node
}
},
{
type: WalkerEvent.Enter,
handler: (node: AstAtRule): AstAtRule => {
console.error(`> enter '@${node.nam}' node at position ${node.loc!.sta.lin}:${node.loc!.sta.col}`);
return node
}
}]
}] as VisitorNodeMap[]
};
const css = `
@media screen {
.foo {
height: calc(100px * 2/ 15);
}
}
@supports (height: 30pt) {
.foo {
height: calc(100px * 2/ 15);
}
}
`;
const result = await transform(css, options);
console.debug(result.code);
// > enter '@media' node at position 3:1
// > visiting '@media' node at position 3:1
// > visiting only '@media' node at position 3:1
// > leaving '@media' node at position 3:1
// > enter '@supports' node at position 11:1
// > visiting '@supports' node at position 11:1
// > leaving '@supports' node at position 11:1
Example: change media at-rule prelude
import {transform, AstAtRule, ParserOptions} from "@tbela99/css-parser";
const options: ParserOptions = {
visitor: {
AtRule: {
media: (node: AstAtRule): AstAtRule => {
node.val = 'tv,screen';
return node
}
}
}
};
const css = `
@media screen {
.foo {
height: calc(100px * 2/ 15);
}
}
`;
const result = await transform(css, options);
console.debug(result.code);
// @media tv,screen{.foo{height:calc(40px/3)}}
Example: add 'width: 3px' everytime a declaration with the name 'height' is found
import {transform, parseDeclarations} from "@tbela99/css-parser";
const options: ParserOptions = {
removeEmpty: false,
visitor: {
Declaration: {
// called only for height declaration
height: (node: AstDeclaration): AstDeclaration[] => {
return [
node,
{
typ: EnumToken.DeclarationNodeType,
nam: 'width',
val: [
<LengthToken>{
typ: EnumToken.LengthTokenType,
val: 3,
unit: 'px'
}
]
}
];
}
}
}
};
const css = `
.foo {
height: calc(100px * 2/ 15);
}
.selector {
color: lch(from peru calc(l * 0.8) calc(c * 0.7) calc(h + 180))
}
`;
console.debug(await transform(css, options));
// .foo{height:calc(40px/3);width:3px}.selector{color:#0880b0}
Example: rename 'margin' to 'padding' and 'height' to 'width'
import {AstDeclaration, ParserOptions, transform} from "../src/node.ts";
const options: ParserOptions = {
visitor: {
// called for every declaration
Declaration: (node: AstDeclaration): null => {
if (node.nam == 'height') {
node.nam = 'width';
}
else if (node.nam == 'margin') {
node.nam = 'padding'
}
return null;
}
}
};
const css = `
.foo {
height: calc(100px * 2/ 15);
margin: 10px;
}
.selector {
margin: 20px;}
`;
const result = await transform(css, options);
console.debug(result.code);
// .foo{width:calc(40px/3);padding:10px}.selector{padding:20px}
Example: add 'width: 3px' to every rule with the selector '.foo'
import {transform, parseDeclarations} from "@tbela99/css-parser";
const options: ParserOptions = {
removeEmpty: false,
visitor: {
Rule: async (node: AstRule): Promise<AstRule | null> => {
if (node.sel == '.foo') {
node.chi.push(...await parseDeclarations('width: 3px'));
return node;
}
return null;
}
}
};
const css = `
.foo {
.foo {
}
}
`;
console.debug(await transform(css, options));
// .foo{width:3px;.foo{width:3px}}
keyframes rule visitor is called on each keyframes rule node.
the keyframes at-rule visitor is called on each keyframes at-rule node. the visitor can be a function or an object with a property named after the keyframes at-rule prelude.
import {transform} from "@tbela99/css-parser";
const css = `
@keyframes slide-in {
from {
transform: translateX(0%);
}
to {
transform: translateX(100%);
}
}
@keyframes identifier {
0% {
top: 0;
left: 0;
}
30% {
top: 50px;
}
68%,
72% {
left: 50px;
}
100% {
top: 100px;
left: 100%;
}
}
`;
const result = await transform(css, {
removePrefix: true,
beautify: true,
visitor: {
KeyframesAtRule: {
slideIn(node) {
node.val = 'slide-in-out';
return node;
}
}
}
});
the value visitor is called on each token of the selector node, declaration value and the at-rule prelude, etc.
generic token visitor is a function whose name is a keyof EnumToken. it is called for every token of the specified type.
import {transform, parse, parseDeclarations} from "@tbela99/css-parser";
const options: ParserOptions = {
inlineCssVariables: true,
visitor: {
// Stylesheet node visitor
StyleSheetNodeType: async (node) => {
// insert a new rule
node.chi.unshift(await parse('html {--base-color: pink}').then(result => result.ast.chi[0]))
},
ColorTokenType: (node) => {
// dump all color tokens
// console.debug(node);
},
FunctionTokenType: (node) => {
// dump all function tokens
// console.debug(node);
},
DeclarationNodeType: (node) => {
// dump all declaration nodes
// console.debug(node);
}
}
};
const css = `
body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); }
`;
console.debug(await transform(css, options));
// body {color:#f3fff0}