Static Analysis For CSS

css_static_analysis_header

By Toby Ho

Static analysis involves analyzing, rather than executing, source code, a process that is typically automated. Recently, I’ve had fun writing about static analysis for Javascript. This time, we’ll do something that’s similar but different – static analysis for CSS.

The CSS Module

The library we’ll use to help perform the static analysis is simply called css and it can be found on npm. So, assuming you already have Node.js installed, you just need to do npm install css to install it. It contains both a parser and an emitter.

Parsing CSS

Let’s say you have a sample.css file:

.btn:hover,
.btn:focus {
  color: #333333;
  text-decoration: none;
}

To parse it using the css library from above, you’d write this:

var css = require('css');
var fs = require('fs');
var code = fs.readFileSync('sample.css') + '';
var ast = css.parse(code);
console.log(JSON.stringify(ast, null, '  '));

When you run this code, you’ll see its abstract syntax tree (AST) printed to the console:

{
  "type": "stylesheet",
  "stylesheet": {
    "rules": [
      {
        "type": "rule",
        "selectors": [
          ".btn:hover",
          ".btn:focus"
        ],
        "declarations": [
          {
            "type": "declaration",
            "property": "color",
            "value": "#333333"
          },
          {
            "type": "declaration",
            "property": "text-decoration",
            "value": "none"
          }
        ]
      }
    ]
  }
}

Modifying the AST

Let’s say that, for whatever reason, we want all the rules in the CSS file to only take effect for elements which are descendants of certain elements – specified by the class root. We can do that by prefixing every selector of every rule with .root.

ast.stylesheet.rules.forEach(function(rule){
  for (var i = 0; i < rule.selectors.length; i++){
    rule.selectors[i] = '.root ' + rule.selectors[i];
  }
});

Emitting the Modified CSS

Now that we have modified the AST, we can use the stringify() method to regenerate the CSS from it:

console.log(css.stringify(ast))

And the output is:

.root .btn:hover,
.root .btn:focus {
  color: #333333;
  text-decoration: none;
}

Yay! Easy, right?

Other Possibilities

What are other interesting things you can do? You are limited only by your imagination. For example, if you want to automatically rename classes, you can do that. If you want to automatically sort or group rules by their selector, you can do that. If you want to automatically detect and remove unused rules (combined with html parsing), you can do that! You can do that!

Homework

It turns out that our example code didn’t cover all the cases. You homework is to run it against the CSS file that’s bundled with Bootstrap, figure out what’s broken, and fix it. Good luck!

This article was originally posted at http://tobyho.com/2014/01/16/static-analysis-for-css/

Modern Web Newsletter

Subscribe to receive the Modern Web tutorials, sent out every second Wednesday.

  • http://gavinengel.com gavin

    Hmmm.. do projects like SASS/LESS do what you are trying to accomplish here?

Top