Last Updated: February 25, 2016
·
2.549K
· sorich87

Hidden Sass feature: CSS3 parser

There's currently no Ruby library which can parse CSS3 files.
The two existing Ruby CSS parser libraries, CSSPool and Ruby CSS Parser, have partial or buggy support of critical CSS3 features like Media Queries.

Fortunately, there's Sass, and, with a little extension, you can make it extract CSS declarations in a nice array.

require 'sass'

class Sass::Tree::Visitors::ToArray < Sass::Tree::Visitors::Base
  protected

  def initialize
    @array = []
  end

  def visit(node, parent = false)
    if node_name(parent) == "root"
      @media = "all"
    end

    method = "visit_#{node_name(node)}"

    if self.respond_to?(method, true)
      self.send(method, node)
    else
      visit_children(node)
    end

    @array
  end

  def visit_children(parent)
    parent.children.map {|c| visit(c, parent)}
  end

  def visit_root(node)
    visit_children(node)
  end

  def visit_media(node)
    @media = node.query.join('')
    visit_children(node)
  end

  def visit_rule(node)
    @selector = node.rule[0]
    visit_children(node)
  end

  def visit_prop(node)
    return unless node.value

    @array << {
      media: @media,
      selector: @selector,
      property: node.name[0],
      value: node.value.to_sass
    }
  end
end

class Sass::Tree::Node
  def to_a
    Sass::Tree::Visitors::ToArray.visit(self)
  end
end

With the code above, this

Sass::Engine.new("body {color: #f00}, p {font-size: 20px}", :syntax => :scss).to_tree.to_a

will result in

> [{:media=>"all", :selector=>"body", :property=>"color", :value=>"#f00"}, {:media=>"all", :selector=>"p", :property=>"font-size", :value=>"20px"}]

That's all. Happy CSS3 parsing!