class Sass::CSS
Sass::CSS.new(“p { color: blue }”).render #=> “pn color: blue”
Example usage:
to produce more concise and idiomatic Sass.
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 templates.
def assert_match(re)
-
re
(Regexp
) -- The regular expression to assert
def assert_match(re) if @template.scan(re) whitespace return end line = @template.string[0..@template.pos].count "\n" pos = @template.pos after = @template.string[pos - 15...pos] after = "..." + after if pos >= 15 # Display basic regexps as plain old strings expected = re.source == Regexp.escape(re.source) ? "\"#{re.source}\"" : re.inspect was = @template.rest[0...15] was += "..." if @template.rest.size >= 15 raise Exception.new(<<MESSAGE) lid CSS on line #{line + 1} after #{after.inspect}: pected #{expected}, was #{was.inspect} AGE end
def build_tree
-
(Tree::Node)
- The root node of the parsed tree
def build_tree root = Tree::Node.new whitespace rules root expand_commas root parent_ref_rules root remove_parent_refs root flatten_rules root fold_commas root root end
def expand_commas(root)
-
root
(Tree::Node
) -- The parent node
def expand_commas(root) root.children.map! do |child| next child unless Tree::RuleNode === child && child.rules.first.include?(',') child.rules.first.split(',').map do |rule| node = Tree::RuleNode.new(rule.strip) node.children = child.children node end end root.children.flatten! end
def flatten_rule(rule)
- 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 child.rules.first[0] == ?& rule.rules = [child.rules.first.gsub(/^&/, rule.rules.first)] else rule.rules = ["#{rule.rules.first} #{child.rules.first}"] end rule.children = child.children end flatten_rules(rule) end
def flatten_rules(root)
-
root
(Tree::Node
) -- The parent node
def flatten_rules(root) root.children.each { |child| flatten_rule(child) if child.is_a?(Tree::RuleNode) } end
def fold_commas(root)
-
rule
(Tree::RuleNode
) -- The candidate for flattening
def fold_commas(root) prev_rule = nil root.children.map! do |child| next child unless child.is_a?(Tree::RuleNode) if prev_rule && prev_rule.children == child.children prev_rule.rules.first << ", #{child.rules.first}" next nil end fold_commas(child) prev_rule = child child end root.children.compact! end
def initialize(template, options = {})
(**options)
-
:old
(Boolean
) --
Parameters:
-
template
(String
) -- The CSS code
def initialize(template, options = {}) if template.is_a? IO template = template.read end @options = options.dup # Backwards compatibility @options[:old] = true if @options[:alternate] == false @template = StringScanner.new(template) end
def parent_ref_rules(root)
-
root
(Tree::Node
) -- The parent node
def parent_ref_rules(root) current_rule = nil root.children.select { |c| Tree::RuleNode === c }.each do |child| root.children.delete child first, rest = child.rules.first.scan(/^(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?$/).first if current_rule.nil? || current_rule.rules.first != first current_rule = Tree::RuleNode.new(first) root << current_rule end if rest child.rules = ["&" + rest] current_rule << child else current_rule.children += child.children end end root.children.each { |v| parent_ref_rules(v) } end
def properties(rule)
-
rule
(Tree::RuleNode
) -- The parent node of the properties
def properties(rule) while @template.scan(/[^:\}\s]+/) name = @template[0] whitespace assert_match /:/ value = '' while @template.scan(/[^;\s\}]+/) value << @template[0] << whitespace end assert_match /(;|(?=\}))/ rule << Tree::PropNode.new(name, value, nil) end assert_match /\}/ end
def remove_parent_refs(root)
-
root
(Tree::Node
) -- The parent node
def remove_parent_refs(root) root.children.each do |child| if child.is_a?(Tree::RuleNode) child.rules.first.gsub! /^& +/, '' remove_parent_refs child end end end
def render
-
(String)
- The resulting Sass code
def render begin build_tree.to_sass(0, @options).strip + "\n" rescue Exception => err line = @template.string[0...@template.pos].split("\n").size err.backtrace.unshift "(css):#{line}" raise err end end
def rule
-
(Tree::Node)
- The parsed rule
def rule rule = "" loop do token = @template.scan(/(?:[^\{\};\/\s]|\/[^*])+/) if token.nil? return if rule.empty? break end rule << token break unless @template.match?(/\s|\/\*/) whitespace rule << " " end rule.strip! directive = rule[0] == ?@ if directive node = Tree::DirectiveNode.new(rule) return node if @template.scan(/;/) assert_match /\{/ whitespace rules(node) return node end assert_match /\{/ node = Tree::RuleNode.new(rule) properties(node) return node end
def rules(root)
-
root
(Tree::Node
) -- The parent node of the rules
def rules(root) while r = rule root << r whitespace end end
def whitespace
-
(String)
- The ignored whitespace
def whitespace space = @template.scan(/\s*/) || '' # If we've hit a comment, # go past it and look for more whitespace if @template.scan(/\/\*/) @template.scan_until(/\*\//) return space + whitespace end return space end