| Class | Sass::CSS |
| In: |
lib/sass/css.rb
|
| Parent: | Object |
This class converts CSS documents into Sass or SCSS templates. It works by parsing the CSS document into a {Sass::Tree} structure, and then applying various transformations to the structure to produce more concise and idiomatic Sass/SCSS.
Example usage:
Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
@param template [String] The CSS stylesheet.
This stylesheet can be encoded using any encoding
that can be converted to Unicode.
If the stylesheet contains an `@charset` declaration,
that overrides the Ruby encoding
(see {file:SASS_REFERENCE.md#encodings the encoding documentation})
@option options :old [Boolean] (false)
Whether or not to output old property syntax
(`:color blue` as opposed to `color: blue`).
This is only meaningful when generating Sass code,
rather than SCSS.
# File lib/sass/css.rb, line 28
28: def initialize(template, options = {})
29: if template.is_a? IO
30: template = template.read
31: end
32:
33: @options = options.dup
34: # Backwards compatibility
35: @options[:old] = true if @options[:alternate] == false
36: @template = template
37: end
Converts the CSS template into Sass or SCSS code.
@param fmt [Symbol] `:sass` or `:scss`, designating the format to return. @return [String] The resulting Sass or SCSS code @raise [Sass::SyntaxError] if there‘s an error parsing the CSS template
# File lib/sass/css.rb, line 44
44: def render(fmt = :sass)
45: check_encoding!
46: build_tree.send("to_#{fmt}", @options).strip + "\n"
47: rescue Sass::SyntaxError => err
48: err.modify_backtrace(:filename => @options[:filename] || '(css)')
49: raise err
50: end
Returns the original encoding of the document, or `nil` under Ruby 1.8.
@return [Encoding, nil] @raise [Encoding::UndefinedConversionError] if the source encoding
cannot be converted to UTF-8
@raise [ArgumentError] if the document uses an unknown encoding with `@charset`
# File lib/sass/css.rb, line 59
59: def source_encoding
60: check_encoding!
61: @original_encoding
62: end
Parses the CSS template and applies various transformations
@return [Tree::Node] The root node of the parsed tree
# File lib/sass/css.rb, line 77
77: def build_tree
78: root = Sass::SCSS::CssParser.new(@template).parse
79: expand_commas root
80: parent_ref_rules root
81: remove_parent_refs root
82: flatten_rules root
83: fold_commas root
84: root
85: end
# File lib/sass/css.rb, line 66
66: def check_encoding!
67: return if @checked_encoding
68: @checked_encoding = true
69: @template, @original_encoding = Haml::Util.check_sass_encoding(@template) do |msg, line|
70: raise Sass::SyntaxError.new(msg, :line => line)
71: end
72: end
Transform
foo, bar, baz
color: blue
into
foo
color: blue
bar
color: blue
baz
color: blue
@param root [Tree::Node] The parent node
# File lib/sass/css.rb, line 102
102: def expand_commas(root)
103: root.children.map! do |child|
104: unless child.is_a?(Tree::RuleNode) && child.rule.first.include?(',')
105: expand_commas(child) if child.is_a?(Tree::DirectiveNode)
106: next child
107: end
108: child.rule.first.split(',').map do |rule|
109: node = Tree::RuleNode.new([rule.strip])
110: node.children = child.children
111: node
112: end
113: end
114: root.children.flatten!
115: end
Flattens a single rule
@param rule [Tree::RuleNode] The candidate for flattening @see flatten_rules
# File lib/sass/css.rb, line 243
243: def flatten_rule(rule)
244: while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
245: child = rule.children.first
246:
247: if child.rule.first[0] == ?&
248: rule.rule = [child.rule.first.gsub(/^&/, rule.rule.first)]
249: else
250: rule.rule = ["#{rule.rule.first} #{child.rule.first}"]
251: end
252:
253: rule.children = child.children
254: end
255:
256: flatten_rules(rule)
257: end
Flatten rules so that
foo
bar
color: red
becomes
foo bar
color: red
and
foo
&.bar
color: blue
becomes
foo.bar
color: blue
@param root [Tree::Node] The parent node
# File lib/sass/css.rb, line 228
228: def flatten_rules(root)
229: root.children.each do |child|
230: case child
231: when Tree::RuleNode
232: flatten_rule(child)
233: when Tree::DirectiveNode
234: flatten_rules(child)
235: end
236: end
237: end
Transform
foo
bar
color: blue
baz
color: blue
into
foo
bar, baz
color: blue
@param rule [Tree::RuleNode] The candidate for flattening
# File lib/sass/css.rb, line 274
274: def fold_commas(root)
275: prev_rule = nil
276: root.children.map! do |child|
277: unless child.is_a?(Tree::RuleNode)
278: fold_commas(child) if child.is_a?(Tree::DirectiveNode)
279: next child
280: end
281:
282: if prev_rule && prev_rule.children == child.children
283: prev_rule.rule.first << ", #{child.rule.first}"
284: next nil
285: end
286:
287: fold_commas(child)
288: prev_rule = child
289: child
290: end
291: root.children.compact!
292: end
Make rules use parent refs so that
foo
color: green
foo.bar
color: blue
becomes
foo
color: green
&.bar
color: blue
This has the side effect of nesting rules, so that
foo
color: green
foo bar
color: red
foo baz
color: blue
becomes
foo
color: green
& bar
color: red
& baz
color: blue
@param root [Tree::Node] The parent node
# File lib/sass/css.rb, line 151
151: def parent_ref_rules(root)
152: current_rule = nil
153: root.children.map! do |child|
154: unless child.is_a?(Tree::RuleNode)
155: parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
156: next child
157: end
158:
159: first, rest = child.rule.first.scan(/\A(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?\Z/m).first
160:
161: if current_rule.nil? || current_rule.rule.first != first
162: current_rule = Tree::RuleNode.new([first])
163: end
164:
165: if rest
166: child.rule = ["&" + rest]
167: current_rule << child
168: else
169: current_rule.children += child.children
170: end
171:
172: current_rule
173: end
174: root.children.compact!
175: root.children.uniq!
176:
177: root.children.each { |v| parent_ref_rules(v) }
178: end
Remove useless parent refs so that
foo
& bar
color: blue
becomes
foo
bar
color: blue
@param root [Tree::Node] The parent node
# File lib/sass/css.rb, line 193
193: def remove_parent_refs(root)
194: root.children.each do |child|
195: case child
196: when Tree::RuleNode
197: child.rule.first.gsub! /^& +/, ''
198: remove_parent_refs child
199: when Tree::DirectiveNode
200: remove_parent_refs child
201: end
202: end
203: end