sass 3.1.0 → 3.3.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/CONTRIBUTING +1 -1
- data/MIT-LICENSE +2 -2
- data/README.md +29 -17
- data/Rakefile +43 -9
- data/VERSION +1 -1
- data/VERSION_DATE +1 -0
- data/VERSION_NAME +1 -1
- data/bin/sass +6 -1
- data/bin/sass-convert +6 -1
- data/bin/scss +6 -1
- data/ext/mkrf_conf.rb +27 -0
- data/lib/sass/cache_stores/base.rb +7 -3
- data/lib/sass/cache_stores/chain.rb +3 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -7
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +2 -1
- data/lib/sass/css.rb +168 -53
- data/lib/sass/engine.rb +502 -174
- data/lib/sass/environment.rb +151 -111
- data/lib/sass/error.rb +7 -7
- data/lib/sass/exec.rb +176 -60
- data/lib/sass/features.rb +40 -0
- data/lib/sass/importers/base.rb +46 -7
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +113 -30
- data/lib/sass/importers.rb +1 -0
- data/lib/sass/logger/base.rb +30 -0
- data/lib/sass/logger/log_level.rb +45 -0
- data/lib/sass/logger.rb +12 -0
- data/lib/sass/media.rb +213 -0
- data/lib/sass/plugin/compiler.rb +194 -104
- data/lib/sass/plugin/configuration.rb +18 -25
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +37 -11
- data/lib/sass/plugin.rb +10 -13
- data/lib/sass/railtie.rb +2 -1
- data/lib/sass/repl.rb +5 -6
- data/lib/sass/script/css_lexer.rb +8 -4
- data/lib/sass/script/css_parser.rb +5 -2
- data/lib/sass/script/functions.rb +1547 -618
- data/lib/sass/script/lexer.rb +122 -72
- data/lib/sass/script/parser.rb +304 -135
- data/lib/sass/script/tree/funcall.rb +306 -0
- data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
- data/lib/sass/script/tree/list_literal.rb +77 -0
- data/lib/sass/script/tree/literal.rb +45 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
- data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
- data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
- data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +15 -0
- data/lib/sass/script/value/arg_list.rb +36 -0
- data/lib/sass/script/value/base.rb +238 -0
- data/lib/sass/script/value/bool.rb +40 -0
- data/lib/sass/script/{color.rb → value/color.rb} +256 -74
- data/lib/sass/script/value/deprecated_false.rb +55 -0
- data/lib/sass/script/value/helpers.rb +155 -0
- data/lib/sass/script/value/list.rb +128 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +49 -0
- data/lib/sass/script/{number.rb → value/number.rb} +115 -62
- data/lib/sass/script/{string.rb → value/string.rb} +9 -11
- data/lib/sass/script/value.rb +12 -0
- data/lib/sass/script.rb +35 -9
- data/lib/sass/scss/css_parser.rb +2 -12
- data/lib/sass/scss/parser.rb +657 -230
- data/lib/sass/scss/rx.rb +17 -12
- data/lib/sass/scss/static_parser.rb +37 -6
- data/lib/sass/scss.rb +0 -1
- data/lib/sass/selector/abstract_sequence.rb +35 -3
- data/lib/sass/selector/comma_sequence.rb +29 -14
- data/lib/sass/selector/sequence.rb +371 -74
- data/lib/sass/selector/simple.rb +28 -13
- data/lib/sass/selector/simple_sequence.rb +163 -36
- data/lib/sass/selector.rb +138 -36
- data/lib/sass/shared.rb +3 -5
- data/lib/sass/source/map.rb +196 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +126 -0
- data/lib/sass/supports.rb +228 -0
- data/lib/sass/tree/at_root_node.rb +82 -0
- data/lib/sass/tree/comment_node.rb +34 -29
- 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 +3 -3
- data/lib/sass/tree/directive_node.rb +33 -3
- data/lib/sass/tree/each_node.rb +9 -9
- data/lib/sass/tree/extend_node.rb +20 -6
- data/lib/sass/tree/for_node.rb +6 -6
- data/lib/sass/tree/function_node.rb +12 -4
- data/lib/sass/tree/if_node.rb +2 -15
- data/lib/sass/tree/import_node.rb +11 -5
- data/lib/sass/tree/media_node.rb +27 -11
- data/lib/sass/tree/mixin_def_node.rb +15 -4
- data/lib/sass/tree/mixin_node.rb +27 -7
- data/lib/sass/tree/node.rb +69 -35
- data/lib/sass/tree/prop_node.rb +47 -31
- data/lib/sass/tree/return_node.rb +4 -3
- data/lib/sass/tree/root_node.rb +20 -4
- data/lib/sass/tree/rule_node.rb +37 -26
- data/lib/sass/tree/supports_node.rb +38 -0
- data/lib/sass/tree/trace_node.rb +33 -0
- data/lib/sass/tree/variable_node.rb +10 -4
- data/lib/sass/tree/visitors/base.rb +5 -8
- data/lib/sass/tree/visitors/check_nesting.rb +67 -52
- data/lib/sass/tree/visitors/convert.rb +134 -53
- data/lib/sass/tree/visitors/cssize.rb +245 -51
- 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 +331 -105
- data/lib/sass/tree/visitors/set_options.rb +125 -0
- data/lib/sass/tree/visitors/to_css.rb +259 -95
- data/lib/sass/tree/warn_node.rb +3 -3
- data/lib/sass/tree/while_node.rb +3 -3
- data/lib/sass/util/cross_platform_random.rb +19 -0
- data/lib/sass/util/multibyte_string_scanner.rb +157 -0
- data/lib/sass/util/normalized_map.rb +130 -0
- data/lib/sass/util/ordered_hash.rb +192 -0
- data/lib/sass/util/subset_map.rb +11 -2
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +565 -39
- data/lib/sass/version.rb +27 -15
- data/lib/sass.rb +39 -4
- data/test/sass/cache_test.rb +15 -0
- data/test/sass/compiler_test.rb +223 -0
- data/test/sass/conversion_test.rb +901 -107
- data/test/sass/css2sass_test.rb +94 -0
- data/test/sass/engine_test.rb +1059 -164
- data/test/sass/exec_test.rb +86 -0
- data/test/sass/extend_test.rb +933 -837
- 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 +995 -136
- data/test/sass/importer_test.rb +338 -18
- data/test/sass/logger_test.rb +58 -0
- data/test/sass/more_results/more_import.css +2 -2
- data/test/sass/plugin_test.rb +114 -30
- data/test/sass/results/cached_import_option.css +3 -0
- data/test/sass/results/filename_fn.css +3 -0
- data/test/sass/results/import.css +2 -2
- data/test/sass/results/import_charset.css +1 -0
- data/test/sass/results/import_charset_1_8.css +1 -0
- data/test/sass/results/import_charset_ibm866.css +1 -0
- data/test/sass/results/import_content.css +1 -0
- data/test/sass/results/script.css +1 -1
- data/test/sass/results/scss_import.css +2 -2
- data/test/sass/results/units.css +2 -2
- data/test/sass/script_conversion_test.rb +43 -1
- data/test/sass/script_test.rb +380 -36
- data/test/sass/scss/css_test.rb +257 -75
- data/test/sass/scss/scss_test.rb +2322 -110
- data/test/sass/source_map_test.rb +887 -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_content.sass +3 -0
- data/test/sass/templates/_same_name_different_partiality.scss +1 -0
- data/test/sass/templates/bork5.sass +3 -0
- data/test/sass/templates/cached_import_option.scss +3 -0
- data/test/sass/templates/double_import_loop1.sass +1 -0
- data/test/sass/templates/filename_fn.scss +18 -0
- data/test/sass/templates/import_charset.sass +2 -0
- data/test/sass/templates/import_charset_1_8.sass +2 -0
- data/test/sass/templates/import_charset_ibm866.sass +2 -0
- data/test/sass/templates/import_content.sass +4 -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/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/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
- data/test/sass/util/normalized_map_test.rb +51 -0
- data/test/sass/util_test.rb +183 -0
- data/test/sass/value_helpers_test.rb +181 -0
- data/test/test_helper.rb +45 -5
- data/vendor/listen/CHANGELOG.md +228 -0
- data/vendor/listen/CONTRIBUTING.md +38 -0
- data/vendor/listen/Gemfile +30 -0
- data/vendor/listen/Guardfile +8 -0
- data/vendor/{fssm → listen}/LICENSE +1 -1
- data/vendor/listen/README.md +315 -0
- data/vendor/listen/Rakefile +47 -0
- data/vendor/listen/Vagrantfile +96 -0
- data/vendor/listen/lib/listen/adapter.rb +214 -0
- data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
- data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
- data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
- data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
- data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
- data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
- data/vendor/listen/lib/listen/directory_record.rb +371 -0
- data/vendor/listen/lib/listen/listener.rb +225 -0
- data/vendor/listen/lib/listen/multi_listener.rb +143 -0
- data/vendor/listen/lib/listen/turnstile.rb +28 -0
- data/vendor/listen/lib/listen/version.rb +3 -0
- data/vendor/listen/lib/listen.rb +40 -0
- data/vendor/listen/listen.gemspec +22 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
- data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
- data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
- data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
- data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
- data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
- data/vendor/listen/spec/listen/listener_spec.rb +169 -0
- data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
- data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
- data/vendor/listen/spec/listen_spec.rb +73 -0
- data/vendor/listen/spec/spec_helper.rb +21 -0
- data/vendor/listen/spec/support/adapter_helper.rb +629 -0
- data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
- data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
- data/vendor/listen/spec/support/listeners_helper.rb +156 -0
- data/vendor/listen/spec/support/platform_helper.rb +15 -0
- metadata +344 -271
- data/lib/sass/less.rb +0 -382
- data/lib/sass/script/bool.rb +0 -18
- data/lib/sass/script/funcall.rb +0 -162
- data/lib/sass/script/list.rb +0 -76
- data/lib/sass/script/literal.rb +0 -245
- data/lib/sass/script/variable.rb +0 -54
- data/lib/sass/scss/sass_parser.rb +0 -11
- data/test/sass/less_conversion_test.rb +0 -653
- data/vendor/fssm/README.markdown +0 -55
- data/vendor/fssm/Rakefile +0 -59
- data/vendor/fssm/VERSION.yml +0 -5
- data/vendor/fssm/example.rb +0 -9
- data/vendor/fssm/fssm.gemspec +0 -77
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
- data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
- data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
- data/vendor/fssm/lib/fssm/monitor.rb +0 -26
- data/vendor/fssm/lib/fssm/path.rb +0 -91
- data/vendor/fssm/lib/fssm/pathname.rb +0 -502
- data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
- data/vendor/fssm/lib/fssm/state/file.rb +0 -24
- data/vendor/fssm/lib/fssm/support.rb +0 -63
- data/vendor/fssm/lib/fssm/tree.rb +0 -176
- data/vendor/fssm/lib/fssm.rb +0 -33
- data/vendor/fssm/profile/prof-cache.rb +0 -40
- data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
- data/vendor/fssm/profile/prof-pathname.rb +0 -68
- data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
- data/vendor/fssm/profile/prof.html +0 -2379
- data/vendor/fssm/spec/path_spec.rb +0 -75
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -9,15 +9,18 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
9
9
|
|
10
10
|
# Returns the immediate parent of the current node.
|
11
11
|
# @return [Tree::Node]
|
12
|
-
|
12
|
+
def parent
|
13
|
+
@parents.last
|
14
|
+
end
|
13
15
|
|
14
16
|
def initialize
|
17
|
+
@parents = []
|
15
18
|
@extends = Sass::Util::SubsetMap.new
|
16
19
|
end
|
17
20
|
|
18
21
|
# If an exception is raised, this adds proper metadata to the backtrace.
|
19
22
|
def visit(node)
|
20
|
-
super(node
|
23
|
+
super(node)
|
21
24
|
rescue Sass::SyntaxError => e
|
22
25
|
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
23
26
|
raise e
|
@@ -26,11 +29,20 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
26
29
|
# Keeps track of the current parent node.
|
27
30
|
def visit_children(parent)
|
28
31
|
with_parent parent do
|
29
|
-
parent.children =
|
32
|
+
parent.children = visit_children_without_parent(parent)
|
30
33
|
parent
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
37
|
+
# Like {#visit\_children}, but doesn't set {#parent}.
|
38
|
+
#
|
39
|
+
# @param node [Sass::Tree::Node]
|
40
|
+
# @return [Array<Sass::Tree::Node>] the flattened results of
|
41
|
+
# visiting all the children of `node`
|
42
|
+
def visit_children_without_parent(node)
|
43
|
+
node.children.map {|c| visit(c)}.flatten
|
44
|
+
end
|
45
|
+
|
34
46
|
# Runs a block of code with the current parent node
|
35
47
|
# replaced with the given node.
|
36
48
|
#
|
@@ -38,10 +50,10 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
38
50
|
# @yield A block in which the parent is set to `parent`.
|
39
51
|
# @return [Object] The return value of the block.
|
40
52
|
def with_parent(parent)
|
41
|
-
|
53
|
+
@parents.push parent
|
42
54
|
yield
|
43
55
|
ensure
|
44
|
-
@
|
56
|
+
@parents.pop
|
45
57
|
end
|
46
58
|
|
47
59
|
# In Ruby 1.8, ensures that there's only one `@charset` directive
|
@@ -52,12 +64,39 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
52
64
|
def visit_root(node)
|
53
65
|
yield
|
54
66
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
61
100
|
end
|
62
101
|
|
63
102
|
return node, @extends
|
@@ -66,6 +105,22 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
66
105
|
raise e
|
67
106
|
end
|
68
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
|
+
|
69
124
|
# Registers an extension in the `@extends` subset map.
|
70
125
|
def visit_extend(node)
|
71
126
|
node.resolved_selector.members.each do |seq|
|
@@ -76,15 +131,18 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
76
131
|
sseq = seq.members.first
|
77
132
|
if !sseq.is_a?(Sass::Selector::SimpleSequence)
|
78
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")
|
79
136
|
end
|
80
137
|
|
81
138
|
sel = sseq.members
|
82
|
-
parent.resolved_rules.members.each do |
|
83
|
-
|
84
|
-
raise Sass::SyntaxError.new("#{
|
139
|
+
parent.resolved_rules.members.each do |member|
|
140
|
+
unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
|
141
|
+
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
|
85
142
|
end
|
86
143
|
|
87
|
-
|
144
|
+
parent_directives = @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)}
|
145
|
+
@extends[sel] = Extend.new(member, sel, node, parent_directives, :not_found)
|
88
146
|
end
|
89
147
|
end
|
90
148
|
|
@@ -93,38 +151,16 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
93
151
|
|
94
152
|
# Modifies exception backtraces to include the imported file.
|
95
153
|
def visit_import(node)
|
96
|
-
|
97
|
-
node.children.map {|c| visit(c)}.flatten
|
154
|
+
visit_children_without_parent(node)
|
98
155
|
rescue Sass::SyntaxError => e
|
99
156
|
e.modify_backtrace(:filename => node.children.first.filename)
|
100
157
|
e.add_backtrace(:filename => node.filename, :line => node.line)
|
101
158
|
raise e
|
102
159
|
end
|
103
160
|
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
if parent.is_a?(Sass::Tree::RuleNode)
|
108
|
-
new_rule = parent.dup
|
109
|
-
new_rule.children = node.children
|
110
|
-
node.children = with_parent(node) {Array(visit(new_rule))}
|
111
|
-
# If the last child is actually the end of the group,
|
112
|
-
# the parent's cssize will set it properly
|
113
|
-
node.children.last.group_end = false unless node.children.empty?
|
114
|
-
else
|
115
|
-
yield
|
116
|
-
end
|
117
|
-
|
118
|
-
media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
|
119
|
-
node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
|
120
|
-
media.each {|n| n.query = "#{node.query} and #{n.query}"}
|
121
|
-
(node.children.empty? ? [] : [node]) + media
|
122
|
-
end
|
123
|
-
|
124
|
-
# Asserts that all the mixin's children are valid in their new location.
|
125
|
-
def visit_mixin(node)
|
126
|
-
# Don't use #visit_children to avoid adding the mixin node to the list of parents.
|
127
|
-
node.children.map {|c| visit(c)}.flatten
|
161
|
+
# Asserts that all the traced children are valid in their new location.
|
162
|
+
def visit_trace(node)
|
163
|
+
visit_children_without_parent(node)
|
128
164
|
rescue Sass::SyntaxError => e
|
129
165
|
e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
|
130
166
|
e.add_backtrace(:filename => node.filename, :line => node.line)
|
@@ -150,17 +186,38 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
150
186
|
result
|
151
187
|
end
|
152
188
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
189
|
+
def visit_atroot(node)
|
190
|
+
# If there aren't any more directives or rules that this @at-root needs to
|
191
|
+
# exclude, we can get rid of it and just evaluate the children.
|
192
|
+
if @parents.none? {|n| node.exclude_node?(n)}
|
193
|
+
results = visit_children_without_parent(node)
|
194
|
+
results.each {|c| c.tabs += node.tabs if bubblable?(c)}
|
195
|
+
if !results.empty? && bubblable?(results.last)
|
196
|
+
results.last.group_end = node.group_end
|
197
|
+
end
|
198
|
+
return results
|
199
|
+
end
|
200
|
+
|
201
|
+
# If this @at-root excludes the immediate parent, return it as-is so that it
|
202
|
+
# can be bubbled up by the parent node.
|
203
|
+
return Bubble.new(node) if node.exclude_node?(parent)
|
204
|
+
|
205
|
+
# Otherwise, duplicate the current parent and move it into the @at-root
|
206
|
+
# node. As above, returning an @at-root node signals to the parent directive
|
207
|
+
# that it should be bubbled upwards.
|
208
|
+
bubble(node)
|
209
|
+
end
|
210
|
+
|
211
|
+
# The following directives are visible and have children. This means they need
|
212
|
+
# to be able to handle bubbling up nodes such as @at-root and @media.
|
159
213
|
|
214
|
+
# Updates the indentation of the rule node based on the nesting
|
215
|
+
# level. The selectors were resolved in {Perform}.
|
216
|
+
def visit_rule(node)
|
160
217
|
yield
|
161
218
|
|
162
|
-
rules = node.children.select {|c|
|
163
|
-
props = node.children.reject {|c|
|
219
|
+
rules = node.children.select {|c| bubblable?(c)}
|
220
|
+
props = node.children.reject {|c| bubblable?(c) || c.invisible?}
|
164
221
|
|
165
222
|
unless props.empty?
|
166
223
|
node.children = props
|
@@ -168,8 +225,145 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
168
225
|
rules.unshift(node)
|
169
226
|
end
|
170
227
|
|
171
|
-
rules
|
172
|
-
|
228
|
+
rules = debubble(rules)
|
229
|
+
unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty? || !bubblable?(rules.last)
|
230
|
+
rules.last.group_end = true
|
231
|
+
end
|
173
232
|
rules
|
174
233
|
end
|
234
|
+
|
235
|
+
# Bubbles a directive up through RuleNodes.
|
236
|
+
def visit_directive(node)
|
237
|
+
return node unless node.has_children
|
238
|
+
return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
|
239
|
+
|
240
|
+
yield
|
241
|
+
|
242
|
+
# Since we don't know if the mere presence of an unknown directive may be
|
243
|
+
# important, we should keep an empty version around even if all the contents
|
244
|
+
# are removed via @at-root. However, if the contents are just bubbled out,
|
245
|
+
# we don't need to do so.
|
246
|
+
directive_exists = node.children.any? do |child|
|
247
|
+
next true unless child.is_a?(Bubble)
|
248
|
+
next false unless child.node.is_a?(Sass::Tree::DirectiveNode)
|
249
|
+
child.node.resolved_value == node.resolved_value
|
250
|
+
end
|
251
|
+
|
252
|
+
if directive_exists
|
253
|
+
[]
|
254
|
+
else
|
255
|
+
empty_node = node.dup
|
256
|
+
empty_node.children = []
|
257
|
+
[empty_node]
|
258
|
+
end + debubble(node.children, node)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Bubbles the `@media` directive up through RuleNodes
|
262
|
+
# and merges it with other `@media` directives.
|
263
|
+
def visit_media(node)
|
264
|
+
return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
|
265
|
+
return Bubble.new(node) if parent.is_a?(Sass::Tree::MediaNode)
|
266
|
+
|
267
|
+
yield
|
268
|
+
|
269
|
+
debubble(node.children, node) do |child|
|
270
|
+
next child unless child.is_a?(Sass::Tree::MediaNode)
|
271
|
+
# Copies of `node` can be bubbled, and we don't want to merge it with its
|
272
|
+
# own query.
|
273
|
+
next child if child.resolved_query == node.resolved_query
|
274
|
+
next child if child.resolved_query = child.resolved_query.merge(node.resolved_query)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Bubbles the `@supports` directive up through RuleNodes.
|
279
|
+
def visit_supports(node)
|
280
|
+
return node unless node.has_children
|
281
|
+
return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
|
282
|
+
|
283
|
+
yield
|
284
|
+
|
285
|
+
debubble(node.children, node)
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
# "Bubbles" `node` one level by copying the parent and wrapping `node`'s
|
291
|
+
# children with it.
|
292
|
+
#
|
293
|
+
# @param node [Sass::Tree::Node].
|
294
|
+
# @return [Bubble]
|
295
|
+
def bubble(node)
|
296
|
+
new_rule = parent.dup
|
297
|
+
new_rule.children = node.children
|
298
|
+
node.children = [new_rule]
|
299
|
+
Bubble.new(node)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Pops all bubbles in `children` and intersperses the results with the other
|
303
|
+
# values.
|
304
|
+
#
|
305
|
+
# If `parent` is passed, it's copied and used as the parent node for the
|
306
|
+
# nested portions of `children`.
|
307
|
+
#
|
308
|
+
# @param children [List<Sass::Tree::Node, Bubble>]
|
309
|
+
# @param parent [Sass::Tree::Node]
|
310
|
+
# @yield [node] An optional block for processing bubbled nodes. Each bubbled
|
311
|
+
# node will be passed to this block.
|
312
|
+
# @yieldparam node [Sass::Tree::Node] A bubbled node.
|
313
|
+
# @yieldreturn [Sass::Tree::Node?] A node to use in place of the bubbled node.
|
314
|
+
# This can be the node itself, or `nil` to indicate that the node should be
|
315
|
+
# omitted.
|
316
|
+
# @return [List<Sass::Tree::Node, Bubble>]
|
317
|
+
def debubble(children, parent = nil)
|
318
|
+
Sass::Util.slice_by(children) {|c| c.is_a?(Bubble)}.map do |(is_bubble, slice)|
|
319
|
+
unless is_bubble
|
320
|
+
next slice unless parent
|
321
|
+
new_parent = parent.dup
|
322
|
+
new_parent.children = slice
|
323
|
+
next new_parent
|
324
|
+
end
|
325
|
+
|
326
|
+
next slice.map do |bubble|
|
327
|
+
next unless (node = block_given? ? yield(bubble.node) : bubble.node)
|
328
|
+
node.tabs += bubble.tabs
|
329
|
+
node.group_end = bubble.group_end
|
330
|
+
[visit(node)].flatten
|
331
|
+
end.compact
|
332
|
+
end.flatten
|
333
|
+
end
|
334
|
+
|
335
|
+
# Returns whether or not a node can be bubbled up through the syntax tree.
|
336
|
+
#
|
337
|
+
# @param node [Sass::Tree::Node]
|
338
|
+
# @return [Boolean]
|
339
|
+
def bubblable?(node)
|
340
|
+
node.is_a?(Sass::Tree::RuleNode) || node.bubbles?
|
341
|
+
end
|
342
|
+
|
343
|
+
# A wrapper class for a node that indicates to the parent that it should
|
344
|
+
# treat the wrapped node as a sibling rather than a child.
|
345
|
+
#
|
346
|
+
# Nodes should be wrapped before they're passed to \{Cssize.visit}. They will
|
347
|
+
# be automatically visited upon calling \{#pop}.
|
348
|
+
#
|
349
|
+
# This duck types as a [Sass::Tree::Node] for the purposes of
|
350
|
+
# tree-manipulation operations.
|
351
|
+
class Bubble
|
352
|
+
attr_accessor :node
|
353
|
+
attr_accessor :tabs
|
354
|
+
attr_accessor :group_end
|
355
|
+
|
356
|
+
def initialize(node)
|
357
|
+
@node = node
|
358
|
+
@tabs = 0
|
359
|
+
end
|
360
|
+
|
361
|
+
def bubbles?
|
362
|
+
true
|
363
|
+
end
|
364
|
+
|
365
|
+
def inspect
|
366
|
+
"(Bubble #{node.inspect})"
|
367
|
+
end
|
368
|
+
end
|
175
369
|
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::Tree::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::Tree::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::Tree::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::Tree::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::Tree::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
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# A visitor for performing selector inheritance on a static CSS tree.
|
2
|
+
#
|
3
|
+
# Destructively modifies the tree.
|
4
|
+
class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base
|
5
|
+
# Performs the given extensions on the static CSS tree based in `root`, then
|
6
|
+
# validates that all extends matched some selector.
|
7
|
+
#
|
8
|
+
# @param root [Tree::Node] The root node of the tree to visit.
|
9
|
+
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
|
10
|
+
# Sass::Tree::Visitors::Cssize::Extend}]
|
11
|
+
# The extensions to perform on this tree.
|
12
|
+
# @return [Object] The return value of \{#visit} for the root node.
|
13
|
+
def self.visit(root, extends)
|
14
|
+
return if extends.empty?
|
15
|
+
new(extends).send(:visit, root)
|
16
|
+
check_extends_fired! extends
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def initialize(extends)
|
22
|
+
@parent_directives = []
|
23
|
+
@extends = extends
|
24
|
+
end
|
25
|
+
|
26
|
+
# If an exception is raised, this adds proper metadata to the backtrace.
|
27
|
+
def visit(node)
|
28
|
+
super(node)
|
29
|
+
rescue Sass::SyntaxError => e
|
30
|
+
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
31
|
+
raise e
|
32
|
+
end
|
33
|
+
|
34
|
+
# Keeps track of the current parent directives.
|
35
|
+
def visit_children(parent)
|
36
|
+
@parent_directives.push parent if parent.is_a?(Sass::Tree::DirectiveNode)
|
37
|
+
super
|
38
|
+
ensure
|
39
|
+
@parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Applies the extend to a single rule's selector.
|
43
|
+
def visit_rule(node)
|
44
|
+
node.resolved_rules = node.resolved_rules.do_extend(@extends, @parent_directives)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def self.check_extends_fired!(extends)
|
50
|
+
extends.each_value do |ex|
|
51
|
+
next if ex.result == :succeeded || ex.node.optional?
|
52
|
+
message = "\"#{ex.extender}\" failed to @extend \"#{ex.target.join}\"."
|
53
|
+
reason =
|
54
|
+
if ex.result == :not_found
|
55
|
+
"The selector \"#{ex.target.join}\" was not found."
|
56
|
+
else
|
57
|
+
"No selectors matching \"#{ex.target.join}\" could be unified with \"#{ex.extender}\"."
|
58
|
+
end
|
59
|
+
|
60
|
+
# TODO(nweiz): this should use the Sass stack trace of the extend node.
|
61
|
+
raise Sass::SyntaxError.new(<<MESSAGE, :filename => ex.node.filename, :line => ex.node.line)
|
62
|
+
#{message}
|
63
|
+
#{reason}
|
64
|
+
Use "@extend #{ex.target.join} !optional" if the extend should be able to fail.
|
65
|
+
MESSAGE
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|