sass 3.3.0.alpha.256 → 3.3.0.alpha.353
Sign up to get free protection for your applications and to get access to all the features.
- data/REVISION +1 -1
- data/Rakefile +21 -1
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass.rb +6 -3
- data/lib/sass/cache_stores/base.rb +1 -1
- 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 +6 -6
- data/lib/sass/engine.rb +60 -34
- data/lib/sass/environment.rb +3 -1
- data/lib/sass/error.rb +5 -5
- data/lib/sass/exec.rb +52 -25
- data/lib/sass/features.rb +0 -2
- data/lib/sass/importers/deprecated_path.rb +1 -1
- data/lib/sass/importers/filesystem.rb +8 -6
- data/lib/sass/logger/base.rb +3 -3
- data/lib/sass/logger/log_level.rb +4 -6
- data/lib/sass/media.rb +2 -2
- data/lib/sass/plugin.rb +4 -2
- data/lib/sass/plugin/compiler.rb +28 -15
- data/lib/sass/plugin/configuration.rb +15 -7
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +24 -8
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script.rb +2 -1
- data/lib/sass/script/css_lexer.rb +8 -3
- data/lib/sass/script/css_parser.rb +6 -2
- data/lib/sass/script/functions.rb +164 -109
- data/lib/sass/script/lexer.rb +30 -20
- data/lib/sass/script/parser.rb +66 -37
- data/lib/sass/script/tree/funcall.rb +23 -14
- data/lib/sass/script/tree/interpolation.rb +5 -1
- data/lib/sass/script/tree/list_literal.rb +5 -4
- data/lib/sass/script/tree/map_literal.rb +1 -1
- data/lib/sass/script/tree/node.rb +2 -2
- data/lib/sass/script/tree/operation.rb +2 -1
- data/lib/sass/script/tree/selector.rb +3 -2
- data/lib/sass/script/tree/string_interpolation.rb +2 -1
- data/lib/sass/script/tree/variable.rb +4 -3
- data/lib/sass/script/value/base.rb +12 -14
- data/lib/sass/script/value/color.rb +35 -16
- data/lib/sass/script/value/helpers.rb +146 -0
- data/lib/sass/script/value/list.rb +24 -5
- data/lib/sass/script/value/map.rb +1 -1
- data/lib/sass/script/value/null.rb +13 -3
- data/lib/sass/script/value/number.rb +44 -35
- data/lib/sass/script/value/string.rb +2 -2
- data/lib/sass/scss/css_parser.rb +2 -1
- data/lib/sass/scss/parser.rb +143 -93
- data/lib/sass/scss/rx.rb +4 -4
- 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 +5 -5
- data/lib/sass/selector.rb +5 -2
- data/lib/sass/selector/abstract_sequence.rb +1 -1
- data/lib/sass/selector/comma_sequence.rb +16 -14
- data/lib/sass/selector/sequence.rb +38 -24
- data/lib/sass/selector/simple.rb +4 -4
- data/lib/sass/selector/simple_sequence.rb +21 -11
- data/lib/sass/source/map.rb +7 -2
- data/lib/sass/source/range.rb +1 -1
- data/lib/sass/supports.rb +3 -3
- data/lib/sass/tree/debug_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +2 -1
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +3 -4
- data/lib/sass/tree/prop_node.rb +4 -2
- data/lib/sass/tree/rule_node.rb +5 -2
- 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 +34 -28
- data/lib/sass/tree/visitors/cssize.rb +4 -3
- data/lib/sass/tree/visitors/deep_copy.rb +1 -0
- data/lib/sass/tree/visitors/perform.rb +31 -16
- data/lib/sass/tree/visitors/to_css.rb +34 -16
- data/lib/sass/util.rb +88 -37
- data/lib/sass/util/multibyte_string_scanner.rb +2 -0
- data/lib/sass/util/ordered_hash.rb +20 -18
- data/lib/sass/util/subset_map.rb +3 -2
- data/lib/sass/util/test.rb +0 -1
- data/lib/sass/version.rb +9 -5
- data/test/rubocop_extensions.rb +70 -0
- data/test/sass/functions_test.rb +20 -1
- data/test/sass/importer_test.rb +2 -1
- data/test/sass/script_test.rb +4 -0
- data/test/sass/source_map_test.rb +1 -1
- data/test/sass/util_test.rb +49 -0
- data/test/sass/value_helpers_test.rb +181 -0
- metadata +13 -9
data/lib/sass/source/range.rb
CHANGED
@@ -26,7 +26,7 @@ module Sass::Source
|
|
26
26
|
# @param end_pos [Sass::Source::Position] See \{#end_pos}
|
27
27
|
# @param file [String] See \{#file}
|
28
28
|
# @param importer [Sass::Importers::Base] See \{#importer}
|
29
|
-
def initialize(start_pos, end_pos, file, importer=nil)
|
29
|
+
def initialize(start_pos, end_pos, file, importer = nil)
|
30
30
|
@start_pos = start_pos
|
31
31
|
@end_pos = end_pos
|
32
32
|
@file = file
|
data/lib/sass/supports.rb
CHANGED
@@ -81,12 +81,12 @@ module Sass::Supports
|
|
81
81
|
|
82
82
|
def left_parens(str)
|
83
83
|
return "(#{str})" if @left.is_a?(Negation)
|
84
|
-
|
84
|
+
str
|
85
85
|
end
|
86
86
|
|
87
87
|
def right_parens(str)
|
88
88
|
return "(#{str})" if @right.is_a?(Negation) || @right.is_a?(Operator)
|
89
|
-
|
89
|
+
str
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -127,7 +127,7 @@ module Sass::Supports
|
|
127
127
|
|
128
128
|
def parens(str)
|
129
129
|
return "(#{str})" if @condition.is_a?(Negation) || @condition.is_a?(Operator)
|
130
|
-
|
130
|
+
str
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
data/lib/sass/tree/debug_node.rb
CHANGED
@@ -21,7 +21,8 @@ module Sass
|
|
21
21
|
attr_accessor :splat
|
22
22
|
|
23
23
|
# @param name [String] The function name
|
24
|
-
# @param args [Array<(Script::Tree::Node, Script::Tree::Node)>]
|
24
|
+
# @param args [Array<(Script::Tree::Node, Script::Tree::Node)>]
|
25
|
+
# The arguments for the function.
|
25
26
|
# @param splat [Script::Tree::Node] See \{#splat}
|
26
27
|
def initialize(name, args, splat)
|
27
28
|
@name = name
|
data/lib/sass/tree/if_node.rb
CHANGED
@@ -51,9 +51,8 @@ module Sass
|
|
51
51
|
end
|
52
52
|
|
53
53
|
paths.each do |p|
|
54
|
-
|
55
|
-
|
56
|
-
end
|
54
|
+
f = p.find(@imported_filename, options_for_importer)
|
55
|
+
return f if f
|
57
56
|
end
|
58
57
|
|
59
58
|
message = "File to import not found or unreadable: #{@imported_filename}.\n"
|
@@ -64,7 +63,7 @@ module Sass
|
|
64
63
|
end
|
65
64
|
raise SyntaxError.new(message)
|
66
65
|
rescue SyntaxError => e
|
67
|
-
raise SyntaxError.new(e.message, :line =>
|
66
|
+
raise SyntaxError.new(e.message, :line => line, :filename => @filename)
|
68
67
|
end
|
69
68
|
|
70
69
|
def options_for_importer
|
data/lib/sass/tree/prop_node.rb
CHANGED
@@ -99,7 +99,8 @@ module Sass::Tree
|
|
99
99
|
def declaration(opts = {:old => @prop_syntax == :old}, fmt = :sass)
|
100
100
|
name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass(opts)}}"}.join
|
101
101
|
if name[0] == ?:
|
102
|
-
raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\"
|
102
|
+
raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\"" +
|
103
|
+
" hack is not allowed in the Sass indented syntax")
|
103
104
|
end
|
104
105
|
|
105
106
|
old = opts[:old] && fmt == :sass
|
@@ -120,7 +121,8 @@ module Sass::Tree
|
|
120
121
|
def check!
|
121
122
|
if @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
|
122
123
|
raise Sass::SyntaxError.new(
|
123
|
-
"Illegal property syntax: can't use #{@prop_syntax} syntax when
|
124
|
+
"Illegal property syntax: can't use #{@prop_syntax} syntax when " +
|
125
|
+
":property_syntax => #{@options[:property_syntax].inspect} is set.")
|
124
126
|
end
|
125
127
|
end
|
126
128
|
|
data/lib/sass/tree/rule_node.rb
CHANGED
@@ -111,12 +111,13 @@ module Sass::Tree
|
|
111
111
|
|
112
112
|
# A hash that will be associated with this rule in the CSS document
|
113
113
|
# if the {file:SASS_REFERENCE.md#debug_info-option `:debug_info` option} is enabled.
|
114
|
-
# This data is used by e.g. [the FireSass Firebug
|
114
|
+
# This data is used by e.g. [the FireSass Firebug
|
115
|
+
# extension](https://addons.mozilla.org/en-US/firefox/addon/103988).
|
115
116
|
#
|
116
117
|
# @return [{#to_s => #to_s}]
|
117
118
|
def debug_info
|
118
119
|
{:filename => filename && ("file://" + Sass::Util.escape_uri(File.expand_path(filename))),
|
119
|
-
:line =>
|
120
|
+
:line => line}
|
120
121
|
end
|
121
122
|
|
122
123
|
# A rule node is invisible if it has only placeholder selectors.
|
@@ -131,7 +132,9 @@ module Sass::Tree
|
|
131
132
|
# We don't use real filename/line info because we don't have it yet.
|
132
133
|
# When we get it, we'll set it on the parsed rules if possible.
|
133
134
|
parser = Sass::SCSS::StaticParser.new(@rule.join.strip, '', nil, 1)
|
135
|
+
# rubocop:disable RescueModifier
|
134
136
|
@parsed_rules = parser.parse_selector rescue nil
|
137
|
+
# rubocop:enable RescueModifier
|
135
138
|
end
|
136
139
|
end
|
137
140
|
end
|
@@ -32,9 +32,9 @@ module Sass::Tree::Visitors
|
|
32
32
|
# @param node [Tree::Node] The node to visit.
|
33
33
|
# @return [Object] The return value of the `visit_*` method for this node.
|
34
34
|
def visit(node)
|
35
|
-
method = "visit_#{node_name node}"
|
36
|
-
if
|
37
|
-
|
35
|
+
method = "visit_#{self.class.node_name node}"
|
36
|
+
if respond_to?(method, true)
|
37
|
+
send(method, node) {visit_children(node)}
|
38
38
|
else
|
39
39
|
visit_children(node)
|
40
40
|
end
|
@@ -59,9 +59,9 @@ module Sass::Tree::Visitors
|
|
59
59
|
#
|
60
60
|
# @param [Tree::Node] node The node.
|
61
61
|
# @return [String] The name.
|
62
|
-
def node_name(node)
|
63
|
-
|
64
|
-
|
62
|
+
def self.node_name(node)
|
63
|
+
@node_names ||= {}
|
64
|
+
@node_names[node.class.name] ||= node.class.name.gsub(NODE_NAME_RE, '\\1').downcase
|
65
65
|
end
|
66
66
|
|
67
67
|
# `yield`s, then runs the visitor on the `@else` clause if the node has one.
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# A visitor for checking that all nodes are properly nested.
|
2
2
|
class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
3
|
+
|
3
4
|
protected
|
4
5
|
|
5
6
|
def initialize
|
@@ -7,9 +8,9 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
7
8
|
end
|
8
9
|
|
9
10
|
def visit(node)
|
10
|
-
if error = @parent && (
|
11
|
-
try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
|
12
|
-
try_send("invalid_#{node_name node}_parent?", @parent, node))
|
11
|
+
if (error = @parent && (
|
12
|
+
try_send("invalid_#{self.class.node_name @parent}_child?", @parent, node) ||
|
13
|
+
try_send("invalid_#{self.class.node_name node}_parent?", @parent, node)))
|
13
14
|
raise Sass::SyntaxError.new(error)
|
14
15
|
end
|
15
16
|
super
|
@@ -19,7 +20,7 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
19
20
|
end
|
20
21
|
|
21
22
|
CONTROL_NODES = [Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode,
|
22
|
-
|
23
|
+
Sass::Tree::WhileNode, Sass::Tree::TraceNode]
|
23
24
|
SCRIPT_NODES = [Sass::Tree::ImportNode] + CONTROL_NODES
|
24
25
|
def visit_children(parent)
|
25
26
|
old_parent = @parent
|
@@ -110,7 +111,9 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
113
|
-
VALID_PROP_CHILDREN = [Sass::Tree::CommentNode,
|
114
|
+
VALID_PROP_CHILDREN = CONTROL_NODES + [Sass::Tree::CommentNode,
|
115
|
+
Sass::Tree::PropNode,
|
116
|
+
Sass::Tree::MixinNode]
|
114
117
|
def invalid_prop_child?(parent, child)
|
115
118
|
unless is_any_of?(child, VALID_PROP_CHILDREN)
|
116
119
|
"Illegal nesting: Only properties may be nested beneath properties."
|
@@ -122,7 +125,8 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
122
125
|
Sass::Tree::MixinNode]
|
123
126
|
def invalid_prop_parent?(parent, child)
|
124
127
|
unless is_any_of?(parent, VALID_PROP_PARENTS)
|
125
|
-
"Properties are only allowed within rules, directives, mixin includes, or other properties." +
|
128
|
+
"Properties are only allowed within rules, directives, mixin includes, or other properties." +
|
129
|
+
child.pseudo_class_selector_message
|
126
130
|
end
|
127
131
|
end
|
128
132
|
|
@@ -133,10 +137,10 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
133
137
|
private
|
134
138
|
|
135
139
|
def is_any_of?(val, classes)
|
136
|
-
|
140
|
+
classes.each do |c|
|
137
141
|
return true if val.is_a?(c)
|
138
142
|
end
|
139
|
-
|
143
|
+
false
|
140
144
|
end
|
141
145
|
|
142
146
|
def try_send(method, *args)
|
@@ -144,4 +148,3 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
144
148
|
send(method, *args)
|
145
149
|
end
|
146
150
|
end
|
147
|
-
|
@@ -23,7 +23,11 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
23
23
|
def visit_children(parent)
|
24
24
|
@tabs += 1
|
25
25
|
return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
|
26
|
-
|
26
|
+
if @format == :sass
|
27
|
+
"\n" + super.join.rstrip + "\n"
|
28
|
+
else
|
29
|
+
" {\n" + super.join.rstrip + "\n#{ @tab_chars * (@tabs - 1)}}\n"
|
30
|
+
end
|
27
31
|
ensure
|
28
32
|
@tabs -= 1
|
29
33
|
end
|
@@ -52,7 +56,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
52
56
|
|
53
57
|
def visit_comment(node)
|
54
58
|
value = interp_to_src(node.value)
|
55
|
-
|
59
|
+
if @format == :sass
|
56
60
|
content = value.gsub(/\*\/$/, '').rstrip
|
57
61
|
if content =~ /\A[ \t]/
|
58
62
|
# Re-indent SCSS comments like this:
|
@@ -63,31 +67,27 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
63
67
|
content.sub!(/\A([ \t]*)\/\*/, '/*\1')
|
64
68
|
end
|
65
69
|
|
66
|
-
content
|
67
|
-
|
68
|
-
|
70
|
+
if content.include?("\n")
|
71
|
+
content.gsub!(%r{\n( \*|//)}, "\n ")
|
72
|
+
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
|
73
|
+
sep = node.type == :silent ? "\n//" : "\n *"
|
74
|
+
if spaces >= 2
|
75
|
+
content.gsub!(/\n /, sep)
|
69
76
|
else
|
70
|
-
content.gsub!(/\n
|
71
|
-
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
|
72
|
-
sep = node.type == :silent ? "\n//" : "\n *"
|
73
|
-
if spaces >= 2
|
74
|
-
content.gsub(/\n /, sep)
|
75
|
-
else
|
76
|
-
content.gsub(/\n#{' ' * spaces}/, sep)
|
77
|
-
end
|
77
|
+
content.gsub!(/\n#{' ' * spaces}/, sep)
|
78
78
|
end
|
79
|
+
end
|
79
80
|
|
80
81
|
content.gsub!(/\A\/\*/, '//') if node.type == :silent
|
81
82
|
content.gsub!(/^/, tab_str)
|
82
|
-
content.rstrip + "\n"
|
83
|
+
content = content.rstrip + "\n"
|
83
84
|
else
|
84
85
|
spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
|
85
86
|
content = if node.type == :silent
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
content
|
87
|
+
value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
|
88
|
+
else
|
89
|
+
value
|
90
|
+
end.gsub(/^/, spaces) + "\n"
|
91
91
|
end
|
92
92
|
content
|
93
93
|
end
|
@@ -98,7 +98,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
98
98
|
|
99
99
|
def visit_directive(node)
|
100
100
|
res = "#{tab_str}#{interp_to_src(node.value)}"
|
101
|
-
res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
|
101
|
+
res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
|
102
102
|
return res + "#{semi}\n" unless node.has_children
|
103
103
|
res + yield + "\n"
|
104
104
|
end
|
@@ -109,7 +109,8 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def visit_extend(node)
|
112
|
-
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}
|
112
|
+
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}" +
|
113
|
+
"#{" !optional" if node.optional?}\n"
|
113
114
|
end
|
114
115
|
|
115
116
|
def visit_for(node)
|
@@ -131,9 +132,12 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
131
132
|
|
132
133
|
def visit_if(node)
|
133
134
|
name =
|
134
|
-
if !@is_else
|
135
|
-
|
136
|
-
|
135
|
+
if !@is_else
|
136
|
+
"if"
|
137
|
+
elsif node.expr
|
138
|
+
"else if"
|
139
|
+
else
|
140
|
+
"else"
|
137
141
|
end
|
138
142
|
@is_else = false
|
139
143
|
str = "#{tab_str}@#{name}"
|
@@ -206,13 +210,14 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
206
210
|
keywords = Sass::Util.hash_to_a(node.keywords).
|
207
211
|
map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}.join(', ')
|
208
212
|
if node.splat
|
209
|
-
splat =
|
213
|
+
splat = args.empty? && keywords.empty? ? "" : ", "
|
210
214
|
splat = "#{splat}#{arg_to_sass[node.splat]}..."
|
211
215
|
splat = "#{splat}, #{node.kwarg_splat.inspect}..." if node.kwarg_splat
|
212
216
|
end
|
213
217
|
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
|
214
218
|
end
|
215
|
-
"#{tab_str}#{@format == :sass ? '+' : '@include '}
|
219
|
+
"#{tab_str}#{@format == :sass ? '+' : '@include '}" +
|
220
|
+
"#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
|
216
221
|
end
|
217
222
|
|
218
223
|
def visit_content(node)
|
@@ -246,7 +251,8 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
246
251
|
end
|
247
252
|
|
248
253
|
def visit_variable(node)
|
249
|
-
"#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}
|
254
|
+
"#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}" +
|
255
|
+
"#{' !default' if node.guarded}#{semi}\n"
|
250
256
|
end
|
251
257
|
|
252
258
|
def visit_warn(node)
|
@@ -280,7 +286,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
280
286
|
def media_interp_to_src(interp)
|
281
287
|
Sass::Util.enum_with_index(interp).map do |r, i|
|
282
288
|
next r if r.is_a?(String)
|
283
|
-
before, after = interp[i-1], interp[i+1]
|
289
|
+
before, after = interp[i - 1], interp[i + 1]
|
284
290
|
if before.is_a?(String) && after.is_a?(String) &&
|
285
291
|
((before[-1] == ?( && after[0] == ?:) ||
|
286
292
|
(before =~ /:\s*/ && after[0] == ?)))
|
@@ -81,7 +81,7 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
81
81
|
node.children.each_with_index.find {|c, _| c.is_a?(Sass::Tree::CharsetNode)}
|
82
82
|
if charset_and_index
|
83
83
|
index = charset_and_index.last
|
84
|
-
node.children = node.children[0..index] + imports + node.children[index+1..-1]
|
84
|
+
node.children = node.children[0..index] + imports + node.children[index + 1..-1]
|
85
85
|
else
|
86
86
|
node.children = imports + node.children
|
87
87
|
end
|
@@ -125,7 +125,7 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
125
125
|
|
126
126
|
sel = sseq.members
|
127
127
|
parent.resolved_rules.members.each do |member|
|
128
|
-
|
128
|
+
unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
|
129
129
|
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
|
130
130
|
end
|
131
131
|
|
@@ -195,7 +195,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
195
195
|
# and updates the indentation of the rule node based on the nesting level.
|
196
196
|
def visit_rule(node)
|
197
197
|
parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
|
198
|
-
# It's possible for resolved_rules to be set
|
198
|
+
# It's possible for resolved_rules to be set
|
199
|
+
# if we've duplicated this node during @media bubbling
|
199
200
|
node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)
|
200
201
|
|
201
202
|
yield
|
@@ -9,6 +9,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# @api private
|
12
|
+
# @comment
|
13
|
+
# rubocop:disable MethodLength
|
12
14
|
def perform_arguments(callable, args, keywords, splat)
|
13
15
|
desc = "#{callable.type.capitalize} #{callable.name}"
|
14
16
|
downcase_desc = "#{callable.type} #{callable.name}"
|
@@ -25,10 +27,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
25
27
|
unknown_args = Sass::Util.array_minus(keywords.keys,
|
26
28
|
callable.args.map {|var| var.first.underscored_name})
|
27
29
|
if callable.splat && unknown_args.include?(callable.splat.underscored_name)
|
28
|
-
raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc}
|
30
|
+
raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc} " +
|
31
|
+
"cannot be used as a named argument.")
|
29
32
|
elsif unknown_args.any?
|
30
33
|
description = unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'
|
31
|
-
raise Sass::SyntaxError.new("#{desc} doesn't have #{description}
|
34
|
+
raise Sass::SyntaxError.new("#{desc} doesn't have #{description} " +
|
35
|
+
"#{unknown_args.map {|name| "$#{name}"}.join ', '}.")
|
32
36
|
end
|
33
37
|
end
|
34
38
|
rescue Sass::SyntaxError => keyword_exception
|
@@ -56,7 +60,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
56
60
|
env = Sass::Environment.new(callable.environment)
|
57
61
|
callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
|
58
62
|
if value && keywords.include?(var.underscored_name)
|
59
|
-
raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name}
|
63
|
+
raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name} " +
|
64
|
+
"both by position and by name.")
|
60
65
|
end
|
61
66
|
|
62
67
|
value ||= keywords.delete(var.underscored_name)
|
@@ -73,7 +78,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
73
78
|
end
|
74
79
|
|
75
80
|
yield env
|
76
|
-
rescue
|
81
|
+
rescue StandardError => e
|
77
82
|
ensure
|
78
83
|
# If there's a keyword exception, we don't want to throw it immediately,
|
79
84
|
# because the invalid keywords may be part of a glob argument that should be
|
@@ -111,7 +116,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
111
116
|
|
112
117
|
kwarg_splat = kwarg_splat.perform(environment)
|
113
118
|
unless kwarg_splat.is_a?(Sass::Script::Value::Map)
|
114
|
-
raise Sass::SyntaxError.new("Variable keyword arguments must be a map
|
119
|
+
raise Sass::SyntaxError.new("Variable keyword arguments must be a map " +
|
120
|
+
"(was #{kwarg_splat.inspect}).")
|
115
121
|
end
|
116
122
|
|
117
123
|
if splat.is_a?(Sass::Script::Value::ArgList)
|
@@ -128,10 +134,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
128
134
|
Sass::Util.map_keys(map.to_h) do |key|
|
129
135
|
next key.value if key.is_a?(Sass::Script::Value::String)
|
130
136
|
raise Sass::SyntaxError.new("Variable keyword argument map must have string keys.\n" +
|
131
|
-
"#{key.inspect} is not a string in #{map.inspect}.")
|
137
|
+
"#{key.inspect} is not a string in #{map.inspect}.")
|
132
138
|
end
|
133
139
|
end
|
134
140
|
end
|
141
|
+
# @comment
|
142
|
+
# rubocop:enable MethodLength
|
135
143
|
|
136
144
|
protected
|
137
145
|
|
@@ -246,7 +254,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
246
254
|
def visit_function(node)
|
247
255
|
env = Sass::Environment.new(@environment, node.options)
|
248
256
|
@environment.set_local_function(node.name,
|
249
|
-
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"))
|
250
259
|
[]
|
251
260
|
end
|
252
261
|
|
@@ -266,7 +275,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
266
275
|
# Returns a static DirectiveNode if this is importing a CSS file,
|
267
276
|
# or parses and includes the imported Sass file.
|
268
277
|
def visit_import(node)
|
269
|
-
if path = node.css_import?
|
278
|
+
if (path = node.css_import?)
|
270
279
|
resolved_node = Sass::Tree::CssImportNode.resolved("url(#{path})")
|
271
280
|
resolved_node.source_range = node.source_range
|
272
281
|
return resolved_node
|
@@ -294,18 +303,22 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
294
303
|
def visit_mixindef(node)
|
295
304
|
env = Sass::Environment.new(@environment, node.options)
|
296
305
|
@environment.set_local_mixin(node.name,
|
297
|
-
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"))
|
298
308
|
[]
|
299
309
|
end
|
300
310
|
|
301
311
|
# Runs a mixin.
|
302
312
|
def visit_mixin(node)
|
303
313
|
include_loop = true
|
304
|
-
|
314
|
+
if @environment.stack.frames.any? {|f| f.is_mixin? && f.name == node.name}
|
315
|
+
handle_include_loop!(node)
|
316
|
+
end
|
305
317
|
include_loop = false
|
306
318
|
|
307
319
|
@environment.stack.with_mixin(node.filename, node.line, node.name) do
|
308
|
-
|
320
|
+
mixin = @environment.mixin(node.name)
|
321
|
+
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin
|
309
322
|
|
310
323
|
if node.children.any? && !mixin.has_content
|
311
324
|
raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
|
@@ -333,10 +346,13 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
333
346
|
end
|
334
347
|
|
335
348
|
def visit_content(node)
|
336
|
-
|
349
|
+
content = @environment.content
|
350
|
+
return [] unless content
|
337
351
|
@environment.stack.with_mixin(node.filename, node.line, '@content') do
|
338
352
|
trace_node = Sass::Tree::TraceNode.from_node('@content', node)
|
339
|
-
with_environment(@environment.caller)
|
353
|
+
with_environment(@environment.caller) do
|
354
|
+
trace_node.children = content.map {|c| visit(c.dup)}.flatten
|
355
|
+
end
|
340
356
|
trace_node
|
341
357
|
end
|
342
358
|
rescue Sass::SyntaxError => e
|
@@ -362,8 +378,6 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
362
378
|
# Runs SassScript interpolation in the selector,
|
363
379
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
364
380
|
def visit_rule(node)
|
365
|
-
rule = node.rule
|
366
|
-
rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
|
367
381
|
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
|
368
382
|
node.filename, node.options[:importer], node.line)
|
369
383
|
node.parsed_rules ||= parser.parse_selector
|
@@ -455,7 +469,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
455
469
|
def handle_include_loop!(node)
|
456
470
|
msg = "An @include loop has been found:"
|
457
471
|
content_count = 0
|
458
|
-
mixins = @environment.stack.frames.select {|f| f.is_mixin?}.reverse
|
472
|
+
mixins = @environment.stack.frames.select {|f| f.is_mixin?}.reverse!.map! {|f| f.name}
|
473
|
+
mixins = mixins.select do |name|
|
459
474
|
if name == '@content'
|
460
475
|
content_count += 1
|
461
476
|
false
|