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 install
gulp
This will create a dist
directory containing distributable artifacts.
Usage
Basic parsing
var css = 'body { color: red; }';
var parser = new shadyCss.Parser();
var ast = parser.parse(css);
Custom parsing
function CustomNodeFactory() {
shadyCss.NodeFactory.apply(this, arguments);
}
CustomNodeFactory.prototype = Object.create(shadyCss.NodeFactory.prototype);
CustomNodeFactory.prototype.expression = function(text) {
if (/^darken\(/.test(text)) {
return {
type: 'darkenExpression',
color: text.replace(/^darken\(/, '').replace(/\)$/, '')
};
} else {
return shadyCss.NodeFactory.prototype.expression.apply(this, arguments);
}
}
var css = 'body { color: darken(red); }';
var parser = new shadyCss.Parser(new CustomNodeFactory());
var ast = parser.parse(css);
Basic stringification
var stringifier = new shadyCss.Stringifier();
stringifier.stringify(ast);
Note: the built-in Parser and Stringifier discard most insignficiant whitespace
from parsed CSS.
Custom stringification
function CustomStringifier() {
shadyCss.Stringifier.apply(this, arguments);
}
CustomStringifier.prototype = Object.create(shadyCss.Stringifier.prototype);
CustomStringifier.prototype.darkenExpression = function(darkenExpression) {
return darken(darkenExpression.color);
};
var stringifier = new CustomStringifier();
var 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);}