sass 3.4.19 → 3.4.20
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 +8 -8
- data/.yardopts +2 -0
- data/Rakefile +10 -3
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/extra/update_watch.rb +1 -1
- data/lib/sass/css.rb +1 -1
- data/lib/sass/engine.rb +36 -10
- data/lib/sass/exec/base.rb +1 -1
- data/lib/sass/importers/base.rb +1 -1
- data/lib/sass/plugin/configuration.rb +37 -21
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/rails.rb +1 -1
- data/lib/sass/script/functions.rb +9 -2
- data/lib/sass/script/lexer.rb +7 -0
- data/lib/sass/script/parser.rb +154 -17
- data/lib/sass/script/tree/interpolation.rb +109 -4
- data/lib/sass/script/tree/string_interpolation.rb +6 -0
- data/lib/sass/script/value/number.rb +4 -0
- data/lib/sass/script/value/string.rb +37 -1
- data/lib/sass/scss/parser.rb +33 -15
- data/lib/sass/selector/simple_sequence.rb +1 -1
- data/lib/sass/tree/css_import_node.rb +9 -1
- data/lib/sass/tree/visitors/convert.rb +1 -0
- data/lib/sass/tree/visitors/perform.rb +3 -0
- data/lib/sass/tree/visitors/to_css.rb +3 -2
- data/lib/sass/util.rb +4 -3
- data/test/sass/conversion_test.rb +8 -0
- data/test/sass/engine_test.rb +28 -0
- data/test/sass/functions_test.rb +3 -0
- data/test/sass/script_conversion_test.rb +27 -26
- data/test/sass/script_test.rb +197 -37
- data/test/sass/scss/scss_test.rb +19 -0
- data/test/sass/source_map_test.rb +130 -64
- data/test/sass/util_test.rb +11 -0
- data/test/test_helper.rb +4 -3
- metadata +2 -2
@@ -27,6 +27,17 @@ module Sass::Script::Tree
|
|
27
27
|
# generate a warning.
|
28
28
|
attr_reader :warn_for_color
|
29
29
|
|
30
|
+
# The type of interpolation deprecation for this node.
|
31
|
+
#
|
32
|
+
# This can be `:none`, indicating that the node doesn't use deprecated
|
33
|
+
# interpolation; `:immediate`, indicating that a deprecation warning should
|
34
|
+
# be emitted as soon as possible; or `:potential`, indicating that a
|
35
|
+
# deprecation warning should be emitted if the resulting string is used in a
|
36
|
+
# way that would distinguish it from a list.
|
37
|
+
#
|
38
|
+
# @return [Symbol]
|
39
|
+
attr_reader :deprecation
|
40
|
+
|
30
41
|
# Interpolation in a property is of the form `before #{mid} after`.
|
31
42
|
#
|
32
43
|
# @param before [Node] See {Interpolation#before}
|
@@ -38,15 +49,16 @@ module Sass::Script::Tree
|
|
38
49
|
# @param warn_for_color [Boolean] See {Interpolation#warn_for_color}
|
39
50
|
# @comment
|
40
51
|
# rubocop:disable ParameterLists
|
41
|
-
def initialize(before, mid, after, wb, wa,
|
52
|
+
def initialize(before, mid, after, wb, wa, opts = {})
|
42
53
|
# rubocop:enable ParameterLists
|
43
54
|
@before = before
|
44
55
|
@mid = mid
|
45
56
|
@after = after
|
46
57
|
@whitespace_before = wb
|
47
58
|
@whitespace_after = wa
|
48
|
-
@originally_text = originally_text
|
49
|
-
@warn_for_color = warn_for_color
|
59
|
+
@originally_text = opts[:originally_text] || false
|
60
|
+
@warn_for_color = opts[:warn_for_color] || false
|
61
|
+
@deprecation = opts[:deprecation] || :none
|
50
62
|
end
|
51
63
|
|
52
64
|
# @return [String] A human-readable s-expression representation of the interpolation
|
@@ -56,6 +68,8 @@ module Sass::Script::Tree
|
|
56
68
|
|
57
69
|
# @see Node#to_sass
|
58
70
|
def to_sass(opts = {})
|
71
|
+
return to_quoted_equivalent.to_sass if deprecation == :immediate
|
72
|
+
|
59
73
|
res = ""
|
60
74
|
res << @before.to_sass(opts) if @before
|
61
75
|
res << ' ' if @before && @whitespace_before
|
@@ -67,6 +81,19 @@ module Sass::Script::Tree
|
|
67
81
|
res
|
68
82
|
end
|
69
83
|
|
84
|
+
# Returns an `unquote()` expression that will evaluate to the same value as
|
85
|
+
# this interpolation.
|
86
|
+
#
|
87
|
+
# @return [Sass::Script::Tree::Node]
|
88
|
+
def to_quoted_equivalent
|
89
|
+
Funcall.new(
|
90
|
+
"unquote",
|
91
|
+
[to_string_interpolation(self)],
|
92
|
+
Sass::Util::NormalizedMap.new,
|
93
|
+
nil,
|
94
|
+
nil)
|
95
|
+
end
|
96
|
+
|
70
97
|
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
|
71
98
|
#
|
72
99
|
# @return [Array<Node>]
|
@@ -87,6 +114,49 @@ module Sass::Script::Tree
|
|
87
114
|
|
88
115
|
protected
|
89
116
|
|
117
|
+
# Converts a script node into a corresponding string interpolation
|
118
|
+
# expression.
|
119
|
+
#
|
120
|
+
# @param node_or_interp [Sass::Script::Tree::Node]
|
121
|
+
# @return [Sass::Script::Tree::StringInterpolation]
|
122
|
+
def to_string_interpolation(node_or_interp)
|
123
|
+
unless node_or_interp.is_a?(Interpolation)
|
124
|
+
node = node_or_interp
|
125
|
+
return string_literal(node.value.to_s) if node.is_a?(Literal)
|
126
|
+
if node.is_a?(StringInterpolation)
|
127
|
+
return concat(string_literal(node.quote), concat(node, string_literal(node.quote)))
|
128
|
+
end
|
129
|
+
return StringInterpolation.new(string_literal(""), node, string_literal(""))
|
130
|
+
end
|
131
|
+
|
132
|
+
interp = node_or_interp
|
133
|
+
after_string_or_interp =
|
134
|
+
if interp.after
|
135
|
+
to_string_interpolation(interp.after)
|
136
|
+
else
|
137
|
+
string_literal("")
|
138
|
+
end
|
139
|
+
if interp.after && interp.whitespace_after
|
140
|
+
after_string_or_interp = concat(string_literal(' '), after_string_or_interp)
|
141
|
+
end
|
142
|
+
|
143
|
+
mid_string_or_interp = to_string_interpolation(interp.mid)
|
144
|
+
|
145
|
+
before_string_or_interp =
|
146
|
+
if interp.before
|
147
|
+
to_string_interpolation(interp.before)
|
148
|
+
else
|
149
|
+
string_literal("")
|
150
|
+
end
|
151
|
+
if interp.before && interp.whitespace_before
|
152
|
+
before_string_or_interp = concat(before_string_or_interp, string_literal(' '))
|
153
|
+
end
|
154
|
+
|
155
|
+
concat(before_string_or_interp, concat(mid_string_or_interp, after_string_or_interp))
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
90
160
|
# Evaluates the interpolation.
|
91
161
|
#
|
92
162
|
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
@@ -112,7 +182,42 @@ MESSAGE
|
|
112
182
|
res << val.to_s(:quote => :none)
|
113
183
|
res << " " if @after && @whitespace_after
|
114
184
|
res << @after.perform(environment).to_s if @after
|
115
|
-
|
185
|
+
str = Sass::Script::Value::String.new(
|
186
|
+
res, :identifier,
|
187
|
+
(to_quoted_equivalent.to_sass if deprecation == :potential))
|
188
|
+
str.source_range = source_range
|
189
|
+
opts(str)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Concatenates two string literals or string interpolation expressions.
|
193
|
+
#
|
194
|
+
# @param string_or_interp1 [Sass::Script::Tree::Literal|Sass::Script::Tree::StringInterpolation]
|
195
|
+
# @param string_or_interp2 [Sass::Script::Tree::Literal|Sass::Script::Tree::StringInterpolation]
|
196
|
+
# @return [Sass::Script::Tree::StringInterpolation]
|
197
|
+
def concat(string_or_interp1, string_or_interp2)
|
198
|
+
if string_or_interp1.is_a?(Literal) && string_or_interp2.is_a?(Literal)
|
199
|
+
return string_literal(string_or_interp1.value.value + string_or_interp2.value.value)
|
200
|
+
end
|
201
|
+
|
202
|
+
if string_or_interp1.is_a?(Literal)
|
203
|
+
string = string_or_interp1
|
204
|
+
interp = string_or_interp2
|
205
|
+
before = string_literal(string.value.value + interp.before.value.value)
|
206
|
+
return StringInterpolation.new(before, interp.mid, interp.after)
|
207
|
+
end
|
208
|
+
|
209
|
+
StringInterpolation.new(
|
210
|
+
string_or_interp1.before,
|
211
|
+
string_or_interp1.mid,
|
212
|
+
concat(string_or_interp1.after, string_or_interp2))
|
213
|
+
end
|
214
|
+
|
215
|
+
# Returns a string literal with the given contents.
|
216
|
+
#
|
217
|
+
# @param string [String]
|
218
|
+
# @return string [Sass::Script::Tree::Literal]
|
219
|
+
def string_literal(string)
|
220
|
+
Literal.new(Sass::Script::Value::String.new(string, :string))
|
116
221
|
end
|
117
222
|
end
|
118
223
|
end
|
@@ -24,6 +24,12 @@ module Sass::Script::Tree
|
|
24
24
|
@before.value.type
|
25
25
|
end
|
26
26
|
|
27
|
+
# Returns the quote character that should be used to wrap a Sass
|
28
|
+
# representation of this interpolation.
|
29
|
+
def quote
|
30
|
+
quote_for(self) || '"'
|
31
|
+
end
|
32
|
+
|
27
33
|
# Interpolation in a string is of the form `"before #{mid} after"`,
|
28
34
|
# where `before` and `after` may include more interpolation.
|
29
35
|
#
|
@@ -289,6 +289,9 @@ module Sass::Script::Value
|
|
289
289
|
# and confusing.
|
290
290
|
str = ("%0.#{self.class.precision}f" % value).gsub(/0*$/, '') if str.include?('e')
|
291
291
|
|
292
|
+
if @options && options[:style] == :compressed
|
293
|
+
str.sub!(/^(-)?0\./, '\1.')
|
294
|
+
end
|
292
295
|
unitless? ? str : "#{str}#{unit_str}"
|
293
296
|
end
|
294
297
|
alias_method :to_sass, :inspect
|
@@ -478,6 +481,7 @@ module Sass::Script::Value
|
|
478
481
|
'cm' => Rational(1, 2.54),
|
479
482
|
'pc' => Rational(1, 6),
|
480
483
|
'mm' => Rational(1, 25.4),
|
484
|
+
'q' => Rational(1, 101.6),
|
481
485
|
'pt' => Rational(1, 72),
|
482
486
|
'px' => Rational(1, 96)
|
483
487
|
},
|
@@ -76,9 +76,13 @@ module Sass::Script::Value
|
|
76
76
|
#
|
77
77
|
# @param value [String] See \{#value}
|
78
78
|
# @param type [Symbol] See \{#type}
|
79
|
-
|
79
|
+
# @param deprecated_interp_equivalent [String?]
|
80
|
+
# If this was created via a potentially-deprecated string interpolation,
|
81
|
+
# this is the replacement expression that should be suggested to the user.
|
82
|
+
def initialize(value, type = :identifier, deprecated_interp_equivalent = nil)
|
80
83
|
super(value)
|
81
84
|
@type = type
|
85
|
+
@deprecated_interp_equivalent = deprecated_interp_equivalent
|
82
86
|
end
|
83
87
|
|
84
88
|
# @see Value#plus
|
@@ -102,6 +106,38 @@ module Sass::Script::Value
|
|
102
106
|
to_s(opts.merge(:sass => true))
|
103
107
|
end
|
104
108
|
|
109
|
+
def separator
|
110
|
+
check_deprecated_interp
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_a
|
115
|
+
check_deprecated_interp
|
116
|
+
super
|
117
|
+
end
|
118
|
+
|
119
|
+
# Prints a warning if this string was created using potentially-deprecated
|
120
|
+
# interpolation.
|
121
|
+
def check_deprecated_interp
|
122
|
+
return unless @deprecated_interp_equivalent
|
123
|
+
|
124
|
+
# rubocop:disable GlobalVars
|
125
|
+
$_sass_deprecated_interp_warnings ||= Set.new
|
126
|
+
key = [source_range.start_pos.line, source_range.file, @deprecated_interp_equivalent]
|
127
|
+
return if $_sass_deprecated_interp_warnings.include?(key)
|
128
|
+
$_sass_deprecated_interp_warnings << key
|
129
|
+
# rubocop:enable GlobalVars
|
130
|
+
|
131
|
+
location = "on line #{source_range.start_pos.line}"
|
132
|
+
location << " of #{source_range.file}" if source_range.file
|
133
|
+
Sass::Util.sass_warn <<WARNING
|
134
|
+
DEPRECATION WARNING #{location}: \#{} interpolation near operators will be simplified
|
135
|
+
in a future version of Sass. To preserve the current behavior, use quotes:
|
136
|
+
|
137
|
+
#{@deprecated_interp_equivalent}
|
138
|
+
WARNING
|
139
|
+
end
|
140
|
+
|
105
141
|
def inspect
|
106
142
|
String.quote(value)
|
107
143
|
end
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -55,6 +55,15 @@ module Sass
|
|
55
55
|
interp_ident
|
56
56
|
end
|
57
57
|
|
58
|
+
# Parses a supports clause for an @import directive
|
59
|
+
def parse_supports_clause
|
60
|
+
init_scanner!
|
61
|
+
ss
|
62
|
+
clause = supports_clause
|
63
|
+
ss
|
64
|
+
clause
|
65
|
+
end
|
66
|
+
|
58
67
|
# Parses a media query list.
|
59
68
|
#
|
60
69
|
# @return [Sass::Media::QueryList] The parsed query list
|
@@ -150,20 +159,16 @@ module Sass
|
|
150
159
|
silent = text =~ %r{\A//}
|
151
160
|
loud = !silent && text =~ %r{\A/[/*]!}
|
152
161
|
line = @line - text.count("\n")
|
162
|
+
comment_start = @scanner.pos - text.length
|
163
|
+
index_before_line = @scanner.string.rindex("\n", comment_start) || -1
|
164
|
+
offset = comment_start - index_before_line
|
153
165
|
|
154
166
|
if silent
|
155
167
|
value = [text.sub(%r{\A\s*//}, '/*').gsub(%r{^\s*//}, ' *') + ' */']
|
156
168
|
else
|
157
|
-
value = Sass::Engine.parse_interp(
|
158
|
-
|
159
|
-
|
160
|
-
last_line_before_comment =
|
161
|
-
if newline_before_comment
|
162
|
-
@scanner.string[newline_before_comment + 1...@scanner.pos - text.length]
|
163
|
-
else
|
164
|
-
@scanner.string[0...@scanner.pos - text.length]
|
165
|
-
end
|
166
|
-
value.unshift(last_line_before_comment.gsub(/[^\s]/, ' '))
|
169
|
+
value = Sass::Engine.parse_interp(text, line, offset, :filename => @filename)
|
170
|
+
line_before_comment = @scanner.string[index_before_line + 1...comment_start]
|
171
|
+
value.unshift(line_before_comment.gsub(/[^\s]/, ' '))
|
167
172
|
end
|
168
173
|
|
169
174
|
type = if silent
|
@@ -173,8 +178,8 @@ module Sass
|
|
173
178
|
else
|
174
179
|
:normal
|
175
180
|
end
|
176
|
-
|
177
|
-
comment
|
181
|
+
start_pos = Sass::Source::Position.new(line, offset)
|
182
|
+
comment = node(Sass::Tree::CommentNode.new(value, type), start_pos)
|
178
183
|
node << comment
|
179
184
|
end
|
180
185
|
|
@@ -379,16 +384,20 @@ module Sass
|
|
379
384
|
if uri
|
380
385
|
str = sass_script(:parse_string)
|
381
386
|
ss
|
387
|
+
supports = supports_clause
|
388
|
+
ss
|
382
389
|
media = media_query_list
|
383
390
|
ss
|
384
|
-
return node(Tree::CssImportNode.new(str, media.to_a), start_pos)
|
391
|
+
return node(Tree::CssImportNode.new(str, media.to_a, supports), start_pos)
|
385
392
|
end
|
386
393
|
ss
|
387
394
|
|
395
|
+
supports = supports_clause
|
396
|
+
ss
|
388
397
|
media = media_query_list
|
389
|
-
if str =~ %r{^(https?:)?//} || media || use_css_import?
|
398
|
+
if str =~ %r{^(https?:)?//} || media || supports || use_css_import?
|
390
399
|
return node(Sass::Tree::CssImportNode.new(
|
391
|
-
Sass::Script::Value::String.quote(str), media.to_a), start_pos)
|
400
|
+
Sass::Script::Value::String.quote(str), media.to_a, supports), start_pos)
|
392
401
|
end
|
393
402
|
|
394
403
|
node(Sass::Tree::ImportNode.new(str.strip), start_pos)
|
@@ -548,6 +557,15 @@ module Sass
|
|
548
557
|
node(node, start_pos)
|
549
558
|
end
|
550
559
|
|
560
|
+
def supports_clause
|
561
|
+
return unless tok(/supports\(/i)
|
562
|
+
ss
|
563
|
+
supports = supports_condition
|
564
|
+
ss
|
565
|
+
tok!(/\)/)
|
566
|
+
supports
|
567
|
+
end
|
568
|
+
|
551
569
|
def supports_condition
|
552
570
|
supports_negation || supports_operator || supports_interpolation
|
553
571
|
end
|
@@ -166,7 +166,7 @@ module Sass
|
|
166
166
|
extended.members.reject! {|seq| seq.has_placeholder?}
|
167
167
|
|
168
168
|
# For `:not()`, we usually want to get rid of any complex
|
169
|
-
# selectors
|
169
|
+
# selectors because that will cause the selector to fail to
|
170
170
|
# parse on all browsers at time of writing. We can keep them
|
171
171
|
# if either the original selector had a complex selector, or
|
172
172
|
# the result of extending has only complex selectors,
|
@@ -15,6 +15,11 @@ module Sass::Tree
|
|
15
15
|
# @return [String]
|
16
16
|
attr_accessor :resolved_uri
|
17
17
|
|
18
|
+
# The supports condition for this import.
|
19
|
+
#
|
20
|
+
# @return [Sass::Supports::Condition]
|
21
|
+
attr_accessor :supports_condition
|
22
|
+
|
18
23
|
# The media query for this rule, interspersed with
|
19
24
|
# {Sass::Script::Tree::Node}s representing `#{}`-interpolation. Any adjacent
|
20
25
|
# strings will be merged together.
|
@@ -30,9 +35,11 @@ module Sass::Tree
|
|
30
35
|
|
31
36
|
# @param uri [String, Sass::Script::Tree::Node] See \{#uri}
|
32
37
|
# @param query [Array<String, Sass::Script::Tree::Node>] See \{#query}
|
33
|
-
|
38
|
+
# @param supports_condition [Sass::Supports::Condition] See \{#supports_condition}
|
39
|
+
def initialize(uri, query = [], supports_condition = nil)
|
34
40
|
@uri = uri
|
35
41
|
@query = query
|
42
|
+
@supports_condition = supports_condition
|
36
43
|
super('')
|
37
44
|
end
|
38
45
|
|
@@ -52,6 +59,7 @@ module Sass::Tree
|
|
52
59
|
@resolved_value ||=
|
53
60
|
begin
|
54
61
|
str = "@import #{resolved_uri}"
|
62
|
+
str << " supports(#{supports_condition.to_css})" if supports_condition
|
55
63
|
str << " #{resolved_query.to_css}" if resolved_query
|
56
64
|
str
|
57
65
|
end
|
@@ -163,6 +163,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
163
163
|
else
|
164
164
|
str = "#{tab_str}@import #{node.uri}"
|
165
165
|
end
|
166
|
+
str << " supports(#{node.supports_condition.to_src(@options)})" if node.supports_condition
|
166
167
|
str << " #{interp_to_src(node.query)}" unless node.query.empty?
|
167
168
|
"#{str}#{semi}\n"
|
168
169
|
end
|
@@ -159,8 +159,9 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
159
159
|
def visit_comment(node)
|
160
160
|
return if node.invisible?
|
161
161
|
spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max)
|
162
|
+
output(spaces)
|
162
163
|
|
163
|
-
content = node.resolved_value.
|
164
|
+
content = node.resolved_value.split("\n").join("\n" + spaces)
|
164
165
|
if node.type == :silent
|
165
166
|
content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"}
|
166
167
|
end
|
@@ -297,7 +298,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
297
298
|
rule_part = seq.to_s
|
298
299
|
if node.style == :compressed
|
299
300
|
rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
|
300
|
-
rule_part.gsub!(/\s*([
|
301
|
+
rule_part.gsub!(/\s*([-,+>])\s*/m, '\1')
|
301
302
|
rule_part.strip!
|
302
303
|
end
|
303
304
|
rule_part
|
data/lib/sass/util.rb
CHANGED
@@ -144,9 +144,10 @@ module Sass
|
|
144
144
|
# @param value [Numeric]
|
145
145
|
# @return [Numeric]
|
146
146
|
def round(value)
|
147
|
-
# If the number is within epsilon of X.5, round up
|
148
|
-
|
149
|
-
value.round
|
147
|
+
# If the number is within epsilon of X.5, round up (or down for negative
|
148
|
+
# numbers).
|
149
|
+
return value.round if (value % 1) - 0.5 <= -1 * Script::Value::Number.epsilon
|
150
|
+
value > 0 ? value.ceil : value.floor
|
150
151
|
end
|
151
152
|
|
152
153
|
# Concatenates all strings that are adjacent in an array,
|
@@ -2112,6 +2112,14 @@ foo {
|
|
2112
2112
|
SCSS
|
2113
2113
|
end
|
2114
2114
|
|
2115
|
+
def test_import_with_supports_clause
|
2116
|
+
assert_renders(<<'SASS', <<'SCSS')
|
2117
|
+
@import url("fallback-layout.css") supports(not (display: #{$display-type}))
|
2118
|
+
SASS
|
2119
|
+
@import url("fallback-layout.css") supports(not (display: #{$display-type}));
|
2120
|
+
SCSS
|
2121
|
+
end
|
2122
|
+
|
2115
2123
|
private
|
2116
2124
|
|
2117
2125
|
def assert_sass_to_sass(sass, options = {})
|