Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use SASS to parse a list of all CSS selectors in an .scss file?

Tags:

sass

ruby

I'd like to programmatically parse an .scss file to generate a flat list of selectors that are used in that file, mostly as basis for some static code analysis.

In SASS terms, I'm looking for a way to get a list of all Sass::Tree::RuleNode for a given .scss file.

So far I'm using Sass::Engine.for_file to create a Tree. Then, in order to use Sass::Tree::RuleNode.resolved_rules on the individual Nodes I have to use Tree::Visitors::Cssize, according to the documentation. But somewhere in there is a (probably simple) error.

require 'sass'

sass_engine = Sass::Engine.for_file('files/examples.scss',{
  :style => :compact,
  :load_paths => ['files'],
  :syntax => :scss,
})

tree = sass_engine.to_tree

Sass::Tree::Visitors::Cssize.visit(tree)

tree.each do |node|
  if node.is_a? Sass::Tree::RuleNode
    puts node.resolved_rules
  end
end

This is the error and stacktrace I'm getting:

ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:186:in `visit_prop': undefined method `empty?' for nil:NilClass (NoMethodError)
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:37:in `visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:21:in `visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:53:in `block in visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:53:in `map'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:53:in `visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:30:in `block in visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:52:in `with_parent'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:29:in `visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:37:in `block in visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:201:in `visit_rule'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:37:in `visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:21:in `visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:53:in `block in visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:53:in `map'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:53:in `visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:30:in `block in visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:52:in `with_parent'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:29:in `visit_children'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:37:in `block in visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:65:in `visit_root'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:37:in `visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:21:in `visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/base.rb:24:in `visit'
    ruby-1.9.3-p362/gems/sass-3.2.10/lib/sass/tree/visitors/cssize.rb:6:in `visit'

Thanks a lot in advance.

like image 434
timme Avatar asked Aug 07 '13 09:08

timme


1 Answers

I'm not overly familiar with the Sass gem codebase but was able to get this working (without crashing, at least) by following the code example of the Cssize class from sass gem's sass/tree/root_node.rb file, which has:

  # Runs the dynamic Sass code *and* computes the CSS for the tree.
  # @see #to_s
  def render
    Visitors::CheckNesting.visit(self)
    result = Visitors::Perform.visit(self)
    Visitors::CheckNesting.visit(result) # Check again to validate mixins
    result, extends = Visitors::Cssize.visit(result)
    Visitors::Extend.visit(result, extends)
    result.to_s
  end

Taking this code snippet and applying it to your script, I get:

#!/usr/bin/env ruby

require 'sass'

sass_engine = Sass::Engine.for_file('files/examples.scss',{
  :style => :compact,
  :load_paths => ['files'],
  :syntax => :scss,
})

tree = sass_engine.to_tree

Sass::Tree::Visitors::CheckNesting.visit(tree)
result = Sass::Tree::Visitors::Perform.visit(tree)
Sass::Tree::Visitors::CheckNesting.visit(result)
result, extends = Sass::Tree::Visitors::Cssize.visit(result)
Sass::Tree::Visitors::Extend.visit(result, extends)

result.each do |node|
  if node.is_a? Sass::Tree::RuleNode
    puts node.resolved_rules
  end
end
like image 104
Stuart M Avatar answered Sep 29 '22 03:09

Stuart M