teriyaki

Tolerant CSS parsing and AST transformation

teriyaki

Tolerant CSS parsing and AST transformation for adding new syntax features. In addition to normal CSS syntax it supports:

  • both block and line style comments (/* */ and //...<NEW LINE>)
  • any statement of the form @foo bar;
  • any block of the form @foo bar { block }
  • strings of any text providing they end in a semicolon (e.g. var foo = 'bar';)
  • absolutely any type of nesting. Everything can be nested in everything else.

If you think I've missed something, be sure to open an issue or submit a pull request.

The parser takes a string and returns an AST.

The hope is that its flexibility will allow people to trial new features for CSS in completely orthogonal ways. It is not recommended for use in tools such as verifiers and minifiers (although it does a decent job of removing comments if you don't midn supporting //...<NEW LINE> style comments).

var teriyaki = require('teriyaki');
teriyaki.parse('.src { foo: "bar"; }');
// =>  
{
  type: "stylesheet",
  range: [ 0, 20 ],
  body: [
    {
      type: "rule",
      range: [ 0, 20 ],
      selectors: {
        type: "selectors",
        range: [ 0, 4 ],
        selectors: [ { type: "selector", range: [ 0, 4 ], val: ".src" } ]
      },
      declarations: [
        {
          type: "declaration",
          range: [ 7, 19 ],
          property: { type: "property", range: [ 7, 10 ], name: "foo" },
          value: { type: "value", range: [ 12, 17 ], value: "\"bar\"" }
        }
      ]
    }
  ],
  comments: []
}

The transformer uses all the same node types as the Parser and ultimately returns a string.

e.g. Remove all line style comments:

var teriyaki = require('teriyaki');
var output = teriyaki.transform(input, function (node) {
  if (node.type === 'comment' && node.comment[0] === '/' && node.comment[1] === '/') {
    node.update('');
  }
});
// Use output here 

e.g. Inline @import urls.

var teriyaki = require('teriyaki');
teriyaki.transform(input, function (node) {
  var match;
  if (node.type === '@' && node.name === 'import' && match = /^url(\"([^\"]+)\")$/.exec(node.query)) {
    var cb = this.async();
    get(match[1], function (errbody) {
      if (err) return cb(err);
      node.update(body.toString());
      cb();
    });
  }
}, function (erroroutput) {
  if (err) throw err;
  // Use output here 
});

MIT

If you find it useful, a donation via gittip would be appreciated.