sass 3.2.7 → 3.3.0.rc.1
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.
- data/MIT-LICENSE +2 -2
- data/README.md +14 -2
- data/Rakefile +25 -1
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/lib/sass/cache_stores/base.rb +4 -2
- data/lib/sass/cache_stores/chain.rb +2 -1
- data/lib/sass/cache_stores/filesystem.rb +2 -6
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +1 -0
- data/lib/sass/css.rb +10 -10
- data/lib/sass/engine.rb +403 -150
- data/lib/sass/environment.rb +136 -57
- data/lib/sass/error.rb +7 -7
- data/lib/sass/exec.rb +123 -39
- data/lib/sass/features.rb +41 -0
- data/lib/sass/importers/base.rb +33 -2
- data/lib/sass/importers/deprecated_path.rb +45 -0
- data/lib/sass/importers/filesystem.rb +25 -14
- data/lib/sass/importers.rb +1 -0
- data/lib/sass/logger/base.rb +3 -3
- data/lib/sass/logger/log_level.rb +4 -6
- data/lib/sass/media.rb +19 -19
- data/lib/sass/plugin/compiler.rb +141 -101
- data/lib/sass/plugin/configuration.rb +18 -22
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +24 -8
- data/lib/sass/plugin.rb +4 -2
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script/css_lexer.rb +9 -4
- data/lib/sass/script/css_parser.rb +6 -2
- data/lib/sass/script/functions.rb +1343 -590
- data/lib/sass/script/lexer.rb +84 -52
- data/lib/sass/script/parser.rb +217 -97
- data/lib/sass/script/tree/funcall.rb +290 -0
- data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +34 -13
- data/lib/sass/script/tree/list_literal.rb +80 -0
- data/lib/sass/script/tree/literal.rb +47 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/{node.rb → tree/node.rb} +22 -12
- data/lib/sass/script/{operation.rb → tree/operation.rb} +17 -25
- data/lib/sass/script/tree/selector.rb +30 -0
- data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +5 -4
- data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +14 -9
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +16 -0
- data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +4 -24
- data/lib/sass/script/value/base.rb +248 -0
- data/lib/sass/script/value/bool.rb +36 -0
- data/lib/sass/script/{color.rb → value/color.rb} +239 -195
- data/lib/sass/script/value/helpers.rb +155 -0
- data/lib/sass/script/value/list.rb +119 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +45 -0
- data/lib/sass/script/{number.rb → value/number.rb} +91 -65
- data/lib/sass/script/{string.rb → value/string.rb} +9 -11
- data/lib/sass/script/value.rb +11 -0
- data/lib/sass/script.rb +35 -8
- data/lib/sass/scss/css_parser.rb +2 -1
- data/lib/sass/scss/parser.rb +338 -170
- data/lib/sass/scss/rx.rb +5 -6
- data/lib/sass/scss/script_lexer.rb +1 -0
- data/lib/sass/scss/script_parser.rb +1 -0
- data/lib/sass/scss/static_parser.rb +23 -6
- data/lib/sass/selector/abstract_sequence.rb +2 -2
- data/lib/sass/selector/comma_sequence.rb +21 -16
- data/lib/sass/selector/sequence.rb +60 -34
- data/lib/sass/selector/simple.rb +11 -12
- data/lib/sass/selector/simple_sequence.rb +55 -33
- data/lib/sass/selector.rb +52 -48
- data/lib/sass/source/map.rb +211 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +120 -0
- data/lib/sass/supports.rb +12 -13
- data/lib/sass/tree/at_root_node.rb +82 -0
- data/lib/sass/tree/comment_node.rb +3 -3
- data/lib/sass/tree/css_import_node.rb +11 -11
- data/lib/sass/tree/debug_node.rb +2 -2
- data/lib/sass/tree/directive_node.rb +13 -2
- data/lib/sass/tree/each_node.rb +8 -8
- data/lib/sass/tree/extend_node.rb +13 -6
- data/lib/sass/tree/for_node.rb +4 -4
- data/lib/sass/tree/function_node.rb +5 -4
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +4 -5
- data/lib/sass/tree/media_node.rb +4 -14
- data/lib/sass/tree/mixin_def_node.rb +4 -4
- data/lib/sass/tree/mixin_node.rb +21 -8
- data/lib/sass/tree/node.rb +29 -12
- data/lib/sass/tree/prop_node.rb +38 -18
- data/lib/sass/tree/return_node.rb +3 -2
- data/lib/sass/tree/root_node.rb +19 -3
- data/lib/sass/tree/rule_node.rb +25 -17
- data/lib/sass/tree/supports_node.rb +0 -13
- data/lib/sass/tree/trace_node.rb +2 -1
- data/lib/sass/tree/variable_node.rb +9 -3
- data/lib/sass/tree/visitors/base.rb +6 -6
- data/lib/sass/tree/visitors/check_nesting.rb +12 -9
- data/lib/sass/tree/visitors/convert.rb +63 -38
- data/lib/sass/tree/visitors/cssize.rb +63 -23
- data/lib/sass/tree/visitors/deep_copy.rb +6 -5
- data/lib/sass/tree/visitors/extend.rb +7 -7
- data/lib/sass/tree/visitors/perform.rb +256 -151
- data/lib/sass/tree/visitors/set_options.rb +6 -6
- data/lib/sass/tree/visitors/to_css.rb +231 -81
- data/lib/sass/tree/warn_node.rb +2 -2
- data/lib/sass/tree/while_node.rb +2 -2
- data/lib/sass/util/multibyte_string_scanner.rb +2 -0
- data/lib/sass/util/normalized_map.rb +65 -0
- data/lib/sass/util/ordered_hash.rb +188 -0
- data/lib/sass/util/subset_map.rb +3 -2
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +220 -34
- data/lib/sass/version.rb +9 -9
- data/lib/sass.rb +14 -7
- data/test/sass/compiler_test.rb +213 -0
- data/test/sass/conversion_test.rb +235 -9
- data/test/sass/engine_test.rb +230 -60
- data/test/sass/exec_test.rb +86 -0
- data/test/sass/extend_test.rb +215 -147
- data/test/sass/functions_test.rb +584 -99
- data/test/sass/importer_test.rb +165 -17
- data/test/sass/plugin_test.rb +19 -13
- data/test/sass/script_conversion_test.rb +40 -0
- data/test/sass/script_test.rb +231 -21
- data/test/sass/scss/css_test.rb +14 -5
- data/test/sass/scss/scss_test.rb +1266 -66
- data/test/sass/source_map_test.rb +879 -0
- data/test/sass/templates/bork5.sass +3 -0
- data/test/sass/util/normalized_map_test.rb +30 -0
- data/test/sass/util_test.rb +90 -0
- data/test/sass/value_helpers_test.rb +181 -0
- data/test/test_helper.rb +7 -2
- metadata +316 -291
- data/lib/sass/script/bool.rb +0 -18
- data/lib/sass/script/funcall.rb +0 -231
- data/lib/sass/script/list.rb +0 -84
- data/lib/sass/script/literal.rb +0 -239
- data/lib/sass/script/null.rb +0 -34
- data/lib/sass/script/variable.rb +0 -58
- data/test/Gemfile +0 -3
- data/vendor/listen/CHANGELOG.md +0 -221
- data/vendor/listen/CONTRIBUTING.md +0 -38
- data/vendor/listen/Gemfile +0 -30
- data/vendor/listen/Guardfile +0 -8
- data/vendor/listen/LICENSE +0 -20
- data/vendor/listen/README.md +0 -315
- data/vendor/listen/Rakefile +0 -47
- data/vendor/listen/Vagrantfile +0 -96
- data/vendor/listen/lib/listen/adapter.rb +0 -214
- data/vendor/listen/lib/listen/adapters/bsd.rb +0 -112
- data/vendor/listen/lib/listen/adapters/darwin.rb +0 -85
- data/vendor/listen/lib/listen/adapters/linux.rb +0 -113
- data/vendor/listen/lib/listen/adapters/polling.rb +0 -67
- data/vendor/listen/lib/listen/adapters/windows.rb +0 -87
- data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
- data/vendor/listen/lib/listen/directory_record.rb +0 -371
- data/vendor/listen/lib/listen/listener.rb +0 -225
- data/vendor/listen/lib/listen/multi_listener.rb +0 -143
- data/vendor/listen/lib/listen/turnstile.rb +0 -28
- data/vendor/listen/lib/listen/version.rb +0 -3
- data/vendor/listen/lib/listen.rb +0 -40
- data/vendor/listen/listen.gemspec +0 -22
- data/vendor/listen/spec/listen/adapter_spec.rb +0 -183
- data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
- data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
- data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
- data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
- data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1225
- data/vendor/listen/spec/listen/listener_spec.rb +0 -169
- data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
- data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
- data/vendor/listen/spec/listen_spec.rb +0 -73
- data/vendor/listen/spec/spec_helper.rb +0 -21
- data/vendor/listen/spec/support/adapter_helper.rb +0 -629
- data/vendor/listen/spec/support/directory_record_helper.rb +0 -55
- data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
- data/vendor/listen/spec/support/listeners_helper.rb +0 -156
- data/vendor/listen/spec/support/platform_helper.rb +0 -15
|
@@ -1,103 +1,156 @@
|
|
|
1
1
|
# A visitor for converting a dynamic Sass tree into a static Sass tree.
|
|
2
2
|
class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
class << self
|
|
4
|
+
# @param root [Tree::Node] The root node of the tree to visit.
|
|
5
|
+
# @param environment [Sass::Environment] The lexical environment.
|
|
6
|
+
# @return [Tree::Node] The resulting tree of static nodes.
|
|
7
|
+
def visit(root, environment = nil)
|
|
8
|
+
new(environment).send(:visit, root)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @api private
|
|
12
|
+
# @comment
|
|
13
|
+
# rubocop:disable MethodLength
|
|
14
|
+
def perform_arguments(callable, args, splat)
|
|
15
|
+
desc = "#{callable.type.capitalize} #{callable.name}"
|
|
16
|
+
downcase_desc = "#{callable.type} #{callable.name}"
|
|
17
|
+
|
|
18
|
+
# All keywords are contained in splat.keywords for consistency,
|
|
19
|
+
# even if there were no splats passed in.
|
|
20
|
+
old_keywords_accessed = splat.keywords_accessed
|
|
21
|
+
keywords = splat.keywords
|
|
22
|
+
splat.keywords_accessed = old_keywords_accessed
|
|
23
|
+
|
|
24
|
+
begin
|
|
25
|
+
unless keywords.empty?
|
|
26
|
+
unknown_args = Sass::Util.array_minus(keywords.keys,
|
|
27
|
+
callable.args.map {|var| var.first.underscored_name})
|
|
28
|
+
if callable.splat && unknown_args.include?(callable.splat.underscored_name)
|
|
29
|
+
raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc} " +
|
|
30
|
+
"cannot be used as a named argument.")
|
|
31
|
+
elsif unknown_args.any?
|
|
32
|
+
description = unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'
|
|
33
|
+
raise Sass::SyntaxError.new("#{desc} doesn't have #{description} " +
|
|
34
|
+
"#{unknown_args.map {|name| "$#{name}"}.join ', '}.")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
rescue Sass::SyntaxError => keyword_exception
|
|
38
|
+
end
|
|
9
39
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
downcase_desc = "#{callable.type} #{callable.name}"
|
|
40
|
+
# If there's no splat, raise the keyword exception immediately. The actual
|
|
41
|
+
# raising happens in the ensure clause at the end of this function.
|
|
42
|
+
return if keyword_exception && !callable.splat
|
|
14
43
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
44
|
+
if args.size > callable.args.size && !callable.splat
|
|
45
|
+
takes = callable.args.size
|
|
46
|
+
passed = args.size
|
|
47
|
+
raise Sass::SyntaxError.new(
|
|
48
|
+
"#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
|
|
49
|
+
"but #{passed} #{passed == 1 ? 'was' : 'were'} passed.")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
splat_sep = :comma
|
|
53
|
+
if splat
|
|
54
|
+
args += splat.to_a
|
|
55
|
+
splat_sep = splat.separator
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
keywords = keywords.dup
|
|
59
|
+
env = Sass::Environment.new(callable.environment)
|
|
60
|
+
callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
|
|
61
|
+
if value && keywords.include?(var.underscored_name)
|
|
62
|
+
raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name} " +
|
|
63
|
+
"both by position and by name.")
|
|
24
64
|
end
|
|
65
|
+
|
|
66
|
+
value ||= keywords.delete(var.underscored_name)
|
|
67
|
+
value ||= default && default.perform(env)
|
|
68
|
+
raise Sass::SyntaxError.new("#{desc} is missing argument #{var.inspect}.") unless value
|
|
69
|
+
env.set_local_var(var.name, value)
|
|
25
70
|
end
|
|
26
|
-
rescue Sass::SyntaxError => keyword_exception
|
|
27
|
-
end
|
|
28
71
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
72
|
+
if callable.splat
|
|
73
|
+
rest = args[callable.args.length..-1] || []
|
|
74
|
+
arg_list = Sass::Script::Value::ArgList.new(rest, keywords.dup, splat_sep)
|
|
75
|
+
arg_list.options = env.options
|
|
76
|
+
env.set_local_var(callable.splat.name, arg_list)
|
|
77
|
+
end
|
|
32
78
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
79
|
+
yield env
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
ensure
|
|
82
|
+
# If there's a keyword exception, we don't want to throw it immediately,
|
|
83
|
+
# because the invalid keywords may be part of a glob argument that should be
|
|
84
|
+
# passed on to another function. So we only raise it if we reach the end of
|
|
85
|
+
# this function *and* the keywords attached to the argument list glob object
|
|
86
|
+
# haven't been accessed.
|
|
87
|
+
#
|
|
88
|
+
# The keyword exception takes precedence over any Sass errors, but not over
|
|
89
|
+
# non-Sass exceptions.
|
|
90
|
+
if keyword_exception &&
|
|
91
|
+
!(arg_list && arg_list.keywords_accessed) &&
|
|
92
|
+
(e.nil? || e.is_a?(Sass::SyntaxError))
|
|
93
|
+
raise keyword_exception
|
|
94
|
+
elsif e
|
|
95
|
+
raise e
|
|
96
|
+
end
|
|
39
97
|
end
|
|
40
98
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
99
|
+
# @api private
|
|
100
|
+
# @return [Sass::Script::Value::ArgList]
|
|
101
|
+
def perform_splat(splat, performed_keywords, kwarg_splat, environment)
|
|
102
|
+
args, kwargs, separator = [], Sass::Util.ordered_hash, :comma
|
|
103
|
+
|
|
104
|
+
if splat
|
|
105
|
+
splat = splat.perform(environment)
|
|
106
|
+
separator = splat.separator || separator
|
|
107
|
+
if splat.is_a?(Sass::Script::Value::ArgList)
|
|
108
|
+
args = splat.to_a
|
|
109
|
+
kwargs = splat.keywords
|
|
110
|
+
elsif splat.is_a?(Sass::Script::Value::Map)
|
|
111
|
+
kwargs = arg_hash(splat)
|
|
112
|
+
else
|
|
113
|
+
args = splat.to_a
|
|
114
|
+
end
|
|
115
|
+
end
|
|
49
116
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
117
|
+
kwargs = kwargs.merge(performed_keywords)
|
|
118
|
+
|
|
119
|
+
if kwarg_splat
|
|
120
|
+
kwarg_splat = kwarg_splat.perform(environment)
|
|
121
|
+
unless kwarg_splat.is_a?(Sass::Script::Value::Map)
|
|
122
|
+
raise Sass::SyntaxError.new("Variable keyword arguments must be a map " +
|
|
123
|
+
"(was #{kwarg_splat.inspect}).")
|
|
124
|
+
end
|
|
125
|
+
kwargs = kwargs.merge(arg_hash(kwarg_splat))
|
|
55
126
|
end
|
|
56
127
|
|
|
57
|
-
|
|
58
|
-
value ||= default && default.perform(env)
|
|
59
|
-
raise Sass::SyntaxError.new("#{desc} is missing argument #{var.inspect}.") unless value
|
|
60
|
-
env.set_local_var(var.name, value)
|
|
128
|
+
Sass::Script::Value::ArgList.new(args, kwargs, separator)
|
|
61
129
|
end
|
|
62
130
|
|
|
63
|
-
|
|
64
|
-
rest = args[callable.args.length..-1]
|
|
65
|
-
arg_list = Sass::Script::ArgList.new(rest, keywords.dup, splat_sep)
|
|
66
|
-
arg_list.options = env.options
|
|
67
|
-
env.set_local_var(callable.splat.name, arg_list)
|
|
68
|
-
end
|
|
131
|
+
private
|
|
69
132
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# this function *and* the keywords attached to the argument list glob object
|
|
77
|
-
# haven't been accessed.
|
|
78
|
-
#
|
|
79
|
-
# The keyword exception takes precedence over any Sass errors, but not over
|
|
80
|
-
# non-Sass exceptions.
|
|
81
|
-
if keyword_exception &&
|
|
82
|
-
!(arg_list && arg_list.keywords_accessed) &&
|
|
83
|
-
(e.nil? || e.is_a?(Sass::SyntaxError))
|
|
84
|
-
raise keyword_exception
|
|
85
|
-
elsif e
|
|
86
|
-
raise e
|
|
133
|
+
def arg_hash(map)
|
|
134
|
+
Sass::Util.map_keys(map.to_h) do |key|
|
|
135
|
+
next key.value if key.is_a?(Sass::Script::Value::String)
|
|
136
|
+
raise Sass::SyntaxError.new("Variable keyword argument map must have string keys.\n" +
|
|
137
|
+
"#{key.inspect} is not a string in #{map.inspect}.")
|
|
138
|
+
end
|
|
87
139
|
end
|
|
88
140
|
end
|
|
141
|
+
# @comment
|
|
142
|
+
# rubocop:enable MethodLength
|
|
89
143
|
|
|
90
144
|
protected
|
|
91
145
|
|
|
92
146
|
def initialize(env)
|
|
93
147
|
@environment = env
|
|
94
|
-
# Stack trace information, including mixin includes and imports.
|
|
95
|
-
@stack = []
|
|
96
148
|
end
|
|
97
149
|
|
|
98
150
|
# If an exception is raised, this adds proper metadata to the backtrace.
|
|
99
151
|
def visit(node)
|
|
100
|
-
super(node.dup)
|
|
152
|
+
return super(node.dup) unless @environment
|
|
153
|
+
@environment.stack.with_base(node.filename, node.line) {super(node.dup)}
|
|
101
154
|
rescue Sass::SyntaxError => e
|
|
102
155
|
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
|
103
156
|
raise e
|
|
@@ -142,11 +195,11 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
142
195
|
# Prints the expression to STDERR.
|
|
143
196
|
def visit_debug(node)
|
|
144
197
|
res = node.expr.perform(@environment)
|
|
145
|
-
res = res.value if res.is_a?(Sass::Script::String)
|
|
198
|
+
res = res.value if res.is_a?(Sass::Script::Value::String)
|
|
146
199
|
if node.filename
|
|
147
|
-
|
|
200
|
+
Sass::Util.sass_warn "#{node.filename}:#{node.line} DEBUG: #{res}"
|
|
148
201
|
else
|
|
149
|
-
|
|
202
|
+
Sass::Util.sass_warn "Line #{node.line} DEBUG: #{res}"
|
|
150
203
|
end
|
|
151
204
|
[]
|
|
152
205
|
end
|
|
@@ -156,8 +209,14 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
156
209
|
list = node.list.perform(@environment)
|
|
157
210
|
|
|
158
211
|
with_environment Sass::Environment.new(@environment) do
|
|
159
|
-
list.to_a.map do |
|
|
160
|
-
|
|
212
|
+
list.to_a.map do |value|
|
|
213
|
+
if node.vars.length == 1
|
|
214
|
+
@environment.set_local_var(node.vars.first, value)
|
|
215
|
+
else
|
|
216
|
+
node.vars.zip(value.to_a) do |(var, sub_value)|
|
|
217
|
+
@environment.set_local_var(var, sub_value || Sass::Script::Value::Null.new)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
161
220
|
node.children.map {|c| visit(c)}
|
|
162
221
|
end.flatten
|
|
163
222
|
end
|
|
@@ -166,7 +225,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
166
225
|
# Runs SassScript interpolation in the selector,
|
|
167
226
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
|
168
227
|
def visit_extend(node)
|
|
169
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.selector),
|
|
228
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.selector),
|
|
229
|
+
node.filename, node.options[:importer], node.line)
|
|
170
230
|
node.resolved_selector = parser.parse_selector
|
|
171
231
|
node
|
|
172
232
|
end
|
|
@@ -184,7 +244,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
184
244
|
with_environment Sass::Environment.new(@environment) do
|
|
185
245
|
range.map do |i|
|
|
186
246
|
@environment.set_local_var(node.var,
|
|
187
|
-
Sass::Script::Number.new(i, from.numerator_units, from.denominator_units))
|
|
247
|
+
Sass::Script::Value::Number.new(i, from.numerator_units, from.denominator_units))
|
|
188
248
|
node.children.map {|c| visit(c)}
|
|
189
249
|
end.flatten
|
|
190
250
|
end
|
|
@@ -194,7 +254,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
194
254
|
def visit_function(node)
|
|
195
255
|
env = Sass::Environment.new(@environment, node.options)
|
|
196
256
|
@environment.set_local_function(node.name,
|
|
197
|
-
Sass::Callable.new(node.name, node.args, node.splat, env,
|
|
257
|
+
Sass::Callable.new(node.name, node.args, node.splat, env,
|
|
258
|
+
node.children, !:has_content, "function"))
|
|
198
259
|
[]
|
|
199
260
|
end
|
|
200
261
|
|
|
@@ -214,59 +275,67 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
214
275
|
# Returns a static DirectiveNode if this is importing a CSS file,
|
|
215
276
|
# or parses and includes the imported Sass file.
|
|
216
277
|
def visit_import(node)
|
|
217
|
-
if path = node.css_import?
|
|
218
|
-
|
|
278
|
+
if (path = node.css_import?)
|
|
279
|
+
resolved_node = Sass::Tree::CssImportNode.resolved("url(#{path})")
|
|
280
|
+
resolved_node.source_range = node.source_range
|
|
281
|
+
return resolved_node
|
|
219
282
|
end
|
|
220
283
|
file = node.imported_file
|
|
221
|
-
|
|
284
|
+
if @environment.stack.frames.any? {|f| f.is_import? && f.filename == file.options[:filename]}
|
|
285
|
+
handle_import_loop!(node)
|
|
286
|
+
end
|
|
222
287
|
|
|
223
288
|
begin
|
|
224
|
-
@stack.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
289
|
+
@environment.stack.with_import(node.filename, node.line) do
|
|
290
|
+
root = file.to_tree
|
|
291
|
+
Sass::Tree::Visitors::CheckNesting.visit(root)
|
|
292
|
+
node.children = root.children.map {|c| visit(c)}.flatten
|
|
293
|
+
node
|
|
294
|
+
end
|
|
229
295
|
rescue Sass::SyntaxError => e
|
|
230
296
|
e.modify_backtrace(:filename => node.imported_file.options[:filename])
|
|
231
297
|
e.add_backtrace(:filename => node.filename, :line => node.line)
|
|
232
298
|
raise e
|
|
233
299
|
end
|
|
234
|
-
ensure
|
|
235
|
-
@stack.pop unless path
|
|
236
300
|
end
|
|
237
301
|
|
|
238
302
|
# Loads a mixin into the environment.
|
|
239
303
|
def visit_mixindef(node)
|
|
240
304
|
env = Sass::Environment.new(@environment, node.options)
|
|
241
305
|
@environment.set_local_mixin(node.name,
|
|
242
|
-
Sass::Callable.new(node.name, node.args, node.splat, env,
|
|
306
|
+
Sass::Callable.new(node.name, node.args, node.splat, env,
|
|
307
|
+
node.children, node.has_content, "mixin"))
|
|
243
308
|
[]
|
|
244
309
|
end
|
|
245
310
|
|
|
246
311
|
# Runs a mixin.
|
|
247
312
|
def visit_mixin(node)
|
|
248
313
|
include_loop = true
|
|
249
|
-
|
|
314
|
+
if @environment.stack.frames.any? {|f| f.is_mixin? && f.name == node.name}
|
|
315
|
+
handle_include_loop!(node)
|
|
316
|
+
end
|
|
250
317
|
include_loop = false
|
|
251
318
|
|
|
252
|
-
@stack.
|
|
253
|
-
|
|
319
|
+
@environment.stack.with_mixin(node.filename, node.line, node.name) do
|
|
320
|
+
mixin = @environment.mixin(node.name)
|
|
321
|
+
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin
|
|
254
322
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
323
|
+
if node.children.any? && !mixin.has_content
|
|
324
|
+
raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
|
|
325
|
+
end
|
|
258
326
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
327
|
+
args = node.args.map {|a| a.perform(@environment)}
|
|
328
|
+
keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
|
|
329
|
+
splat = self.class.perform_splat(node.splat, keywords, node.kwarg_splat, @environment)
|
|
262
330
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
331
|
+
self.class.perform_arguments(mixin, args, splat) do |env|
|
|
332
|
+
env.caller = Sass::Environment.new(@environment)
|
|
333
|
+
env.content = [node.children, @environment] if node.has_children
|
|
266
334
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
335
|
+
trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
|
|
336
|
+
with_environment(env) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
|
|
337
|
+
trace_node
|
|
338
|
+
end
|
|
270
339
|
end
|
|
271
340
|
rescue Sass::SyntaxError => e
|
|
272
341
|
unless include_loop
|
|
@@ -274,22 +343,24 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
274
343
|
e.add_backtrace(:line => node.line)
|
|
275
344
|
end
|
|
276
345
|
raise e
|
|
277
|
-
ensure
|
|
278
|
-
@stack.pop unless include_loop
|
|
279
346
|
end
|
|
280
347
|
|
|
281
348
|
def visit_content(node)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
349
|
+
content, content_env = @environment.content
|
|
350
|
+
return [] unless content
|
|
351
|
+
@environment.stack.with_mixin(node.filename, node.line, '@content') do
|
|
352
|
+
trace_node = Sass::Tree::TraceNode.from_node('@content', node)
|
|
353
|
+
content_env = Sass::Environment.new(content_env)
|
|
354
|
+
content_env.caller = Sass::Environment.new(@environment)
|
|
355
|
+
with_environment(content_env) do
|
|
356
|
+
trace_node.children = content.map {|c| visit(c.dup)}.flatten
|
|
357
|
+
end
|
|
358
|
+
trace_node
|
|
359
|
+
end
|
|
287
360
|
rescue Sass::SyntaxError => e
|
|
288
361
|
e.modify_backtrace(:mixin => '@content', :line => node.line)
|
|
289
362
|
e.add_backtrace(:line => node.line)
|
|
290
363
|
raise e
|
|
291
|
-
ensure
|
|
292
|
-
@stack.pop if content
|
|
293
364
|
end
|
|
294
365
|
|
|
295
366
|
# Runs any SassScript that may be embedded in a property.
|
|
@@ -297,6 +368,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
297
368
|
node.resolved_name = run_interp(node.name)
|
|
298
369
|
val = node.value.perform(@environment)
|
|
299
370
|
node.resolved_value = val.to_s
|
|
371
|
+
node.value_source_range = val.source_range if val.source_range
|
|
300
372
|
yield
|
|
301
373
|
end
|
|
302
374
|
|
|
@@ -308,38 +380,78 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
308
380
|
# Runs SassScript interpolation in the selector,
|
|
309
381
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
|
310
382
|
def visit_rule(node)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
383
|
+
old_at_root_without_rule, @at_root_without_rule = @at_root_without_rule, false
|
|
384
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
|
|
385
|
+
node.filename, node.options[:importer], node.line)
|
|
314
386
|
node.parsed_rules ||= parser.parse_selector
|
|
315
|
-
|
|
316
|
-
@
|
|
317
|
-
|
|
318
|
-
|
|
387
|
+
node.resolved_rules = node.parsed_rules.resolve_parent_refs(
|
|
388
|
+
@environment.selector, !old_at_root_without_rule)
|
|
389
|
+
node.stack_trace = @environment.stack.to_s if node.options[:trace_selectors]
|
|
390
|
+
with_environment Sass::Environment.new(@environment, node.options) do
|
|
391
|
+
@environment.selector = node.resolved_rules
|
|
392
|
+
node.children = node.children.map {|c| visit(c)}.flatten
|
|
393
|
+
end
|
|
394
|
+
node
|
|
395
|
+
ensure
|
|
396
|
+
@at_root_without_rule = old_at_root_without_rule
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Sets a variable that indicates that the first level of rule nodes
|
|
400
|
+
# shouldn't include the parent selector by default.
|
|
401
|
+
def visit_atroot(node)
|
|
402
|
+
if node.query
|
|
403
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
|
404
|
+
node.filename, node.options[:importer], node.line)
|
|
405
|
+
node.resolved_type, node.resolved_value = parser.parse_static_at_root_query
|
|
406
|
+
else
|
|
407
|
+
node.resolved_type, node.resolved_value = :without, ['rule']
|
|
319
408
|
end
|
|
409
|
+
|
|
410
|
+
old_at_root_without_rule = @at_root_without_rule
|
|
411
|
+
@at_root_without_rule = true if node.exclude?('rule')
|
|
320
412
|
yield
|
|
413
|
+
ensure
|
|
414
|
+
@at_root_without_rule = old_at_root_without_rule
|
|
321
415
|
end
|
|
322
416
|
|
|
323
417
|
# Loads the new variable value into the environment.
|
|
324
418
|
def visit_variable(node)
|
|
325
|
-
|
|
419
|
+
env = @environment
|
|
420
|
+
if node.global
|
|
421
|
+
env = env.global_env
|
|
422
|
+
elsif env.parent && env.is_var_global?(node.name)
|
|
423
|
+
var_expr = "$#{node.name}: #{node.expr.to_sass(env.options)} !global"
|
|
424
|
+
var_expr << " !default" if node.guarded
|
|
425
|
+
location = "on line #{node.line}"
|
|
426
|
+
location << " of #{node.filename}" if node.filename
|
|
427
|
+
Sass::Util.sass_warn <<WARNING
|
|
428
|
+
DEPRECATION WARNING #{location}:
|
|
429
|
+
Assigning to global variable "$#{node.name}" by default is deprecated.
|
|
430
|
+
In future versions of Sass, this will create a new local variable.
|
|
431
|
+
If you want to assign to the global variable, use "#{var_expr}" instead.
|
|
432
|
+
WARNING
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
var = env.var(node.name)
|
|
326
436
|
return [] if node.guarded && var && !var.null?
|
|
327
437
|
val = node.expr.perform(@environment)
|
|
328
|
-
|
|
438
|
+
if node.expr.source_range
|
|
439
|
+
val.source_range = node.expr.source_range
|
|
440
|
+
else
|
|
441
|
+
val.source_range = node.source_range
|
|
442
|
+
end
|
|
443
|
+
env.set_var(node.name, val)
|
|
329
444
|
[]
|
|
330
445
|
end
|
|
331
446
|
|
|
332
447
|
# Prints the expression to STDERR with a stylesheet trace.
|
|
333
448
|
def visit_warn(node)
|
|
334
|
-
@stack.push(:filename => node.filename, :line => node.line)
|
|
335
449
|
res = node.expr.perform(@environment)
|
|
336
|
-
res = res.value if res.is_a?(Sass::Script::String)
|
|
450
|
+
res = res.value if res.is_a?(Sass::Script::Value::String)
|
|
337
451
|
msg = "WARNING: #{res}\n "
|
|
338
|
-
msg <<
|
|
452
|
+
msg << @environment.stack.to_s.gsub("\n", "\n ") << "\n"
|
|
339
453
|
Sass::Util.sass_warn msg
|
|
340
454
|
[]
|
|
341
|
-
ensure
|
|
342
|
-
@stack.pop
|
|
343
455
|
end
|
|
344
456
|
|
|
345
457
|
# Runs the child nodes until the continuation expression becomes false.
|
|
@@ -353,11 +465,15 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
353
465
|
|
|
354
466
|
def visit_directive(node)
|
|
355
467
|
node.resolved_value = run_interp(node.value)
|
|
356
|
-
|
|
468
|
+
with_environment Sass::Environment.new(@environment) do
|
|
469
|
+
node.children = node.children.map {|c| visit(c)}.flatten
|
|
470
|
+
node
|
|
471
|
+
end
|
|
357
472
|
end
|
|
358
473
|
|
|
359
474
|
def visit_media(node)
|
|
360
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
|
475
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
|
476
|
+
node.filename, node.options[:importer], node.line)
|
|
361
477
|
node.resolved_query ||= parser.parse_media_query_list
|
|
362
478
|
yield
|
|
363
479
|
end
|
|
@@ -371,7 +487,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
371
487
|
def visit_cssimport(node)
|
|
372
488
|
node.resolved_uri = run_interp([node.uri])
|
|
373
489
|
if node.query
|
|
374
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
|
490
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
|
491
|
+
node.filename, node.options[:importer], node.line)
|
|
375
492
|
node.resolved_query ||= parser.parse_media_query_list
|
|
376
493
|
end
|
|
377
494
|
yield
|
|
@@ -379,25 +496,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
379
496
|
|
|
380
497
|
private
|
|
381
498
|
|
|
382
|
-
def stack_trace
|
|
383
|
-
trace = []
|
|
384
|
-
stack = @stack.map {|e| e.dup}.reverse
|
|
385
|
-
stack.each_cons(2) {|(e1, e2)| e1[:caller] = e2[:name]; [e1, e2]}
|
|
386
|
-
stack.each_with_index do |entry, i|
|
|
387
|
-
msg = "#{i == 0 ? "on" : "from"} line #{entry[:line]}"
|
|
388
|
-
msg << " of #{entry[:filename] || "an unknown file"}"
|
|
389
|
-
msg << ", in `#{entry[:caller]}'" if entry[:caller]
|
|
390
|
-
trace << msg
|
|
391
|
-
end
|
|
392
|
-
trace
|
|
393
|
-
end
|
|
394
|
-
|
|
395
499
|
def run_interp_no_strip(text)
|
|
396
500
|
text.map do |r|
|
|
397
501
|
next r if r.is_a?(String)
|
|
398
502
|
val = r.perform(@environment)
|
|
399
503
|
# Interpolated strings should never render with quotes
|
|
400
|
-
next val.value if val.is_a?(Sass::Script::String)
|
|
504
|
+
next val.value if val.is_a?(Sass::Script::Value::String)
|
|
401
505
|
val.to_s
|
|
402
506
|
end.join
|
|
403
507
|
end
|
|
@@ -409,8 +513,9 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
409
513
|
def handle_include_loop!(node)
|
|
410
514
|
msg = "An @include loop has been found:"
|
|
411
515
|
content_count = 0
|
|
412
|
-
mixins = @stack.
|
|
413
|
-
|
|
516
|
+
mixins = @environment.stack.frames.select {|f| f.is_mixin?}.reverse!.map! {|f| f.name}
|
|
517
|
+
mixins = mixins.select do |name|
|
|
518
|
+
if name == '@content'
|
|
414
519
|
content_count += 1
|
|
415
520
|
false
|
|
416
521
|
elsif content_count > 0
|
|
@@ -421,7 +526,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
421
526
|
end
|
|
422
527
|
end
|
|
423
528
|
|
|
424
|
-
return
|
|
529
|
+
return unless mixins.include?(node.name)
|
|
425
530
|
raise Sass::SyntaxError.new("#{msg} #{node.name} includes itself") if mixins.size == 1
|
|
426
531
|
|
|
427
532
|
msg << "\n" << Sass::Util.enum_cons(mixins.reverse + [node.name], 2).map do |m1, m2|
|
|
@@ -432,7 +537,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
|
432
537
|
|
|
433
538
|
def handle_import_loop!(node)
|
|
434
539
|
msg = "An @import loop has been found:"
|
|
435
|
-
files = @stack.map {|
|
|
540
|
+
files = @environment.stack.frames.select {|f| f.is_import?}.map {|f| f.filename}.compact
|
|
436
541
|
if node.filename == node.imported_file.options[:filename]
|
|
437
542
|
raise Sass::SyntaxError.new("#{msg} #{node.filename} imports itself")
|
|
438
543
|
end
|
|
@@ -26,7 +26,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def visit_extend(node)
|
|
29
|
-
node.selector.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
|
|
29
|
+
node.selector.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
|
30
30
|
yield
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -73,7 +73,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def visit_prop(node)
|
|
76
|
-
node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
|
|
76
|
+
node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
|
77
77
|
node.value.options = @options
|
|
78
78
|
yield
|
|
79
79
|
end
|
|
@@ -84,7 +84,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def visit_rule(node)
|
|
87
|
-
node.rule.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
|
|
87
|
+
node.rule.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
|
88
88
|
yield
|
|
89
89
|
end
|
|
90
90
|
|
|
@@ -104,17 +104,17 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def visit_directive(node)
|
|
107
|
-
node.value.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
|
|
107
|
+
node.value.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
|
108
108
|
yield
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
def visit_media(node)
|
|
112
|
-
node.query.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
|
|
112
|
+
node.query.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
|
113
113
|
yield
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
def visit_cssimport(node)
|
|
117
|
-
node.query.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)} if node.query
|
|
117
|
+
node.query.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)} if node.query
|
|
118
118
|
yield
|
|
119
119
|
end
|
|
120
120
|
|