xass 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +11 -0
- data/CONTRIBUTING +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +201 -0
- data/Rakefile +349 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/push +13 -0
- data/bin/sass +13 -0
- data/bin/sass-convert +12 -0
- data/bin/scss +13 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +18 -0
- data/lib/sass/cache_stores/base.rb +88 -0
- data/lib/sass/cache_stores/chain.rb +33 -0
- data/lib/sass/cache_stores/filesystem.rb +64 -0
- data/lib/sass/cache_stores/memory.rb +47 -0
- data/lib/sass/cache_stores/null.rb +25 -0
- data/lib/sass/cache_stores.rb +15 -0
- data/lib/sass/callbacks.rb +66 -0
- data/lib/sass/css.rb +409 -0
- data/lib/sass/engine.rb +930 -0
- data/lib/sass/environment.rb +101 -0
- data/lib/sass/error.rb +201 -0
- data/lib/sass/exec.rb +707 -0
- data/lib/sass/importers/base.rb +139 -0
- data/lib/sass/importers/filesystem.rb +186 -0
- data/lib/sass/importers.rb +22 -0
- data/lib/sass/logger/base.rb +32 -0
- data/lib/sass/logger/log_level.rb +49 -0
- data/lib/sass/logger.rb +15 -0
- data/lib/sass/media.rb +213 -0
- data/lib/sass/plugin/compiler.rb +406 -0
- data/lib/sass/plugin/configuration.rb +123 -0
- data/lib/sass/plugin/generic.rb +15 -0
- data/lib/sass/plugin/merb.rb +48 -0
- data/lib/sass/plugin/rack.rb +60 -0
- data/lib/sass/plugin/rails.rb +47 -0
- data/lib/sass/plugin/staleness_checker.rb +199 -0
- data/lib/sass/plugin.rb +133 -0
- data/lib/sass/railtie.rb +10 -0
- data/lib/sass/repl.rb +57 -0
- data/lib/sass/root.rb +7 -0
- data/lib/sass/script/arg_list.rb +52 -0
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +606 -0
- data/lib/sass/script/css_lexer.rb +29 -0
- data/lib/sass/script/css_parser.rb +31 -0
- data/lib/sass/script/funcall.rb +245 -0
- data/lib/sass/script/functions.rb +1543 -0
- data/lib/sass/script/interpolation.rb +79 -0
- data/lib/sass/script/lexer.rb +345 -0
- data/lib/sass/script/list.rb +85 -0
- data/lib/sass/script/literal.rb +221 -0
- data/lib/sass/script/node.rb +99 -0
- data/lib/sass/script/null.rb +37 -0
- data/lib/sass/script/number.rb +453 -0
- data/lib/sass/script/operation.rb +110 -0
- data/lib/sass/script/parser.rb +502 -0
- data/lib/sass/script/string.rb +51 -0
- data/lib/sass/script/string_interpolation.rb +103 -0
- data/lib/sass/script/unary_operation.rb +69 -0
- data/lib/sass/script/variable.rb +58 -0
- data/lib/sass/script.rb +39 -0
- data/lib/sass/scss/css_parser.rb +36 -0
- data/lib/sass/scss/parser.rb +1180 -0
- data/lib/sass/scss/rx.rb +133 -0
- data/lib/sass/scss/script_lexer.rb +15 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/scss/static_parser.rb +54 -0
- data/lib/sass/scss.rb +16 -0
- data/lib/sass/selector/abstract_sequence.rb +94 -0
- data/lib/sass/selector/comma_sequence.rb +92 -0
- data/lib/sass/selector/sequence.rb +507 -0
- data/lib/sass/selector/simple.rb +119 -0
- data/lib/sass/selector/simple_sequence.rb +215 -0
- data/lib/sass/selector.rb +452 -0
- data/lib/sass/shared.rb +76 -0
- data/lib/sass/supports.rb +229 -0
- data/lib/sass/tree/charset_node.rb +22 -0
- data/lib/sass/tree/comment_node.rb +82 -0
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +60 -0
- data/lib/sass/tree/debug_node.rb +18 -0
- data/lib/sass/tree/directive_node.rb +42 -0
- data/lib/sass/tree/each_node.rb +24 -0
- data/lib/sass/tree/extend_node.rb +36 -0
- data/lib/sass/tree/for_node.rb +36 -0
- data/lib/sass/tree/function_node.rb +34 -0
- data/lib/sass/tree/if_node.rb +52 -0
- data/lib/sass/tree/import_node.rb +75 -0
- data/lib/sass/tree/media_node.rb +58 -0
- data/lib/sass/tree/mixin_def_node.rb +38 -0
- data/lib/sass/tree/mixin_node.rb +39 -0
- data/lib/sass/tree/node.rb +196 -0
- data/lib/sass/tree/prop_node.rb +152 -0
- data/lib/sass/tree/return_node.rb +18 -0
- data/lib/sass/tree/root_node.rb +78 -0
- data/lib/sass/tree/rule_node.rb +132 -0
- data/lib/sass/tree/supports_node.rb +51 -0
- data/lib/sass/tree/trace_node.rb +32 -0
- data/lib/sass/tree/variable_node.rb +30 -0
- data/lib/sass/tree/visitors/base.rb +75 -0
- data/lib/sass/tree/visitors/check_nesting.rb +147 -0
- data/lib/sass/tree/visitors/convert.rb +316 -0
- data/lib/sass/tree/visitors/cssize.rb +241 -0
- data/lib/sass/tree/visitors/deep_copy.rb +102 -0
- data/lib/sass/tree/visitors/extend.rb +68 -0
- data/lib/sass/tree/visitors/perform.rb +446 -0
- data/lib/sass/tree/visitors/set_options.rb +125 -0
- data/lib/sass/tree/visitors/to_css.rb +228 -0
- data/lib/sass/tree/warn_node.rb +18 -0
- data/lib/sass/tree/while_node.rb +18 -0
- data/lib/sass/util/multibyte_string_scanner.rb +155 -0
- data/lib/sass/util/subset_map.rb +109 -0
- data/lib/sass/util/test.rb +10 -0
- data/lib/sass/util.rb +948 -0
- data/lib/sass/version.rb +126 -0
- data/lib/sass.rb +95 -0
- data/rails/init.rb +1 -0
- data/test/Gemfile +3 -0
- data/test/Gemfile.lock +10 -0
- data/test/sass/cache_test.rb +89 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/conversion_test.rb +1760 -0
- data/test/sass/css2sass_test.rb +458 -0
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/engine_test.rb +3244 -0
- data/test/sass/exec_test.rb +86 -0
- data/test/sass/extend_test.rb +1482 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
- data/test/sass/functions_test.rb +1139 -0
- data/test/sass/importer_test.rb +192 -0
- data/test/sass/logger_test.rb +58 -0
- data/test/sass/mock_importer.rb +49 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +564 -0
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/basic.css +9 -0
- data/test/sass/results/cached_import_option.css +3 -0
- data/test/sass/results/compact.css +5 -0
- data/test/sass/results/complex.css +86 -0
- data/test/sass/results/compressed.css +1 -0
- data/test/sass/results/expanded.css +19 -0
- data/test/sass/results/filename_fn.css +3 -0
- data/test/sass/results/if.css +3 -0
- data/test/sass/results/import.css +31 -0
- data/test/sass/results/import_charset.css +5 -0
- data/test/sass/results/import_charset_1_8.css +5 -0
- data/test/sass/results/import_charset_ibm866.css +5 -0
- data/test/sass/results/import_content.css +1 -0
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/mixins.css +95 -0
- data/test/sass/results/multiline.css +24 -0
- data/test/sass/results/nested.css +22 -0
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/script.css +16 -0
- data/test/sass/results/scss_import.css +31 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +3 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/results/warn.css +0 -0
- data/test/sass/results/warn_imported.css +0 -0
- data/test/sass/script_conversion_test.rb +299 -0
- data/test/sass/script_test.rb +622 -0
- data/test/sass/scss/css_test.rb +1100 -0
- data/test/sass/scss/rx_test.rb +156 -0
- data/test/sass/scss/scss_test.rb +2106 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/templates/_cached_import_option_partial.scss +1 -0
- data/test/sass/templates/_double_import_loop2.sass +1 -0
- data/test/sass/templates/_filename_fn_import.scss +11 -0
- data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
- data/test/sass/templates/_imported_charset_utf8.sass +4 -0
- data/test/sass/templates/_imported_content.sass +3 -0
- data/test/sass/templates/_partial.sass +2 -0
- data/test/sass/templates/_same_name_different_partiality.scss +1 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/basic.sass +23 -0
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/bork5.sass +3 -0
- data/test/sass/templates/cached_import_option.scss +3 -0
- data/test/sass/templates/compact.sass +17 -0
- data/test/sass/templates/complex.sass +305 -0
- data/test/sass/templates/compressed.sass +15 -0
- data/test/sass/templates/double_import_loop1.sass +1 -0
- data/test/sass/templates/expanded.sass +17 -0
- data/test/sass/templates/filename_fn.scss +18 -0
- data/test/sass/templates/if.sass +11 -0
- data/test/sass/templates/import.sass +12 -0
- data/test/sass/templates/import_charset.sass +9 -0
- data/test/sass/templates/import_charset_1_8.sass +6 -0
- data/test/sass/templates/import_charset_ibm866.sass +11 -0
- data/test/sass/templates/import_content.sass +4 -0
- data/test/sass/templates/importee.less +2 -0
- data/test/sass/templates/importee.sass +19 -0
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +76 -0
- data/test/sass/templates/multiline.sass +20 -0
- data/test/sass/templates/nested.sass +25 -0
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_import.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/same_name_different_ext.sass +2 -0
- data/test/sass/templates/same_name_different_ext.scss +1 -0
- data/test/sass/templates/same_name_different_partiality.scss +1 -0
- data/test/sass/templates/script.sass +101 -0
- data/test/sass/templates/scss_import.scss +11 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/single_import_loop.sass +1 -0
- data/test/sass/templates/subdir/import_up1.scss +1 -0
- data/test/sass/templates/subdir/import_up2.scss +1 -0
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- data/test/sass/templates/units.sass +11 -0
- data/test/sass/templates/warn.sass +3 -0
- data/test/sass/templates/warn_imported.sass +4 -0
- data/test/sass/test_helper.rb +8 -0
- data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
- data/test/sass/util/subset_map_test.rb +91 -0
- data/test/sass/util_test.rb +382 -0
- data/test/test_helper.rb +80 -0
- metadata +354 -0
data/lib/sass/css.rb
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../sass'
|
|
2
|
+
require 'sass/tree/node'
|
|
3
|
+
require 'sass/scss/css_parser'
|
|
4
|
+
|
|
5
|
+
module Sass
|
|
6
|
+
# This class converts CSS documents into Sass or SCSS templates.
|
|
7
|
+
# It works by parsing the CSS document into a {Sass::Tree} structure,
|
|
8
|
+
# and then applying various transformations to the structure
|
|
9
|
+
# to produce more concise and idiomatic Sass/SCSS.
|
|
10
|
+
#
|
|
11
|
+
# Example usage:
|
|
12
|
+
#
|
|
13
|
+
# Sass::CSS.new("p { color: blue }").render(:sass) #=> "p\n color: blue"
|
|
14
|
+
# Sass::CSS.new("p { color: blue }").render(:scss) #=> "p {\n color: blue; }"
|
|
15
|
+
class CSS
|
|
16
|
+
# @param template [String] The CSS stylesheet.
|
|
17
|
+
# This stylesheet can be encoded using any encoding
|
|
18
|
+
# that can be converted to Unicode.
|
|
19
|
+
# If the stylesheet contains an `@charset` declaration,
|
|
20
|
+
# that overrides the Ruby encoding
|
|
21
|
+
# (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
|
|
22
|
+
# @option options :old [Boolean] (false)
|
|
23
|
+
# Whether or not to output old property syntax
|
|
24
|
+
# (`:color blue` as opposed to `color: blue`).
|
|
25
|
+
# This is only meaningful when generating Sass code,
|
|
26
|
+
# rather than SCSS.
|
|
27
|
+
# @option options :indent [String] (" ")
|
|
28
|
+
# The string to use for indenting each line. Defaults to two spaces.
|
|
29
|
+
def initialize(template, options = {})
|
|
30
|
+
if template.is_a? IO
|
|
31
|
+
template = template.read
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@options = options.dup
|
|
35
|
+
# Backwards compatibility
|
|
36
|
+
@options[:old] = true if @options[:alternate] == false
|
|
37
|
+
@template = template
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Converts the CSS template into Sass or SCSS code.
|
|
41
|
+
#
|
|
42
|
+
# @param fmt [Symbol] `:sass` or `:scss`, designating the format to return.
|
|
43
|
+
# @return [String] The resulting Sass or SCSS code
|
|
44
|
+
# @raise [Sass::SyntaxError] if there's an error parsing the CSS template
|
|
45
|
+
def render(fmt = :sass)
|
|
46
|
+
check_encoding!
|
|
47
|
+
build_tree.send("to_#{fmt}", @options).strip + "\n"
|
|
48
|
+
rescue Sass::SyntaxError => err
|
|
49
|
+
err.modify_backtrace(:filename => @options[:filename] || '(css)')
|
|
50
|
+
raise err
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns the original encoding of the document,
|
|
54
|
+
# or `nil` under Ruby 1.8.
|
|
55
|
+
#
|
|
56
|
+
# @return [Encoding, nil]
|
|
57
|
+
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
|
58
|
+
# cannot be converted to UTF-8
|
|
59
|
+
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
|
60
|
+
def source_encoding
|
|
61
|
+
check_encoding!
|
|
62
|
+
@original_encoding
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def check_encoding!
|
|
68
|
+
return if @checked_encoding
|
|
69
|
+
@checked_encoding = true
|
|
70
|
+
@template, @original_encoding = Sass::Util.check_sass_encoding(@template) do |msg, line|
|
|
71
|
+
raise Sass::SyntaxError.new(msg, :line => line)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Parses the CSS template and applies various transformations
|
|
76
|
+
#
|
|
77
|
+
# @return [Tree::Node] The root node of the parsed tree
|
|
78
|
+
def build_tree
|
|
79
|
+
root = Sass::SCSS::CssParser.new(@template, @options[:filename]).parse
|
|
80
|
+
parse_selectors root
|
|
81
|
+
expand_commas root
|
|
82
|
+
nest_seqs root
|
|
83
|
+
parent_ref_rules root
|
|
84
|
+
flatten_rules root
|
|
85
|
+
bubble_subject root
|
|
86
|
+
fold_commas root
|
|
87
|
+
dump_selectors root
|
|
88
|
+
root
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Parse all the selectors in the document and assign them to
|
|
92
|
+
# {Sass::Tree::RuleNode#parsed_rules}.
|
|
93
|
+
#
|
|
94
|
+
# @param root [Tree::Node] The parent node
|
|
95
|
+
def parse_selectors(root)
|
|
96
|
+
root.children.each do |child|
|
|
97
|
+
next parse_selectors(child) if child.is_a?(Tree::DirectiveNode)
|
|
98
|
+
next unless child.is_a?(Tree::RuleNode)
|
|
99
|
+
parser = Sass::SCSS::CssParser.new(child.rule.first, child.filename, child.line)
|
|
100
|
+
child.parsed_rules = parser.parse_selector
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Transform
|
|
105
|
+
#
|
|
106
|
+
# foo, bar, baz
|
|
107
|
+
# color: blue
|
|
108
|
+
#
|
|
109
|
+
# into
|
|
110
|
+
#
|
|
111
|
+
# foo
|
|
112
|
+
# color: blue
|
|
113
|
+
# bar
|
|
114
|
+
# color: blue
|
|
115
|
+
# baz
|
|
116
|
+
# color: blue
|
|
117
|
+
#
|
|
118
|
+
# @param root [Tree::Node] The parent node
|
|
119
|
+
def expand_commas(root)
|
|
120
|
+
root.children.map! do |child|
|
|
121
|
+
# child.parsed_rules.members.size > 1 iff the rule contains a comma
|
|
122
|
+
unless child.is_a?(Tree::RuleNode) && child.parsed_rules.members.size > 1
|
|
123
|
+
expand_commas(child) if child.is_a?(Tree::DirectiveNode)
|
|
124
|
+
next child
|
|
125
|
+
end
|
|
126
|
+
child.parsed_rules.members.map do |seq|
|
|
127
|
+
node = Tree::RuleNode.new([])
|
|
128
|
+
node.parsed_rules = make_cseq(seq)
|
|
129
|
+
node.children = child.children
|
|
130
|
+
node
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
root.children.flatten!
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Make rules use nesting so that
|
|
137
|
+
#
|
|
138
|
+
# foo
|
|
139
|
+
# color: green
|
|
140
|
+
# foo bar
|
|
141
|
+
# color: red
|
|
142
|
+
# foo baz
|
|
143
|
+
# color: blue
|
|
144
|
+
#
|
|
145
|
+
# becomes
|
|
146
|
+
#
|
|
147
|
+
# foo
|
|
148
|
+
# color: green
|
|
149
|
+
# bar
|
|
150
|
+
# color: red
|
|
151
|
+
# baz
|
|
152
|
+
# color: blue
|
|
153
|
+
#
|
|
154
|
+
# @param root [Tree::Node] The parent node
|
|
155
|
+
def nest_seqs(root)
|
|
156
|
+
current_rule = nil
|
|
157
|
+
root.children.map! do |child|
|
|
158
|
+
unless child.is_a?(Tree::RuleNode)
|
|
159
|
+
nest_seqs(child) if child.is_a?(Tree::DirectiveNode)
|
|
160
|
+
next child
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
seq = first_seq(child)
|
|
164
|
+
seq.members.reject! {|sseq| sseq == "\n"}
|
|
165
|
+
first, rest = seq.members.first, seq.members[1..-1]
|
|
166
|
+
|
|
167
|
+
if current_rule.nil? || first_sseq(current_rule) != first
|
|
168
|
+
current_rule = Tree::RuleNode.new([])
|
|
169
|
+
current_rule.parsed_rules = make_seq(first)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
unless rest.empty?
|
|
173
|
+
child.parsed_rules = make_seq(*rest)
|
|
174
|
+
current_rule << child
|
|
175
|
+
else
|
|
176
|
+
current_rule.children += child.children
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
current_rule
|
|
180
|
+
end
|
|
181
|
+
root.children.compact!
|
|
182
|
+
root.children.uniq!
|
|
183
|
+
|
|
184
|
+
root.children.each {|v| nest_seqs(v)}
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Make rules use parent refs so that
|
|
188
|
+
#
|
|
189
|
+
# foo
|
|
190
|
+
# color: green
|
|
191
|
+
# foo.bar
|
|
192
|
+
# color: blue
|
|
193
|
+
#
|
|
194
|
+
# becomes
|
|
195
|
+
#
|
|
196
|
+
# foo
|
|
197
|
+
# color: green
|
|
198
|
+
# &.bar
|
|
199
|
+
# color: blue
|
|
200
|
+
#
|
|
201
|
+
# @param root [Tree::Node] The parent node
|
|
202
|
+
def parent_ref_rules(root)
|
|
203
|
+
current_rule = nil
|
|
204
|
+
root.children.map! do |child|
|
|
205
|
+
unless child.is_a?(Tree::RuleNode)
|
|
206
|
+
parent_ref_rules(child) if child.is_a?(Tree::DirectiveNode)
|
|
207
|
+
next child
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
sseq = first_sseq(child)
|
|
211
|
+
next child unless sseq.is_a?(Sass::Selector::SimpleSequence)
|
|
212
|
+
|
|
213
|
+
firsts, rest = [sseq.members.first], sseq.members[1..-1]
|
|
214
|
+
firsts.push rest.shift if firsts.first.is_a?(Sass::Selector::Parent)
|
|
215
|
+
|
|
216
|
+
last_simple_subject = rest.empty? && sseq.subject?
|
|
217
|
+
if current_rule.nil? || first_sseq(current_rule).members != firsts ||
|
|
218
|
+
!!first_sseq(current_rule).subject? != !!last_simple_subject
|
|
219
|
+
current_rule = Tree::RuleNode.new([])
|
|
220
|
+
current_rule.parsed_rules = make_sseq(last_simple_subject, *firsts)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
unless rest.empty?
|
|
224
|
+
rest.unshift Sass::Selector::Parent.new
|
|
225
|
+
child.parsed_rules = make_sseq(sseq.subject?, *rest)
|
|
226
|
+
current_rule << child
|
|
227
|
+
else
|
|
228
|
+
current_rule.children += child.children
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
current_rule
|
|
232
|
+
end
|
|
233
|
+
root.children.compact!
|
|
234
|
+
root.children.uniq!
|
|
235
|
+
|
|
236
|
+
root.children.each {|v| parent_ref_rules(v)}
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Flatten rules so that
|
|
240
|
+
#
|
|
241
|
+
# foo
|
|
242
|
+
# bar
|
|
243
|
+
# color: red
|
|
244
|
+
#
|
|
245
|
+
# becomes
|
|
246
|
+
#
|
|
247
|
+
# foo bar
|
|
248
|
+
# color: red
|
|
249
|
+
#
|
|
250
|
+
# and
|
|
251
|
+
#
|
|
252
|
+
# foo
|
|
253
|
+
# &.bar
|
|
254
|
+
# color: blue
|
|
255
|
+
#
|
|
256
|
+
# becomes
|
|
257
|
+
#
|
|
258
|
+
# foo.bar
|
|
259
|
+
# color: blue
|
|
260
|
+
#
|
|
261
|
+
# @param root [Tree::Node] The parent node
|
|
262
|
+
def flatten_rules(root)
|
|
263
|
+
root.children.each do |child|
|
|
264
|
+
case child
|
|
265
|
+
when Tree::RuleNode
|
|
266
|
+
flatten_rule(child)
|
|
267
|
+
when Tree::DirectiveNode
|
|
268
|
+
flatten_rules(child)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Flattens a single rule.
|
|
274
|
+
#
|
|
275
|
+
# @param rule [Tree::RuleNode] The candidate for flattening
|
|
276
|
+
# @see #flatten_rules
|
|
277
|
+
def flatten_rule(rule)
|
|
278
|
+
while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
|
|
279
|
+
child = rule.children.first
|
|
280
|
+
|
|
281
|
+
if first_simple_sel(child).is_a?(Sass::Selector::Parent)
|
|
282
|
+
rule.parsed_rules = child.parsed_rules.resolve_parent_refs(rule.parsed_rules)
|
|
283
|
+
else
|
|
284
|
+
rule.parsed_rules = make_seq(*(first_seq(rule).members + first_seq(child).members))
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
rule.children = child.children
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
flatten_rules(rule)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def bubble_subject(root)
|
|
294
|
+
root.children.each do |child|
|
|
295
|
+
bubble_subject(child) if child.is_a?(Tree::RuleNode) || child.is_a?(Tree::DirectiveNode)
|
|
296
|
+
next unless child.is_a?(Tree::RuleNode) && !child.children.empty?
|
|
297
|
+
next unless child.children.all? do |c|
|
|
298
|
+
next unless c.is_a?(Tree::RuleNode)
|
|
299
|
+
first_simple_sel(c).is_a?(Sass::Selector::Parent) && first_sseq(c).subject?
|
|
300
|
+
end
|
|
301
|
+
first_sseq(child).subject = true
|
|
302
|
+
child.children.each {|c| first_sseq(c).subject = false}
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Transform
|
|
307
|
+
#
|
|
308
|
+
# foo
|
|
309
|
+
# bar
|
|
310
|
+
# color: blue
|
|
311
|
+
# baz
|
|
312
|
+
# color: blue
|
|
313
|
+
#
|
|
314
|
+
# into
|
|
315
|
+
#
|
|
316
|
+
# foo
|
|
317
|
+
# bar, baz
|
|
318
|
+
# color: blue
|
|
319
|
+
#
|
|
320
|
+
# @param rule [Tree::RuleNode] The candidate for flattening
|
|
321
|
+
def fold_commas(root)
|
|
322
|
+
prev_rule = nil
|
|
323
|
+
root.children.map! do |child|
|
|
324
|
+
unless child.is_a?(Tree::RuleNode)
|
|
325
|
+
fold_commas(child) if child.is_a?(Tree::DirectiveNode)
|
|
326
|
+
next child
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
if prev_rule && prev_rule.children == child.children
|
|
330
|
+
prev_rule.parsed_rules.members << first_seq(child)
|
|
331
|
+
next nil
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
fold_commas(child)
|
|
335
|
+
prev_rule = child
|
|
336
|
+
child
|
|
337
|
+
end
|
|
338
|
+
root.children.compact!
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Dump all the parsed {Sass::Tree::RuleNode} selectors to strings.
|
|
342
|
+
#
|
|
343
|
+
# @param root [Tree::Node] The parent node
|
|
344
|
+
def dump_selectors(root)
|
|
345
|
+
root.children.each do |child|
|
|
346
|
+
next dump_selectors(child) if child.is_a?(Tree::DirectiveNode)
|
|
347
|
+
next unless child.is_a?(Tree::RuleNode)
|
|
348
|
+
child.rule = [child.parsed_rules.to_s]
|
|
349
|
+
dump_selectors(child)
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Create a {Sass::Selector::CommaSequence}.
|
|
354
|
+
#
|
|
355
|
+
# @param seqs [Array<Sass::Selector::Sequence>]
|
|
356
|
+
# @return [Sass::Selector::CommaSequence]
|
|
357
|
+
def make_cseq(*seqs)
|
|
358
|
+
Sass::Selector::CommaSequence.new(seqs)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Create a {Sass::Selector::CommaSequence} containing only a single
|
|
362
|
+
# {Sass::Selector::Sequence}.
|
|
363
|
+
#
|
|
364
|
+
# @param sseqs [Array<Sass::Selector::Sequence, String>]
|
|
365
|
+
# @return [Sass::Selector::CommaSequence]
|
|
366
|
+
def make_seq(*sseqs)
|
|
367
|
+
make_cseq(Sass::Selector::Sequence.new(sseqs))
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Create a {Sass::Selector::CommaSequence} containing only a single
|
|
371
|
+
# {Sass::Selector::Sequence} which in turn contains only a single
|
|
372
|
+
# {Sass::Selector::SimpleSequence}.
|
|
373
|
+
#
|
|
374
|
+
# @param subject [Boolean] Whether this is a subject selector
|
|
375
|
+
# @param sseqs [Array<Sass::Selector::Sequence, String>]
|
|
376
|
+
# @return [Sass::Selector::CommaSequence]
|
|
377
|
+
def make_sseq(subject, *sseqs)
|
|
378
|
+
make_seq(Sass::Selector::SimpleSequence.new(sseqs, subject))
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Return the first {Sass::Selector::Sequence} in a {Sass::Tree::RuleNode}.
|
|
382
|
+
#
|
|
383
|
+
# @param rule [Sass::Tree::RuleNode]
|
|
384
|
+
# @return [Sass::Selector::Sequence]
|
|
385
|
+
def first_seq(rule)
|
|
386
|
+
rule.parsed_rules.members.first
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Return the first {Sass::Selector::SimpleSequence} in a
|
|
390
|
+
# {Sass::Tree::RuleNode}.
|
|
391
|
+
#
|
|
392
|
+
# @param rule [Sass::Tree::RuleNode]
|
|
393
|
+
# @return [Sass::Selector::SimpleSequence, String]
|
|
394
|
+
def first_sseq(rule)
|
|
395
|
+
first_seq(rule).members.first
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Return the first {Sass::Selector::Simple} in a {Sass::Tree::RuleNode},
|
|
399
|
+
# unless the rule begins with a combinator.
|
|
400
|
+
#
|
|
401
|
+
# @param rule [Sass::Tree::RuleNode]
|
|
402
|
+
# @return [Sass::Selector::Simple?]
|
|
403
|
+
def first_simple_sel(rule)
|
|
404
|
+
sseq = first_sseq(rule)
|
|
405
|
+
return unless sseq.is_a?(Sass::Selector::SimpleSequence)
|
|
406
|
+
sseq.members.first
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|