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
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# A visitor for converting a Sass tree into a source string.
|
|
2
|
+
class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
3
|
+
# Runs the visitor on a tree.
|
|
4
|
+
#
|
|
5
|
+
# @param root [Tree::Node] The root node of the Sass tree.
|
|
6
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
7
|
+
# @param format [Symbol] `:sass` or `:scss`.
|
|
8
|
+
# @return [String] The Sass or SCSS source for the tree.
|
|
9
|
+
def self.visit(root, options, format)
|
|
10
|
+
new(options, format).send(:visit, root)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
protected
|
|
14
|
+
|
|
15
|
+
def initialize(options, format)
|
|
16
|
+
@options = options
|
|
17
|
+
@format = format
|
|
18
|
+
@tabs = 0
|
|
19
|
+
# 2 spaces by default
|
|
20
|
+
@tab_chars = @options[:indent] || " "
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def visit_children(parent)
|
|
24
|
+
@tabs += 1
|
|
25
|
+
return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
|
|
26
|
+
(@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : "\n#{ @tab_chars * (@tabs-1)}}\n")
|
|
27
|
+
ensure
|
|
28
|
+
@tabs -= 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Ensures proper spacing between top-level nodes.
|
|
32
|
+
def visit_root(node)
|
|
33
|
+
Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
|
|
34
|
+
visit(child) +
|
|
35
|
+
if nxt &&
|
|
36
|
+
(child.is_a?(Sass::Tree::CommentNode) &&
|
|
37
|
+
child.line + child.lines + 1 == nxt.line) ||
|
|
38
|
+
(child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
|
|
39
|
+
child.line + 1 == nxt.line) ||
|
|
40
|
+
(child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
|
|
41
|
+
child.line + 1 == nxt.line)
|
|
42
|
+
""
|
|
43
|
+
else
|
|
44
|
+
"\n"
|
|
45
|
+
end
|
|
46
|
+
end.join.rstrip + "\n"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def visit_charset(node)
|
|
50
|
+
"#{tab_str}@charset \"#{node.name}\"#{semi}\n"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def visit_comment(node)
|
|
54
|
+
value = interp_to_src(node.value)
|
|
55
|
+
content = if @format == :sass
|
|
56
|
+
content = value.gsub(/\*\/$/, '').rstrip
|
|
57
|
+
if content =~ /\A[ \t]/
|
|
58
|
+
# Re-indent SCSS comments like this:
|
|
59
|
+
# /* foo
|
|
60
|
+
# bar
|
|
61
|
+
# baz */
|
|
62
|
+
content.gsub!(/^/, ' ')
|
|
63
|
+
content.sub!(/\A([ \t]*)\/\*/, '/*\1')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
content =
|
|
67
|
+
unless content.include?("\n")
|
|
68
|
+
content
|
|
69
|
+
else
|
|
70
|
+
content.gsub!(/\n( \*|\/\/)/, "\n ")
|
|
71
|
+
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
|
|
72
|
+
sep = node.type == :silent ? "\n//" : "\n *"
|
|
73
|
+
if spaces >= 2
|
|
74
|
+
content.gsub(/\n /, sep)
|
|
75
|
+
else
|
|
76
|
+
content.gsub(/\n#{' ' * spaces}/, sep)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
content.gsub!(/\A\/\*/, '//') if node.type == :silent
|
|
81
|
+
content.gsub!(/^/, tab_str)
|
|
82
|
+
content.rstrip + "\n"
|
|
83
|
+
else
|
|
84
|
+
spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
|
|
85
|
+
content = if node.type == :silent
|
|
86
|
+
value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
|
|
87
|
+
else
|
|
88
|
+
value
|
|
89
|
+
end.gsub(/^/, spaces) + "\n"
|
|
90
|
+
content
|
|
91
|
+
end
|
|
92
|
+
content
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def visit_debug(node)
|
|
96
|
+
"#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def visit_directive(node)
|
|
100
|
+
res = "#{tab_str}#{interp_to_src(node.value)}"
|
|
101
|
+
res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2');
|
|
102
|
+
return res + "#{semi}\n" unless node.has_children
|
|
103
|
+
res + yield + "\n"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def visit_each(node)
|
|
107
|
+
"#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def visit_extend(node)
|
|
111
|
+
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}#{" !optional" if node.optional?}\n"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def visit_for(node)
|
|
115
|
+
"#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
|
|
116
|
+
"#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def visit_function(node)
|
|
120
|
+
args = node.args.map do |v, d|
|
|
121
|
+
d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
|
|
122
|
+
end.join(", ")
|
|
123
|
+
if node.splat
|
|
124
|
+
args << ", " unless node.args.empty?
|
|
125
|
+
args << node.splat.to_sass(@options) << "..."
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
"#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def visit_if(node)
|
|
132
|
+
name =
|
|
133
|
+
if !@is_else; "if"
|
|
134
|
+
elsif node.expr; "else if"
|
|
135
|
+
else; "else"
|
|
136
|
+
end
|
|
137
|
+
@is_else = false
|
|
138
|
+
str = "#{tab_str}@#{name}"
|
|
139
|
+
str << " #{node.expr.to_sass(@options)}" if node.expr
|
|
140
|
+
str << yield
|
|
141
|
+
@is_else = true
|
|
142
|
+
str << visit(node.else) if node.else
|
|
143
|
+
str
|
|
144
|
+
ensure
|
|
145
|
+
@is_else = false
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def visit_import(node)
|
|
149
|
+
quote = @format == :scss ? '"' : ''
|
|
150
|
+
"#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def visit_media(node)
|
|
154
|
+
"#{tab_str}@media #{media_interp_to_src(node.query)}#{yield}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def visit_supports(node)
|
|
158
|
+
"#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def visit_cssimport(node)
|
|
162
|
+
if node.uri.is_a?(Sass::Script::Node)
|
|
163
|
+
str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
|
|
164
|
+
else
|
|
165
|
+
str = "#{tab_str}@import #{node.uri}"
|
|
166
|
+
end
|
|
167
|
+
str << " #{interp_to_src(node.query)}" unless node.query.empty?
|
|
168
|
+
"#{str}#{semi}\n"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def visit_mixindef(node)
|
|
172
|
+
args =
|
|
173
|
+
if node.args.empty? && node.splat.nil?
|
|
174
|
+
""
|
|
175
|
+
else
|
|
176
|
+
str = '('
|
|
177
|
+
str << node.args.map do |v, d|
|
|
178
|
+
if d
|
|
179
|
+
"#{v.to_sass(@options)}: #{d.to_sass(@options)}"
|
|
180
|
+
else
|
|
181
|
+
v.to_sass(@options)
|
|
182
|
+
end
|
|
183
|
+
end.join(", ")
|
|
184
|
+
|
|
185
|
+
if node.splat
|
|
186
|
+
str << ", " unless node.args.empty?
|
|
187
|
+
str << node.splat.to_sass(@options) << '...'
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
str << ')'
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
"#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def visit_mixin(node)
|
|
197
|
+
arg_to_sass = lambda do |arg|
|
|
198
|
+
sass = arg.to_sass(@options)
|
|
199
|
+
sass = "(#{sass})" if arg.is_a?(Sass::Script::List) && arg.separator == :comma
|
|
200
|
+
sass
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
unless node.args.empty? && node.keywords.empty? && node.splat.nil?
|
|
204
|
+
args = node.args.map(&arg_to_sass).join(", ")
|
|
205
|
+
keywords = Sass::Util.hash_to_a(node.keywords).
|
|
206
|
+
map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}.join(', ')
|
|
207
|
+
if node.splat
|
|
208
|
+
splat = (args.empty? && keywords.empty?) ? "" : ", "
|
|
209
|
+
splat = "#{splat}#{arg_to_sass[node.splat]}..."
|
|
210
|
+
end
|
|
211
|
+
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
|
|
212
|
+
end
|
|
213
|
+
"#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def visit_content(node)
|
|
217
|
+
"#{tab_str}@content#{semi}\n"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def visit_prop(node)
|
|
221
|
+
res = tab_str + node.declaration(@options, @format)
|
|
222
|
+
return res + semi + "\n" if node.children.empty?
|
|
223
|
+
res + yield.rstrip + semi + "\n"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def visit_return(node)
|
|
227
|
+
"#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def visit_rule(node)
|
|
231
|
+
if @format == :sass
|
|
232
|
+
name = selector_to_sass(node.rule)
|
|
233
|
+
name = "\\" + name if name[0] == ?:
|
|
234
|
+
name.gsub(/^/, tab_str) + yield
|
|
235
|
+
elsif @format == :scss
|
|
236
|
+
name = selector_to_scss(node.rule)
|
|
237
|
+
res = name + yield
|
|
238
|
+
if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent
|
|
239
|
+
res.slice!(-3..-1)
|
|
240
|
+
res << "\n" << tab_str << "}\n"
|
|
241
|
+
end
|
|
242
|
+
res
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def visit_variable(node)
|
|
247
|
+
"#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def visit_warn(node)
|
|
251
|
+
"#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def visit_while(node)
|
|
255
|
+
"#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
private
|
|
259
|
+
|
|
260
|
+
def interp_to_src(interp)
|
|
261
|
+
interp.map do |r|
|
|
262
|
+
next r if r.is_a?(String)
|
|
263
|
+
"\#{#{r.to_sass(@options)}}"
|
|
264
|
+
end.join
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Like interp_to_src, but removes the unnecessary `#{}` around the keys and
|
|
268
|
+
# values in media expressions.
|
|
269
|
+
def media_interp_to_src(interp)
|
|
270
|
+
Sass::Util.enum_with_index(interp).map do |r, i|
|
|
271
|
+
next r if r.is_a?(String)
|
|
272
|
+
before, after = interp[i-1], interp[i+1]
|
|
273
|
+
if before.is_a?(String) && after.is_a?(String) &&
|
|
274
|
+
((before[-1] == ?( && after[0] == ?:) ||
|
|
275
|
+
(before =~ /:\s*/ && after[0] == ?)))
|
|
276
|
+
r.to_sass(@options)
|
|
277
|
+
else
|
|
278
|
+
"\#{#{r.to_sass(@options)}}"
|
|
279
|
+
end
|
|
280
|
+
end.join
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def selector_to_src(sel)
|
|
284
|
+
@format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def selector_to_sass(sel)
|
|
288
|
+
sel.map do |r|
|
|
289
|
+
if r.is_a?(String)
|
|
290
|
+
r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
|
|
291
|
+
else
|
|
292
|
+
"\#{#{r.to_sass(@options)}}"
|
|
293
|
+
end
|
|
294
|
+
end.join
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def selector_to_scss(sel)
|
|
298
|
+
interp_to_src(sel).gsub(/^[ \t]*/, tab_str).gsub(/[ \t]*$/, '')
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def semi
|
|
302
|
+
@format == :sass ? "" : ";"
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def tab_str
|
|
306
|
+
@tab_chars * @tabs
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def dasherize(s)
|
|
310
|
+
if @options[:dasherize]
|
|
311
|
+
s.gsub('_', '-')
|
|
312
|
+
else
|
|
313
|
+
s
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# A visitor for converting a static Sass tree into a static CSS tree.
|
|
2
|
+
class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
3
|
+
# @param root [Tree::Node] The root node of the tree to visit.
|
|
4
|
+
# @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
|
|
5
|
+
# *and* the extensions defined for this tree
|
|
6
|
+
def self.visit(root); super; end
|
|
7
|
+
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
# Returns the immediate parent of the current node.
|
|
11
|
+
# @return [Tree::Node]
|
|
12
|
+
attr_reader :parent
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@parent_directives = []
|
|
16
|
+
@extends = Sass::Util::SubsetMap.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# If an exception is raised, this adds proper metadata to the backtrace.
|
|
20
|
+
def visit(node)
|
|
21
|
+
super(node)
|
|
22
|
+
rescue Sass::SyntaxError => e
|
|
23
|
+
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
|
24
|
+
raise e
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Keeps track of the current parent node.
|
|
28
|
+
def visit_children(parent)
|
|
29
|
+
with_parent parent do
|
|
30
|
+
parent.children = super.flatten
|
|
31
|
+
parent
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
MERGEABLE_DIRECTIVES = [Sass::Tree::MediaNode]
|
|
36
|
+
|
|
37
|
+
# Runs a block of code with the current parent node
|
|
38
|
+
# replaced with the given node.
|
|
39
|
+
#
|
|
40
|
+
# @param parent [Tree::Node] The new parent for the duration of the block.
|
|
41
|
+
# @yield A block in which the parent is set to `parent`.
|
|
42
|
+
# @return [Object] The return value of the block.
|
|
43
|
+
def with_parent(parent)
|
|
44
|
+
if parent.is_a?(Sass::Tree::DirectiveNode)
|
|
45
|
+
if MERGEABLE_DIRECTIVES.any? {|klass| parent.is_a?(klass)}
|
|
46
|
+
old_parent_directive = @parent_directives.pop
|
|
47
|
+
end
|
|
48
|
+
@parent_directives.push parent
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
old_parent, @parent = @parent, parent
|
|
52
|
+
yield
|
|
53
|
+
ensure
|
|
54
|
+
@parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode)
|
|
55
|
+
@parent_directives.push old_parent_directive if old_parent_directive
|
|
56
|
+
@parent = old_parent
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# In Ruby 1.8, ensures that there's only one `@charset` directive
|
|
60
|
+
# and that it's at the top of the document.
|
|
61
|
+
#
|
|
62
|
+
# @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
|
|
63
|
+
# *and* the extensions defined for this tree
|
|
64
|
+
def visit_root(node)
|
|
65
|
+
yield
|
|
66
|
+
|
|
67
|
+
if parent.nil?
|
|
68
|
+
# In Ruby 1.9 we can make all @charset nodes invisible
|
|
69
|
+
# and infer the final @charset from the encoding of the final string.
|
|
70
|
+
if Sass::Util.ruby1_8?
|
|
71
|
+
charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
|
|
72
|
+
node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
|
|
73
|
+
node.children.unshift charset if charset
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
imports_to_move = []
|
|
77
|
+
import_limit = nil
|
|
78
|
+
i = -1
|
|
79
|
+
node.children.reject! do |n|
|
|
80
|
+
i += 1
|
|
81
|
+
if import_limit
|
|
82
|
+
next false unless n.is_a?(Sass::Tree::CssImportNode)
|
|
83
|
+
imports_to_move << n
|
|
84
|
+
next true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if !n.is_a?(Sass::Tree::CommentNode) &&
|
|
88
|
+
!n.is_a?(Sass::Tree::CharsetNode) &&
|
|
89
|
+
!n.is_a?(Sass::Tree::CssImportNode)
|
|
90
|
+
import_limit = i
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if import_limit
|
|
97
|
+
node.children = node.children[0...import_limit] + imports_to_move +
|
|
98
|
+
node.children[import_limit..-1]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
return node, @extends
|
|
103
|
+
rescue Sass::SyntaxError => e
|
|
104
|
+
e.sass_template ||= node.template
|
|
105
|
+
raise e
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# A simple struct wrapping up information about a single `@extend` instance. A
|
|
109
|
+
# single [ExtendNode] can have multiple Extends if either the parent node or
|
|
110
|
+
# the extended selector is a comma sequence.
|
|
111
|
+
#
|
|
112
|
+
# @attr extender [Sass::Selector::Sequence]
|
|
113
|
+
# The selector of the CSS rule containing the `@extend`.
|
|
114
|
+
# @attr target [Array<Sass::Selector::Simple>] The selector being `@extend`ed.
|
|
115
|
+
# @attr node [Sass::Tree::ExtendNode] The node that produced this extend.
|
|
116
|
+
# @attr directives [Array<Sass::Tree::DirectiveNode>]
|
|
117
|
+
# The directives containing the `@extend`.
|
|
118
|
+
# @attr result [Symbol]
|
|
119
|
+
# The result of this extend. One of `:not_found` (the target doesn't exist
|
|
120
|
+
# in the document), `:failed_to_unify` (the target exists but cannot be
|
|
121
|
+
# unified with the extender), or `:succeeded`.
|
|
122
|
+
Extend = Struct.new(:extender, :target, :node, :directives, :result)
|
|
123
|
+
|
|
124
|
+
# Registers an extension in the `@extends` subset map.
|
|
125
|
+
def visit_extend(node)
|
|
126
|
+
node.resolved_selector.members.each do |seq|
|
|
127
|
+
if seq.members.size > 1
|
|
128
|
+
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
sseq = seq.members.first
|
|
132
|
+
if !sseq.is_a?(Sass::Selector::SimpleSequence)
|
|
133
|
+
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
|
|
134
|
+
elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
|
|
135
|
+
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend parent selectors")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
sel = sseq.members
|
|
139
|
+
parent.resolved_rules.members.each do |member|
|
|
140
|
+
if !member.members.last.is_a?(Sass::Selector::SimpleSequence)
|
|
141
|
+
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
@extends[sel] = Extend.new(member, sel, node, @parent_directives.dup, :not_found)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
[]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Modifies exception backtraces to include the imported file.
|
|
152
|
+
def visit_import(node)
|
|
153
|
+
# Don't use #visit_children to avoid adding the import node to the list of parents.
|
|
154
|
+
node.children.map {|c| visit(c)}.flatten
|
|
155
|
+
rescue Sass::SyntaxError => e
|
|
156
|
+
e.modify_backtrace(:filename => node.children.first.filename)
|
|
157
|
+
e.add_backtrace(:filename => node.filename, :line => node.line)
|
|
158
|
+
raise e
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Bubbles the `@media` directive up through RuleNodes
|
|
162
|
+
# and merges it with other `@media` directives.
|
|
163
|
+
def visit_media(node)
|
|
164
|
+
yield unless bubble(node)
|
|
165
|
+
media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
|
|
166
|
+
node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
|
|
167
|
+
media = media.select {|n| n.resolved_query = n.resolved_query.merge(node.resolved_query)}
|
|
168
|
+
(node.children.empty? ? [] : [node]) + media
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Bubbles the `@supports` directive up through RuleNodes.
|
|
172
|
+
def visit_supports(node)
|
|
173
|
+
yield unless bubble(node)
|
|
174
|
+
node
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Asserts that all the traced children are valid in their new location.
|
|
178
|
+
def visit_trace(node)
|
|
179
|
+
# Don't use #visit_children to avoid adding the trace node to the list of parents.
|
|
180
|
+
node.children.map {|c| visit(c)}.flatten
|
|
181
|
+
rescue Sass::SyntaxError => e
|
|
182
|
+
e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
|
|
183
|
+
e.add_backtrace(:filename => node.filename, :line => node.line)
|
|
184
|
+
raise e
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Converts nested properties into flat properties
|
|
188
|
+
# and updates the indentation of the prop node based on the nesting level.
|
|
189
|
+
def visit_prop(node)
|
|
190
|
+
if parent.is_a?(Sass::Tree::PropNode)
|
|
191
|
+
node.resolved_name = "#{parent.resolved_name}-#{node.resolved_name}"
|
|
192
|
+
node.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if node.style == :nested
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
yield
|
|
196
|
+
|
|
197
|
+
result = node.children.dup
|
|
198
|
+
if !node.resolved_value.empty? || node.children.empty?
|
|
199
|
+
node.send(:check!)
|
|
200
|
+
result.unshift(node)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
result
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Resolves parent references and nested selectors,
|
|
207
|
+
# and updates the indentation of the rule node based on the nesting level.
|
|
208
|
+
def visit_rule(node)
|
|
209
|
+
parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
|
|
210
|
+
# It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
|
|
211
|
+
node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)
|
|
212
|
+
|
|
213
|
+
yield
|
|
214
|
+
|
|
215
|
+
rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles?}
|
|
216
|
+
props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles? || c.invisible?}
|
|
217
|
+
|
|
218
|
+
unless props.empty?
|
|
219
|
+
node.children = props
|
|
220
|
+
rules.each {|r| r.tabs += 1} if node.style == :nested
|
|
221
|
+
rules.unshift(node)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
rules.last.group_end = true unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty?
|
|
225
|
+
|
|
226
|
+
rules
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
private
|
|
230
|
+
|
|
231
|
+
def bubble(node)
|
|
232
|
+
return unless parent.is_a?(Sass::Tree::RuleNode)
|
|
233
|
+
new_rule = parent.dup
|
|
234
|
+
new_rule.children = node.children
|
|
235
|
+
node.children = with_parent(node) {Array(visit(new_rule))}
|
|
236
|
+
# If the last child is actually the end of the group,
|
|
237
|
+
# the parent's cssize will set it properly
|
|
238
|
+
node.children.last.group_end = false unless node.children.empty?
|
|
239
|
+
true
|
|
240
|
+
end
|
|
241
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# A visitor for copying the full structure of a Sass tree.
|
|
2
|
+
class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
|
|
3
|
+
protected
|
|
4
|
+
|
|
5
|
+
def visit(node)
|
|
6
|
+
super(node.dup)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def visit_children(parent)
|
|
10
|
+
parent.children = parent.children.map {|c| visit(c)}
|
|
11
|
+
parent
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def visit_debug(node)
|
|
15
|
+
node.expr = node.expr.deep_copy
|
|
16
|
+
yield
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def visit_each(node)
|
|
20
|
+
node.list = node.list.deep_copy
|
|
21
|
+
yield
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def visit_extend(node)
|
|
25
|
+
node.selector = node.selector.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
|
26
|
+
yield
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def visit_for(node)
|
|
30
|
+
node.from = node.from.deep_copy
|
|
31
|
+
node.to = node.to.deep_copy
|
|
32
|
+
yield
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def visit_function(node)
|
|
36
|
+
node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
|
|
37
|
+
yield
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def visit_if(node)
|
|
41
|
+
node.expr = node.expr.deep_copy if node.expr
|
|
42
|
+
node.else = visit(node.else) if node.else
|
|
43
|
+
yield
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def visit_mixindef(node)
|
|
47
|
+
node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
|
|
48
|
+
yield
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def visit_mixin(node)
|
|
52
|
+
node.args = node.args.map {|a| a.deep_copy}
|
|
53
|
+
node.keywords = Hash[node.keywords.map {|k, v| [k, v.deep_copy]}]
|
|
54
|
+
yield
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def visit_prop(node)
|
|
58
|
+
node.name = node.name.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
|
59
|
+
node.value = node.value.deep_copy
|
|
60
|
+
yield
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def visit_return(node)
|
|
64
|
+
node.expr = node.expr.deep_copy
|
|
65
|
+
yield
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def visit_rule(node)
|
|
69
|
+
node.rule = node.rule.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
|
70
|
+
yield
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def visit_variable(node)
|
|
74
|
+
node.expr = node.expr.deep_copy
|
|
75
|
+
yield
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def visit_warn(node)
|
|
79
|
+
node.expr = node.expr.deep_copy
|
|
80
|
+
yield
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def visit_while(node)
|
|
84
|
+
node.expr = node.expr.deep_copy
|
|
85
|
+
yield
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def visit_directive(node)
|
|
89
|
+
node.value = node.value.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
|
90
|
+
yield
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def visit_media(node)
|
|
94
|
+
node.query = node.query.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
|
95
|
+
yield
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def visit_supports(node)
|
|
99
|
+
node.condition = node.condition.deep_copy
|
|
100
|
+
yield
|
|
101
|
+
end
|
|
102
|
+
end
|