oreorenasass 3.4.4 → 3.4.5
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 +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +50 -70
- data/Rakefile +5 -26
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass.rb +12 -19
- data/lib/sass/cache_stores/base.rb +2 -2
- data/lib/sass/cache_stores/chain.rb +1 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -1
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +0 -1
- data/lib/sass/css.rb +13 -11
- data/lib/sass/engine.rb +173 -424
- data/lib/sass/environment.rb +58 -148
- data/lib/sass/error.rb +14 -11
- data/lib/sass/exec.rb +703 -5
- data/lib/sass/importers/base.rb +6 -49
- data/lib/sass/importers/filesystem.rb +19 -44
- data/lib/sass/logger.rb +4 -1
- data/lib/sass/logger/base.rb +4 -2
- data/lib/sass/logger/log_level.rb +7 -3
- data/lib/sass/media.rb +23 -20
- data/lib/sass/plugin.rb +7 -7
- data/lib/sass/plugin/compiler.rb +145 -304
- data/lib/sass/plugin/configuration.rb +23 -18
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script.rb +8 -35
- data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +606 -0
- data/lib/sass/script/css_lexer.rb +4 -8
- data/lib/sass/script/css_parser.rb +2 -5
- data/lib/sass/script/funcall.rb +245 -0
- data/lib/sass/script/functions.rb +408 -1491
- data/lib/sass/script/interpolation.rb +79 -0
- data/lib/sass/script/lexer.rb +68 -172
- data/lib/sass/script/list.rb +85 -0
- data/lib/sass/script/literal.rb +221 -0
- data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
- data/lib/sass/script/{value/null.rb → null.rb} +7 -14
- data/lib/sass/script/{value/number.rb → number.rb} +75 -152
- data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
- data/lib/sass/script/parser.rb +110 -245
- data/lib/sass/script/string.rb +51 -0
- data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
- data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
- data/lib/sass/script/variable.rb +58 -0
- data/lib/sass/scss/css_parser.rb +3 -9
- data/lib/sass/scss/parser.rb +421 -450
- data/lib/sass/scss/rx.rb +11 -19
- data/lib/sass/scss/static_parser.rb +7 -321
- data/lib/sass/selector.rb +194 -68
- data/lib/sass/selector/abstract_sequence.rb +14 -29
- data/lib/sass/selector/comma_sequence.rb +25 -108
- data/lib/sass/selector/sequence.rb +66 -159
- data/lib/sass/selector/simple.rb +25 -23
- data/lib/sass/selector/simple_sequence.rb +63 -173
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/supports.rb +15 -13
- data/lib/sass/tree/charset_node.rb +1 -1
- 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 +4 -21
- data/lib/sass/tree/each_node.rb +8 -8
- data/lib/sass/tree/extend_node.rb +7 -14
- data/lib/sass/tree/for_node.rb +4 -4
- data/lib/sass/tree/function_node.rb +4 -9
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +5 -4
- data/lib/sass/tree/media_node.rb +14 -4
- data/lib/sass/tree/mixin_def_node.rb +4 -4
- data/lib/sass/tree/mixin_node.rb +8 -21
- data/lib/sass/tree/node.rb +12 -54
- data/lib/sass/tree/prop_node.rb +20 -39
- data/lib/sass/tree/return_node.rb +2 -3
- data/lib/sass/tree/root_node.rb +3 -19
- data/lib/sass/tree/rule_node.rb +22 -35
- data/lib/sass/tree/supports_node.rb +13 -0
- data/lib/sass/tree/trace_node.rb +1 -2
- data/lib/sass/tree/variable_node.rb +3 -9
- data/lib/sass/tree/visitors/base.rb +8 -5
- data/lib/sass/tree/visitors/check_nesting.rb +19 -49
- data/lib/sass/tree/visitors/convert.rb +56 -74
- data/lib/sass/tree/visitors/cssize.rb +74 -202
- data/lib/sass/tree/visitors/deep_copy.rb +5 -10
- data/lib/sass/tree/visitors/extend.rb +7 -7
- data/lib/sass/tree/visitors/perform.rb +185 -278
- data/lib/sass/tree/visitors/set_options.rb +6 -20
- data/lib/sass/tree/visitors/to_css.rb +81 -234
- data/lib/sass/tree/warn_node.rb +2 -2
- data/lib/sass/tree/while_node.rb +2 -2
- data/lib/sass/util.rb +152 -522
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/subset_map.rb +3 -4
- data/lib/sass/util/test.rb +1 -0
- data/lib/sass/version.rb +22 -20
- data/test/Gemfile +3 -0
- data/test/Gemfile.lock +10 -0
- data/test/sass/cache_test.rb +20 -62
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/conversion_test.rb +2 -296
- data/test/sass/css2sass_test.rb +4 -23
- data/test/sass/engine_test.rb +354 -411
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +145 -324
- data/test/sass/functions_test.rb +86 -873
- data/test/sass/importer_test.rb +21 -241
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +26 -16
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +7 -36
- data/test/sass/script_test.rb +53 -485
- data/test/sass/scss/css_test.rb +28 -143
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +325 -2119
- data/test/sass/templates/scss_import.scss +1 -2
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +1 -86
- data/test/test_helper.rb +8 -37
- metadata +19 -66
- data/lib/sass/exec/base.rb +0 -187
- data/lib/sass/exec/sass_convert.rb +0 -264
- data/lib/sass/exec/sass_scss.rb +0 -424
- data/lib/sass/features.rb +0 -47
- data/lib/sass/script/tree.rb +0 -16
- data/lib/sass/script/tree/funcall.rb +0 -306
- data/lib/sass/script/tree/interpolation.rb +0 -118
- data/lib/sass/script/tree/list_literal.rb +0 -77
- data/lib/sass/script/tree/literal.rb +0 -45
- data/lib/sass/script/tree/map_literal.rb +0 -64
- data/lib/sass/script/tree/selector.rb +0 -26
- data/lib/sass/script/tree/variable.rb +0 -57
- data/lib/sass/script/value.rb +0 -11
- data/lib/sass/script/value/base.rb +0 -240
- data/lib/sass/script/value/bool.rb +0 -35
- data/lib/sass/script/value/color.rb +0 -680
- data/lib/sass/script/value/helpers.rb +0 -262
- data/lib/sass/script/value/list.rb +0 -113
- data/lib/sass/script/value/map.rb +0 -70
- data/lib/sass/script/value/string.rb +0 -97
- data/lib/sass/selector/pseudo.rb +0 -256
- data/lib/sass/source/map.rb +0 -210
- data/lib/sass/source/position.rb +0 -39
- data/lib/sass/source/range.rb +0 -41
- data/lib/sass/stack.rb +0 -120
- data/lib/sass/tree/at_root_node.rb +0 -83
- data/lib/sass/tree/error_node.rb +0 -18
- data/lib/sass/tree/keyframe_rule_node.rb +0 -15
- data/lib/sass/util/cross_platform_random.rb +0 -19
- data/lib/sass/util/normalized_map.rb +0 -130
- data/lib/sass/util/ordered_hash.rb +0 -192
- data/test/sass/compiler_test.rb +0 -232
- data/test/sass/encoding_test.rb +0 -219
- data/test/sass/source_map_test.rb +0 -977
- data/test/sass/superselector_test.rb +0 -191
- data/test/sass/util/normalized_map_test.rb +0 -51
- data/test/sass/value_helpers_test.rb +0 -179
@@ -16,18 +16,13 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
|
|
16
16
|
yield
|
17
17
|
end
|
18
18
|
|
19
|
-
def visit_error(node)
|
20
|
-
node.expr = node.expr.deep_copy
|
21
|
-
yield
|
22
|
-
end
|
23
|
-
|
24
19
|
def visit_each(node)
|
25
20
|
node.list = node.list.deep_copy
|
26
21
|
yield
|
27
22
|
end
|
28
23
|
|
29
24
|
def visit_extend(node)
|
30
|
-
node.selector = node.selector.map {|c| c.is_a?(Sass::Script::
|
25
|
+
node.selector = node.selector.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
31
26
|
yield
|
32
27
|
end
|
33
28
|
|
@@ -60,7 +55,7 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
|
|
60
55
|
end
|
61
56
|
|
62
57
|
def visit_prop(node)
|
63
|
-
node.name = node.name.map {|c| c.is_a?(Sass::Script::
|
58
|
+
node.name = node.name.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
64
59
|
node.value = node.value.deep_copy
|
65
60
|
yield
|
66
61
|
end
|
@@ -71,7 +66,7 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
|
|
71
66
|
end
|
72
67
|
|
73
68
|
def visit_rule(node)
|
74
|
-
node.rule = node.rule.map {|c| c.is_a?(Sass::Script::
|
69
|
+
node.rule = node.rule.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
75
70
|
yield
|
76
71
|
end
|
77
72
|
|
@@ -91,12 +86,12 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
|
|
91
86
|
end
|
92
87
|
|
93
88
|
def visit_directive(node)
|
94
|
-
node.value = node.value.map {|c| c.is_a?(Sass::Script::
|
89
|
+
node.value = node.value.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
95
90
|
yield
|
96
91
|
end
|
97
92
|
|
98
93
|
def visit_media(node)
|
99
|
-
node.query = node.query.map {|c| c.is_a?(Sass::Script::
|
94
|
+
node.query = node.query.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
|
100
95
|
yield
|
101
96
|
end
|
102
97
|
|
@@ -49,7 +49,7 @@ class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base
|
|
49
49
|
def self.check_extends_fired!(extends)
|
50
50
|
extends.each_value do |ex|
|
51
51
|
next if ex.result == :succeeded || ex.node.optional?
|
52
|
-
|
52
|
+
warn = "\"#{ex.extender}\" failed to @extend \"#{ex.target.join}\"."
|
53
53
|
reason =
|
54
54
|
if ex.result == :not_found
|
55
55
|
"The selector \"#{ex.target.join}\" was not found."
|
@@ -57,12 +57,12 @@ class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base
|
|
57
57
|
"No selectors matching \"#{ex.target.join}\" could be unified with \"#{ex.extender}\"."
|
58
58
|
end
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
#{
|
63
|
-
|
64
|
-
Use "@extend #{ex.target.join} !optional" if the extend should be able to fail.
|
65
|
-
|
60
|
+
Sass::Util.sass_warn <<WARN
|
61
|
+
WARNING on line #{ex.node.line}#{" of #{ex.node.filename}" if ex.node.filename}: #{warn}
|
62
|
+
#{reason}
|
63
|
+
This will be an error in future releases of Sass.
|
64
|
+
Use "@extend #{ex.target.join} !optional" if the extend should be able to fail.
|
65
|
+
WARN
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -1,155 +1,103 @@
|
|
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
|
-
|
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
|
39
|
-
|
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
|
43
|
-
|
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
|
3
|
+
# @param root [Tree::Node] The root node of the tree to visit.
|
4
|
+
# @param environment [Sass::Environment] The lexical environment.
|
5
|
+
# @return [Tree::Node] The resulting tree of static nodes.
|
6
|
+
def self.visit(root, environment = Sass::Environment.new)
|
7
|
+
new(environment).send(:visit, root)
|
8
|
+
end
|
51
9
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
10
|
+
# @api private
|
11
|
+
def self.perform_arguments(callable, args, keywords, splat)
|
12
|
+
desc = "#{callable.type.capitalize} #{callable.name}"
|
13
|
+
downcase_desc = "#{callable.type} #{callable.name}"
|
57
14
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
15
|
+
begin
|
16
|
+
unless keywords.empty?
|
17
|
+
unknown_args = Sass::Util.array_minus(keywords.keys,
|
18
|
+
callable.args.map {|var| var.first.underscored_name})
|
19
|
+
if callable.splat && unknown_args.include?(callable.splat.underscored_name)
|
20
|
+
raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc} cannot be used as a named argument.")
|
21
|
+
elsif unknown_args.any?
|
22
|
+
description = unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'
|
23
|
+
raise Sass::SyntaxError.new("#{desc} doesn't have #{description} #{unknown_args.map {|name| "$#{name}"}.join ', '}.")
|
63
24
|
end
|
64
|
-
|
65
|
-
value ||= keywords.delete(var.name)
|
66
|
-
value ||= default && default.perform(env)
|
67
|
-
raise Sass::SyntaxError.new("#{desc} is missing argument #{var.inspect}.") unless value
|
68
|
-
env.set_local_var(var.name, value)
|
69
25
|
end
|
26
|
+
rescue Sass::SyntaxError => keyword_exception
|
27
|
+
end
|
70
28
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
arg_list.options = env.options
|
75
|
-
env.set_local_var(callable.splat.name, arg_list)
|
76
|
-
end
|
29
|
+
# If there's no splat, raise the keyword exception immediately. The actual
|
30
|
+
# raising happens in the ensure clause at the end of this function.
|
31
|
+
return if keyword_exception && !callable.splat
|
77
32
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# this function *and* the keywords attached to the argument list glob object
|
85
|
-
# haven't been accessed.
|
86
|
-
#
|
87
|
-
# The keyword exception takes precedence over any Sass errors, but not over
|
88
|
-
# non-Sass exceptions.
|
89
|
-
if keyword_exception &&
|
90
|
-
!(arg_list && arg_list.keywords_accessed) &&
|
91
|
-
(e.nil? || e.is_a?(Sass::SyntaxError))
|
92
|
-
raise keyword_exception
|
93
|
-
elsif e
|
94
|
-
raise e
|
95
|
-
end
|
33
|
+
if args.size > callable.args.size && !callable.splat
|
34
|
+
takes = callable.args.size
|
35
|
+
passed = args.size
|
36
|
+
raise Sass::SyntaxError.new(
|
37
|
+
"#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
|
38
|
+
"but #{passed} #{passed == 1 ? 'was' : 'were'} passed.")
|
96
39
|
end
|
97
40
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
args = splat.to_a
|
113
|
-
end
|
114
|
-
end
|
115
|
-
kwargs ||= Sass::Util::NormalizedMap.new
|
116
|
-
kwargs.update(performed_keywords)
|
117
|
-
|
118
|
-
if kwarg_splat
|
119
|
-
kwarg_splat = kwarg_splat.perform(environment)
|
120
|
-
unless kwarg_splat.is_a?(Sass::Script::Value::Map)
|
121
|
-
raise Sass::SyntaxError.new("Variable keyword arguments must be a map " +
|
122
|
-
"(was #{kwarg_splat.inspect}).")
|
123
|
-
end
|
124
|
-
kwargs.update(arg_hash(kwarg_splat))
|
41
|
+
splat_sep = :comma
|
42
|
+
if splat
|
43
|
+
args += splat.to_a
|
44
|
+
splat_sep = splat.separator if splat.is_a?(Sass::Script::List)
|
45
|
+
# If the splat argument exists, there won't be any keywords passed in
|
46
|
+
# manually, so we can safely overwrite rather than merge here.
|
47
|
+
keywords = splat.keywords if splat.is_a?(Sass::Script::ArgList)
|
48
|
+
end
|
49
|
+
|
50
|
+
keywords = keywords.dup
|
51
|
+
env = Sass::Environment.new(callable.environment)
|
52
|
+
callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
|
53
|
+
if value && keywords.include?(var.underscored_name)
|
54
|
+
raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name} both by position and by name.")
|
125
55
|
end
|
126
56
|
|
127
|
-
|
57
|
+
value ||= keywords.delete(var.underscored_name)
|
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
61
|
end
|
129
62
|
|
130
|
-
|
63
|
+
if callable.splat
|
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
69
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
70
|
+
yield env
|
71
|
+
rescue Exception => e
|
72
|
+
ensure
|
73
|
+
# If there's a keyword exception, we don't want to throw it immediately,
|
74
|
+
# because the invalid keywords may be part of a glob argument that should be
|
75
|
+
# passed on to another function. So we only raise it if we reach the end of
|
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
|
138
87
|
end
|
139
88
|
end
|
140
|
-
# @comment
|
141
|
-
# rubocop:enable MethodLength
|
142
89
|
|
143
90
|
protected
|
144
91
|
|
145
92
|
def initialize(env)
|
146
93
|
@environment = env
|
94
|
+
# Stack trace information, including mixin includes and imports.
|
95
|
+
@stack = []
|
147
96
|
end
|
148
97
|
|
149
98
|
# If an exception is raised, this adds proper metadata to the backtrace.
|
150
99
|
def visit(node)
|
151
|
-
|
152
|
-
@environment.stack.with_base(node.filename, node.line) {super(node.dup)}
|
100
|
+
super(node.dup)
|
153
101
|
rescue Sass::SyntaxError => e
|
154
102
|
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
155
103
|
raise e
|
@@ -194,11 +142,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
194
142
|
# Prints the expression to STDERR.
|
195
143
|
def visit_debug(node)
|
196
144
|
res = node.expr.perform(@environment)
|
197
|
-
if res.is_a?(Sass::Script::
|
198
|
-
res = res.value
|
199
|
-
else
|
200
|
-
res = res.to_sass
|
201
|
-
end
|
145
|
+
res = res.value if res.is_a?(Sass::Script::String)
|
202
146
|
if node.filename
|
203
147
|
Sass::Util.sass_warn "#{node.filename}:#{node.line} DEBUG: #{res}"
|
204
148
|
else
|
@@ -207,30 +151,13 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
207
151
|
[]
|
208
152
|
end
|
209
153
|
|
210
|
-
# Throws the expression as an error.
|
211
|
-
def visit_error(node)
|
212
|
-
res = node.expr.perform(@environment)
|
213
|
-
if res.is_a?(Sass::Script::Value::String)
|
214
|
-
res = res.value
|
215
|
-
else
|
216
|
-
res = res.to_sass
|
217
|
-
end
|
218
|
-
raise Sass::SyntaxError.new(res)
|
219
|
-
end
|
220
|
-
|
221
154
|
# Runs the child nodes once for each value in the list.
|
222
155
|
def visit_each(node)
|
223
156
|
list = node.list.perform(@environment)
|
224
157
|
|
225
158
|
with_environment Sass::Environment.new(@environment) do
|
226
|
-
list.to_a.map do |
|
227
|
-
|
228
|
-
@environment.set_local_var(node.vars.first, value)
|
229
|
-
else
|
230
|
-
node.vars.zip(value.to_a) do |(var, sub_value)|
|
231
|
-
@environment.set_local_var(var, sub_value || Sass::Script::Value::Null.new)
|
232
|
-
end
|
233
|
-
end
|
159
|
+
list.to_a.map do |v|
|
160
|
+
@environment.set_local_var(node.var, v)
|
234
161
|
node.children.map {|c| visit(c)}
|
235
162
|
end.flatten
|
236
163
|
end
|
@@ -239,8 +166,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
239
166
|
# Runs SassScript interpolation in the selector,
|
240
167
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
241
168
|
def visit_extend(node)
|
242
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.selector),
|
243
|
-
node.filename, node.options[:importer], node.line)
|
169
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.selector), node.filename, node.line)
|
244
170
|
node.resolved_selector = parser.parse_selector
|
245
171
|
node
|
246
172
|
end
|
@@ -253,14 +179,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
253
179
|
to.assert_int!
|
254
180
|
|
255
181
|
to = to.coerce(from.numerator_units, from.denominator_units)
|
256
|
-
|
257
|
-
range = Range.new(direction * from.to_i, direction * to.to_i, node.exclusive)
|
182
|
+
range = Range.new(from.to_i, to.to_i, node.exclusive)
|
258
183
|
|
259
184
|
with_environment Sass::Environment.new(@environment) do
|
260
185
|
range.map do |i|
|
261
186
|
@environment.set_local_var(node.var,
|
262
|
-
Sass::Script::
|
263
|
-
from.numerator_units, from.denominator_units))
|
187
|
+
Sass::Script::Number.new(i, from.numerator_units, from.denominator_units))
|
264
188
|
node.children.map {|c| visit(c)}
|
265
189
|
end.flatten
|
266
190
|
end
|
@@ -270,8 +194,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
270
194
|
def visit_function(node)
|
271
195
|
env = Sass::Environment.new(@environment, node.options)
|
272
196
|
@environment.set_local_function(node.name,
|
273
|
-
Sass::Callable.new(node.name, node.args, node.splat, env,
|
274
|
-
node.children, !:has_content, "function"))
|
197
|
+
Sass::Callable.new(node.name, node.args, node.splat, env, node.children, !:has_content, "function"))
|
275
198
|
[]
|
276
199
|
end
|
277
200
|
|
@@ -291,84 +214,82 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
291
214
|
# Returns a static DirectiveNode if this is importing a CSS file,
|
292
215
|
# or parses and includes the imported Sass file.
|
293
216
|
def visit_import(node)
|
294
|
-
if
|
295
|
-
|
296
|
-
resolved_node.source_range = node.source_range
|
297
|
-
return resolved_node
|
217
|
+
if path = node.css_import?
|
218
|
+
return Sass::Tree::CssImportNode.resolved("url(#{path})")
|
298
219
|
end
|
299
220
|
file = node.imported_file
|
300
|
-
if @
|
301
|
-
handle_import_loop!(node)
|
302
|
-
end
|
221
|
+
handle_import_loop!(node) if @stack.any? {|e| e[:filename] == file.options[:filename]}
|
303
222
|
|
304
223
|
begin
|
305
|
-
@
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
end
|
224
|
+
@stack.push(:filename => node.filename, :line => node.line)
|
225
|
+
root = file.to_tree
|
226
|
+
Sass::Tree::Visitors::CheckNesting.visit(root)
|
227
|
+
node.children = root.children.map {|c| visit(c)}.flatten
|
228
|
+
node
|
311
229
|
rescue Sass::SyntaxError => e
|
312
230
|
e.modify_backtrace(:filename => node.imported_file.options[:filename])
|
313
231
|
e.add_backtrace(:filename => node.filename, :line => node.line)
|
314
232
|
raise e
|
315
233
|
end
|
234
|
+
ensure
|
235
|
+
@stack.pop unless path
|
316
236
|
end
|
317
237
|
|
318
238
|
# Loads a mixin into the environment.
|
319
239
|
def visit_mixindef(node)
|
320
240
|
env = Sass::Environment.new(@environment, node.options)
|
321
241
|
@environment.set_local_mixin(node.name,
|
322
|
-
Sass::Callable.new(node.name, node.args, node.splat, env,
|
323
|
-
node.children, node.has_content, "mixin"))
|
242
|
+
Sass::Callable.new(node.name, node.args, node.splat, env, node.children, node.has_content, "mixin"))
|
324
243
|
[]
|
325
244
|
end
|
326
245
|
|
327
246
|
# Runs a mixin.
|
328
247
|
def visit_mixin(node)
|
329
|
-
|
330
|
-
|
331
|
-
|
248
|
+
include_loop = true
|
249
|
+
handle_include_loop!(node) if @stack.any? {|e| e[:name] == node.name}
|
250
|
+
include_loop = false
|
332
251
|
|
333
|
-
|
334
|
-
|
335
|
-
end
|
252
|
+
@stack.push(:filename => node.filename, :line => node.line, :name => node.name)
|
253
|
+
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
|
336
254
|
|
337
|
-
|
338
|
-
|
339
|
-
|
255
|
+
if node.children.any? && !mixin.has_content
|
256
|
+
raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
|
257
|
+
end
|
340
258
|
|
341
|
-
|
342
|
-
|
343
|
-
|
259
|
+
args = node.args.map {|a| a.perform(@environment)}
|
260
|
+
keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
|
261
|
+
splat = node.splat.perform(@environment) if node.splat
|
344
262
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
263
|
+
self.class.perform_arguments(mixin, args, keywords, splat) do |env|
|
264
|
+
env.caller = Sass::Environment.new(@environment)
|
265
|
+
env.content = node.children if node.has_children
|
266
|
+
|
267
|
+
trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
|
268
|
+
with_environment(env) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
|
269
|
+
trace_node
|
349
270
|
end
|
350
271
|
rescue Sass::SyntaxError => e
|
351
|
-
|
352
|
-
|
272
|
+
unless include_loop
|
273
|
+
e.modify_backtrace(:mixin => node.name, :line => node.line)
|
274
|
+
e.add_backtrace(:line => node.line)
|
275
|
+
end
|
353
276
|
raise e
|
277
|
+
ensure
|
278
|
+
@stack.pop unless include_loop
|
354
279
|
end
|
355
280
|
|
356
281
|
def visit_content(node)
|
357
|
-
content
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
content_env.caller = Sass::Environment.new(@environment)
|
363
|
-
with_environment(content_env) do
|
364
|
-
trace_node.children = content.map {|c| visit(c.dup)}.flatten
|
365
|
-
end
|
366
|
-
trace_node
|
367
|
-
end
|
282
|
+
return [] unless content = @environment.content
|
283
|
+
@stack.push(:filename => node.filename, :line => node.line, :name => '@content')
|
284
|
+
trace_node = Sass::Tree::TraceNode.from_node('@content', node)
|
285
|
+
with_environment(@environment.caller) {trace_node.children = content.map {|c| visit(c.dup)}.flatten}
|
286
|
+
trace_node
|
368
287
|
rescue Sass::SyntaxError => e
|
369
288
|
e.modify_backtrace(:mixin => '@content', :line => node.line)
|
370
289
|
e.add_backtrace(:line => node.line)
|
371
290
|
raise e
|
291
|
+
ensure
|
292
|
+
@stack.pop if content
|
372
293
|
end
|
373
294
|
|
374
295
|
# Runs any SassScript that may be embedded in a property.
|
@@ -376,7 +297,6 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
376
297
|
node.resolved_name = run_interp(node.name)
|
377
298
|
val = node.value.perform(@environment)
|
378
299
|
node.resolved_value = val.to_s
|
379
|
-
node.value_source_range = val.source_range if val.source_range
|
380
300
|
yield
|
381
301
|
end
|
382
302
|
|
@@ -388,83 +308,38 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
388
308
|
# Runs SassScript interpolation in the selector,
|
389
309
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
390
310
|
def visit_rule(node)
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
keyframe_rule_node.source_range = node.source_range
|
400
|
-
with_environment Sass::Environment.new(@environment, node.options) do
|
401
|
-
keyframe_rule_node.children = node.children.map {|c| visit(c)}.flatten
|
402
|
-
end
|
403
|
-
keyframe_rule_node
|
404
|
-
else
|
405
|
-
@at_root_without_rule = false
|
406
|
-
node.parsed_rules ||= parser.parse_selector
|
407
|
-
node.resolved_rules = node.parsed_rules.resolve_parent_refs(
|
408
|
-
@environment.selector, !old_at_root_without_rule)
|
409
|
-
node.stack_trace = @environment.stack.to_s if node.options[:trace_selectors]
|
410
|
-
with_environment Sass::Environment.new(@environment, node.options) do
|
411
|
-
@environment.selector = node.resolved_rules
|
412
|
-
node.children = node.children.map {|c| visit(c)}.flatten
|
413
|
-
end
|
414
|
-
node
|
311
|
+
rule = node.rule
|
312
|
+
rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
|
313
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
|
314
|
+
node.parsed_rules ||= parser.parse_selector
|
315
|
+
if node.options[:trace_selectors]
|
316
|
+
@stack.push(:filename => node.filename, :line => node.line)
|
317
|
+
node.stack_trace = stack_trace
|
318
|
+
@stack.pop
|
415
319
|
end
|
416
|
-
ensure
|
417
|
-
@at_root_without_rule = old_at_root_without_rule
|
418
|
-
end
|
419
|
-
|
420
|
-
# Sets a variable that indicates that the first level of rule nodes
|
421
|
-
# shouldn't include the parent selector by default.
|
422
|
-
def visit_atroot(node)
|
423
|
-
if node.query
|
424
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
425
|
-
node.filename, node.options[:importer], node.line)
|
426
|
-
node.resolved_type, node.resolved_value = parser.parse_static_at_root_query
|
427
|
-
else
|
428
|
-
node.resolved_type, node.resolved_value = :without, ['rule']
|
429
|
-
end
|
430
|
-
|
431
|
-
old_at_root_without_rule = @at_root_without_rule
|
432
|
-
old_in_keyframes = @in_keyframes
|
433
|
-
@at_root_without_rule = true if node.exclude?('rule')
|
434
|
-
@in_keyframes = false if node.exclude?('keyframes')
|
435
320
|
yield
|
436
|
-
ensure
|
437
|
-
@in_keyframes = old_in_keyframes
|
438
|
-
@at_root_without_rule = old_at_root_without_rule
|
439
321
|
end
|
440
322
|
|
441
323
|
# Loads the new variable value into the environment.
|
442
324
|
def visit_variable(node)
|
443
|
-
|
444
|
-
|
445
|
-
if node.guarded
|
446
|
-
var = env.var(node.name)
|
447
|
-
return [] if var && !var.null?
|
448
|
-
end
|
449
|
-
|
325
|
+
var = @environment.var(node.name)
|
326
|
+
return [] if node.guarded && var && !var.null?
|
450
327
|
val = node.expr.perform(@environment)
|
451
|
-
|
452
|
-
val.source_range = node.expr.source_range
|
453
|
-
else
|
454
|
-
val.source_range = node.source_range
|
455
|
-
end
|
456
|
-
env.set_var(node.name, val)
|
328
|
+
@environment.set_var(node.name, val)
|
457
329
|
[]
|
458
330
|
end
|
459
331
|
|
460
332
|
# Prints the expression to STDERR with a stylesheet trace.
|
461
333
|
def visit_warn(node)
|
334
|
+
@stack.push(:filename => node.filename, :line => node.line)
|
462
335
|
res = node.expr.perform(@environment)
|
463
|
-
res = res.value if res.is_a?(Sass::Script::
|
336
|
+
res = res.value if res.is_a?(Sass::Script::String)
|
464
337
|
msg = "WARNING: #{res}\n "
|
465
|
-
msg <<
|
338
|
+
msg << stack_trace.join("\n ") << "\n"
|
466
339
|
Sass::Util.sass_warn msg
|
467
340
|
[]
|
341
|
+
ensure
|
342
|
+
@stack.pop
|
468
343
|
end
|
469
344
|
|
470
345
|
# Runs the child nodes until the continuation expression becomes false.
|
@@ -478,18 +353,11 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
478
353
|
|
479
354
|
def visit_directive(node)
|
480
355
|
node.resolved_value = run_interp(node.value)
|
481
|
-
|
482
|
-
with_environment Sass::Environment.new(@environment) do
|
483
|
-
node.children = node.children.map {|c| visit(c)}.flatten
|
484
|
-
node
|
485
|
-
end
|
486
|
-
ensure
|
487
|
-
@in_keyframes = old_in_keyframes
|
356
|
+
yield
|
488
357
|
end
|
489
358
|
|
490
359
|
def visit_media(node)
|
491
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
492
|
-
node.filename, node.options[:importer], node.line)
|
360
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.query), node.filename, node.line)
|
493
361
|
node.resolved_query ||= parser.parse_media_query_list
|
494
362
|
yield
|
495
363
|
end
|
@@ -502,9 +370,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
502
370
|
|
503
371
|
def visit_cssimport(node)
|
504
372
|
node.resolved_uri = run_interp([node.uri])
|
505
|
-
if node.query
|
506
|
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
|
507
|
-
node.filename, node.options[:importer], node.line)
|
373
|
+
if node.query
|
374
|
+
parser = Sass::SCSS::StaticParser.new(run_interp(node.query), node.filename, node.line)
|
508
375
|
node.resolved_query ||= parser.parse_media_query_list
|
509
376
|
end
|
510
377
|
yield
|
@@ -512,10 +379,26 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
512
379
|
|
513
380
|
private
|
514
381
|
|
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
|
+
|
515
395
|
def run_interp_no_strip(text)
|
516
396
|
text.map do |r|
|
517
397
|
next r if r.is_a?(String)
|
518
|
-
r.perform(@environment)
|
398
|
+
val = r.perform(@environment)
|
399
|
+
# Interpolated strings should never render with quotes
|
400
|
+
next val.value if val.is_a?(Sass::Script::String)
|
401
|
+
val.to_s
|
519
402
|
end.join
|
520
403
|
end
|
521
404
|
|
@@ -523,9 +406,33 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
523
406
|
run_interp_no_strip(text).strip
|
524
407
|
end
|
525
408
|
|
409
|
+
def handle_include_loop!(node)
|
410
|
+
msg = "An @include loop has been found:"
|
411
|
+
content_count = 0
|
412
|
+
mixins = @stack.reverse.map {|s| s[:name]}.compact.select do |s|
|
413
|
+
if s == '@content'
|
414
|
+
content_count += 1
|
415
|
+
false
|
416
|
+
elsif content_count > 0
|
417
|
+
content_count -= 1
|
418
|
+
false
|
419
|
+
else
|
420
|
+
true
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
return unless mixins.include?(node.name)
|
425
|
+
raise Sass::SyntaxError.new("#{msg} #{node.name} includes itself") if mixins.size == 1
|
426
|
+
|
427
|
+
msg << "\n" << Sass::Util.enum_cons(mixins.reverse + [node.name], 2).map do |m1, m2|
|
428
|
+
" #{m1} includes #{m2}"
|
429
|
+
end.join("\n")
|
430
|
+
raise Sass::SyntaxError.new(msg)
|
431
|
+
end
|
432
|
+
|
526
433
|
def handle_import_loop!(node)
|
527
434
|
msg = "An @import loop has been found:"
|
528
|
-
files = @
|
435
|
+
files = @stack.map {|s| s[:filename]}.compact
|
529
436
|
if node.filename == node.imported_file.options[:filename]
|
530
437
|
raise Sass::SyntaxError.new("#{msg} #{node.filename} imports itself")
|
531
438
|
end
|