Skip to content Skip to sidebar Skip to footer

CSS Parser For JavaScript?

Update 2018: This question was asked long before PostCSS existed, and I would have probably used that. I'd like to parse a blob of CSS into an AST so I can add prefixes to certai

Solution 1:

Update: I previously mentioned JSCSSP, which is buggy seems to be abandoned. Obviously enough, the css module on NPM is the best:

css = require 'css'

input = '''
  body {
    font-family: sans-serif;
  }
  #thing.foo p.bar {
    font-weight: bold;
  }
'''

obj = css.parse input
sheet = obj.stylesheet

for rule in sheet.rules
  rule.selectors = ('#XXX ' + s for s in rule.selectors)

console.log css.stringify(obj)

Output:

#XXX body {
  font-family: sans-serif;
}
#XXX #thing.foo p.bar {
  font-weight: bold;
}

Solution 2:

Also worth mentioning is LESS. While it is primarily a (fantastic) extension to CSS, the LESS parser does give you access to the AST.

A pure CSS stylesheet is also a valid LESS stylesheet, so you can start with what you have now and ease in to LESS' extensions.


Solution 3:

Here is our open source CSS parser css.js

Here is a simple parsing example :

<script type="text/javascript">
    var cssString = ' .someSelector { margin:40px 10px; padding:5px}';
    //initialize parser object
    var parser = new cssjs();
    //parse css string
    var parsed = parser.parseCSS(cssString);

    console.log(parsed);
</script>

To stringify parsed data structure into CSS string after editing

var newCSSString = parser.getCSSForEditor(parsed);

Main features of our CSS parser is :

  • It is lightweight.
  • It outputs easy to understand javascript object. No complex AST.
  • It is battle tested(and unit tested also) and constantly used in our products(JotForm Form Designer).
  • It supports media queries, keyframes and font-face rules.
  • It preserves comments while parsing.

Solution 4:

For what it's worth, JSDOM uses CSSOM.


Solution 5:

Edit

I ended up using this library which was light enough for my implementation (provided in Kemal Dağ's answer). Other options were too heavy for the client-side implementation I was after.

https://github.com/jotform/css.js

Original Content

a paid nerd's original answer worked great until I hit media queries.

I had to add some recursion and this is what I ended up with.

Forgive me for the TypeScript.

TypeScript Implementation

private scopeCSS(css: string): CSS.Stylesheet {
  let ast: CSS.Stylesheet = CSS.parse(css);
  let stylesheet: CSS.StyleRules|undefined = ast.stylesheet;
  if (stylesheet) {
    let rules: Array<CSS.Rule|CSS.Media> = stylesheet.rules;
    let prefix = `[data-id='sticky-container-${this.parent.id}']`;

    // Append our container scope to rules
    // Recursive rule appender
    let ruleAppend = (rules: Array<CSS.Rule|CSS.Media>) => {
      rules.forEach(rule => {
        let cssRule = <CSS.Rule>rule;
        let mediaRule = <CSS.Media>rule;
        if (cssRule.selectors !== undefined) {
          cssRule.selectors = cssRule.selectors.map(selector => `${prefix} ${selector}`);
        }
        if (mediaRule.rules !== undefined) {
          ruleAppend(mediaRule.rules);
        }
      });
    };

    ruleAppend(rules);
  }
  return ast;
}

Babel'ized Vanilla JS Implementation

function scopeCSS(css, prefix) {
  var ast = CSS.parse(css);
  var stylesheet = ast.stylesheet;
  if (stylesheet) {
    var rules = stylesheet.rules;
    // Append our container scope to rules
    // Recursive rule appender
    var ruleAppend = function(rules) {
      rules.forEach(function(rule) {
        if (rule.selectors !== undefined) {
          rule.selectors = rule.selectors.map(function(selector) {
            return prefix + " " + selector;
          });
        }
        if (rule.rules !== undefined) {
          ruleAppend(rule.rules);
        }
      });
    };
    ruleAppend(rules);
  }
  return ast;
}

Post a Comment for "CSS Parser For JavaScript?"