class Sass::CSS

Sass::CSS.new(“p { color: blue }”).render(:scss) #=> “p {n color: blue; }”
Sass::CSS.new(“p { color: blue }”).render(:sass) #=> “pn color: blue”
Example usage:
to produce more concise and idiomatic Sass/SCSS.
and then applying various transformations to the structure
It works by parsing the CSS document into a {Sass::Tree} structure,
This class converts CSS documents into Sass or SCSS templates.

def bubble_subject(root)

def bubble_subject(root)
  root.children.each do |child|
    bubble_subject(child) if child.is_a?(Tree::RuleNode) || child.is_a?(Tree::DirectiveNode)
    next unless child.is_a?(Tree::RuleNode) && !child.children.empty?
    next unless child.children.all? do |c|
      next unless c.is_a?(Tree::RuleNode)
      first_simple_sel(c).is_a?(Sass::Selector::Parent) && first_sseq(c).subject?
    end
    first_sseq(child).subject = true
    child.children.each {|c| first_sseq(c).subject = false}
  end
end

def build_tree

Returns:
  • (Tree::Node) - The root node of the parsed tree
def build_tree
  root = Sass::SCSS::CssParser.new(@template, @options[:filename], nil).parse
  parse_selectors(root)
  expand_commas(root)
  nest_seqs(root)
  parent_ref_rules(root)
  flatten_rules(root)
  bubble_subject(root)
  fold_commas(root)
  dump_selectors(root)
  root
end

def check_encoding!

def check_encoding!
  return if @checked_encoding
  @checked_encoding = true
  @template, @original_encoding = Sass::Util.check_sass_encoding(@template)
end

def dump_selectors(root)

Parameters:
  • root (Tree::Node) -- The parent node
def dump_selectors(root)
  root.children.each do |child|
    next dump_selectors(child) if child.is_a?(Tree::DirectiveNode)
    next unless child.is_a?(Tree::RuleNode)
    child.rule = [child.parsed_rules.to_s]
    dump_selectors(child)
  end
end

def expand_commas(root)

Parameters:
  • root (Tree::Node) -- The parent node
def expand_commas(root)
  root.children.map! do |child|
    # child.parsed_rules.members.size > 1 iff the rule contains a comma
    unless child.is_a?(Tree::RuleNode) && child.parsed_rules.members.size > 1
      expand_commas(child) if child.is_a?(Tree::DirectiveNode)
      next child
    end
    child.parsed_rules.members.map do |seq|
      node = Tree::RuleNode.new([])
      node.parsed_rules = make_cseq(seq)
      node.children = child.children
      node
    end
  end
  root.children.flatten!
end

def first_seq(rule)

Returns:
  • (Sass::Selector::Sequence) -

Parameters:
  • rule (Sass::Tree::RuleNode) --
def first_seq(rule)
  rule.parsed_rules.members.first
end

def first_simple_sel(rule)

Returns:
  • (Sass::Selector::Simple?) -

Parameters:
  • rule (Sass::Tree::RuleNode) --
def first_simple_sel(rule)
  sseq = first_sseq(rule)
  return unless sseq.is_a?(Sass::Selector::SimpleSequence)
  sseq.members.first
end

def first_sseq(rule)

Returns:
  • (Sass::Selector::SimpleSequence, String) -

Parameters:
  • rule (Sass::Tree::RuleNode) --
def first_sseq(rule)
  first_seq(rule).members.first
end

def flatten_rule(rule)

Other tags:
    See: #flatten_rules -

Parameters:
  • rule (Tree::RuleNode) -- The candidate for flattening
def flatten_rule(rule)
  while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
    child = rule.children.first
    if first_simple_sel(child).is_a?(Sass::Selector::Parent)
      rule.parsed_rules = child.parsed_rules.resolve_parent_refs(rule.parsed_rules)
    else
      rule.parsed_rules = make_seq(*(first_seq(rule).members + first_seq(child).members))
    end
    rule.children = child.children
  end
  flatten_rules(rule)
end

def flatten_rules(root)

Parameters:
  • root (Tree::Node) -- The parent node
def flatten_rules(root)
  root.children.each do |child|
    case child
    when Tree::RuleNode
      flatten_rule(child)
    when Tree::DirectiveNode
      flatten_rules(child)
    end
  end
end

def fold_commas(root)

Parameters:
  • root (Tree::Node) -- The parent node
def fold_commas(root)
  prev_rule = nil
  root.children.map! do |child|
    unless child.is_a?(Tree::RuleNode)
      fold_commas(child) if child.is_a?(Tree::DirectiveNode)
      next child
    end
    if prev_rule && prev_rule.children.map {|c| c.to_sass} == child.children.map {|c| c.to_sass}
      prev_rule.parsed_rules.members << first_seq(child)
      next nil
    end
    fold_commas(child)
    prev_rule = child
    child
  end
  root.children.compact!
end

def initialize(template, options = {})

Options Hash: (**options)
  • :indent (String) --
  • :old (Boolean) --

Parameters:
  • template (String) -- The CSS stylesheet.
def initialize(template, options = {})
  if template.is_a? IO
    template = template.read
  end
  @options = options.merge(:_convert => true)
  # Backwards compatibility
  @options[:old] = true if @options[:alternate] == false
  @template = template
  @checked_encoding = false
end

def make_cseq(*seqs)

Returns:
  • (Sass::Selector::CommaSequence) -

Parameters:
  • seqs (Array) --
def make_cseq(*seqs)
  Sass::Selector::CommaSequence.new(seqs)
end

def make_seq(*sseqs)

Returns:
  • (Sass::Selector::CommaSequence) -

Parameters:
  • sseqs (Array) --
def make_seq(*sseqs)
  make_cseq(Sass::Selector::Sequence.new(sseqs))
end

def make_sseq(subject, *sseqs)

Returns:
  • (Sass::Selector::CommaSequence) -

Parameters:
  • sseqs (Array) --
  • subject (Boolean) -- Whether this is a subject selector
def make_sseq(subject, *sseqs)
  make_seq(Sass::Selector::SimpleSequence.new(sseqs, subject))
end

def nest_seqs(root)

Parameters:
  • root (Tree::Node) -- The parent node
def nest_seqs(root)
  current_rule = nil
  root.children.map! do |child|
    unless child.is_a?(Tree::RuleNode)
      nest_seqs(child) if child.is_a?(Tree::DirectiveNode)
      next child
    end
    seq = first_seq(child)
    seq.members.reject! {|sseq| sseq == "\n"}
    first, rest = seq.members.first, seq.members[1..-1]
    if current_rule.nil? || first_sseq(current_rule) != first
      current_rule = Tree::RuleNode.new([])
      current_rule.parsed_rules = make_seq(first)
    end
    if rest.empty?
      current_rule.children += child.children
    else
      child.parsed_rules = make_seq(*rest)
      current_rule << child
    end
    current_rule
  end
  root.children.compact!
  root.children.uniq!
  root.children.each {|v| nest_seqs(v)}
end

def parent_ref_rules(root)

Parameters:
  • root (Tree::Node) -- The parent node
def parent_ref_rules(root)
  current_rule = nil
  root.children.map! do |child|
    unless child.is_a?(Tree::RuleNode)
      parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
      next child
    end
    sseq = first_sseq(child)
    next child unless sseq.is_a?(Sass::Selector::SimpleSequence)
    firsts, rest = [sseq.members.first], sseq.members[1..-1]
    firsts.push rest.shift if firsts.first.is_a?(Sass::Selector::Parent)
    last_simple_subject = rest.empty? && sseq.subject?
    if current_rule.nil? || first_sseq(current_rule).members != firsts ||
        !!first_sseq(current_rule).subject? != !!last_simple_subject
      current_rule = Tree::RuleNode.new([])
      current_rule.parsed_rules = make_sseq(last_simple_subject, *firsts)
    end
    if rest.empty?
      current_rule.children += child.children
    else
      rest.unshift Sass::Selector::Parent.new
      child.parsed_rules = make_sseq(sseq.subject?, *rest)
      current_rule << child
    end
    current_rule
  end
  root.children.compact!
  root.children.uniq!
  root.children.each {|v| parent_ref_rules(v)}
end

def parse_selectors(root)

Parameters:
  • root (Tree::Node) -- The parent node
def parse_selectors(root)
  root.children.each do |child|
    next parse_selectors(child) if child.is_a?(Tree::DirectiveNode)
    next unless child.is_a?(Tree::RuleNode)
    parser = Sass::SCSS::CssParser.new(child.rule.first, child.filename, nil, child.line)
    child.parsed_rules = parser.parse_selector
  end
end

def render(fmt = :sass)

Raises:
  • (Sass::SyntaxError) - if there's an error parsing the CSS template

Returns:
  • (String) - The resulting Sass or SCSS code

Parameters:
  • fmt (Symbol) -- `:sass` or `:scss`, designating the format to return.
def render(fmt = :sass)
  check_encoding!
  build_tree.send("to_#{fmt}", @options).strip + "\n"
rescue Sass::SyntaxError => err
  err.modify_backtrace(:filename => @options[:filename] || '(css)')
  raise err
end

def source_encoding

Raises:
  • (ArgumentError) - if the document uses an unknown encoding with `@charset`
  • (Encoding::UndefinedConversionError) - if the source encoding

Returns:
  • (Encoding, nil) -
def source_encoding
  check_encoding!
  @original_encoding
end