Shady CSS Parser
The motivation for Shady CSS Parser is to provide a fast, small and flexible
CSS parser suitable for facilitating runtime parsing and transformation of CSS.
The Polymer library and the Polymer Designer tool are both example cases where
fast and flexible CSS parsing and transformation is a critical feature.
Goals
- Feasibility of being used in conjunction with Polymer or Polymer
Designer.
- Parse CSS loosely and flexibly. This parser is not spec-compliant, however
it will parse all spec-compliant CSS.
- Parse CSS quickly and efficiently. This parser is a suitable tool to aide in
the design and implementation of runtime transformations.
- Graceful error recovery. Malformed CSS will be parsed by this
parser as closely as possible to the way a browser would parse it.
Installing
With node
and npm
installed, run the following command:
npm install shady-css-parser
Building
Run the following commands from the project root:
npm run build
This will create a dist
directory containing distributable artifacts.
Usage
Basic parsing
import * as shadyCss from 'shady-css-parser';
const css = 'body { color: red; }';
const parser = new shadyCss.Parser();
const ast = parser.parse(css);
Custom parsing
class CustomNodeFactory extends shadyCss.NodeFactory {
expression(text) {
if (/^darken\(/.test(text)) {
return {
type: 'darkenExpression',
color: text.replace(/^darken\(/, '').replace(/\)$/, ''),
};
} else {
return super.expression.apply(this, arguments);
}
}
}
const css = 'body { color: darken(red); }';
const parser = new shadyCss.Parser(new CustomNodeFactory());
const ast = parser.parse(css);
Basic stringification
const stringifier = new shadyCss.Stringifier();
stringifier.stringify(ast);
Note: the built-in Parser and Stringifier discard most insignficiant whitespace
from parsed CSS.
Custom stringification
class CustomStringifier extends shadyCss.Stringifier {
darkenExpression(darkenExpression) {
return darken(darkenExpression.color);
}
}
const stringifier = new CustomStringifier();
const css = stringifier.stringify(ast);
Example ASTs
Custom property declaration
.container {
--nog: blue;
}
{
"type": 1,
"rules": [
{
"type": 4,
"selector": ".container",
"rulelist": {
"type": 7,
"rules": [
{
"type": 6,
"name": "--nog",
"value": {
"type": 5,
"text": "blue"
}
}
]
}
}
]
}
Mixin declaration
ruleset {
--mixin-name: {
};
}
{
"type": 1,
"rules": [
{
"type": 4,
"selector": "ruleset",
"rulelist": {
"type": 7,
"rules": [
{
"type": 6,
"name": "--mixin-name",
"value": {
"type": 7,
"rules": [
{
"type": 2,
"value": "\/* rules *\/"
}
]
}
}
]
}
}
]
}
Mixin application
.title {
@apply(--my-toolbar-title-theme);
}
{
"type": 1,
"rules": [
{
"type": 4,
"selector": ".title",
"rulelist": {
"type": 7,
"rules": [
{
"type": 3,
"name": "apply",
"parameters": "(--my-toolbar-title-theme)",
"rulelist": null
}
]
}
}
]
}
baz: lur;
};
}
{
"type": 1,
"rules": [
{
"type": 2,
"value": "\/* unclosed\n@fiz {\n --huk: {\n \/* buz *\/"
},
{
"type": 6,
"name": "baz",
"value": {
"type": 5,
"text": "lur"
}
},
{
"type": 8,
"text": "};\n"
},
{
"type": 8,
"text": "}"
}
]
}
Example stringification
Basic ruleset
body {
margin: 0;
padding: 0px
}
body{margin:0;padding:0px;}
At rules
@import url('foo.css');
@font-face {
font-family: foo;
}
@charset 'foo';
@import url('foo.css');@font-face{font-family:foo;}@charset 'foo';
Custom properties
:root {
--qux: vim;
--foo: {
bar: baz;
};
}
#target {
gak: var(--qux);
@apply(--foo);
}
:root{--qux:vim;--foo:{bar:baz;};}#target{gak:var(--qux);@apply (--foo);}