sass 3.3.0 → 3.4.25
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/.yardopts +3 -1
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +1 -1
- data/README.md +76 -62
- data/Rakefile +104 -24
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/extra/sass-spec-ref.sh +32 -0
- data/extra/update_watch.rb +1 -1
- data/lib/sass/cache_stores/filesystem.rb +9 -5
- data/lib/sass/cache_stores/memory.rb +4 -5
- data/lib/sass/callbacks.rb +2 -2
- data/lib/sass/css.rb +12 -13
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +106 -70
- data/lib/sass/environment.rb +39 -19
- data/lib/sass/error.rb +17 -20
- data/lib/sass/exec/base.rb +199 -0
- data/lib/sass/exec/sass_convert.rb +283 -0
- data/lib/sass/exec/sass_scss.rb +440 -0
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/features.rb +9 -2
- data/lib/sass/importers/base.rb +8 -3
- data/lib/sass/importers/filesystem.rb +30 -38
- data/lib/sass/logger/base.rb +8 -2
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger.rb +8 -3
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +224 -90
- data/lib/sass/plugin/configuration.rb +38 -22
- data/lib/sass/plugin/merb.rb +2 -2
- data/lib/sass/plugin/rack.rb +3 -3
- data/lib/sass/plugin/rails.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +4 -4
- data/lib/sass/plugin.rb +6 -5
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/css_parser.rb +2 -3
- data/lib/sass/script/css_variable_warning.rb +52 -0
- data/lib/sass/script/functions.rb +739 -318
- data/lib/sass/script/lexer.rb +134 -54
- data/lib/sass/script/parser.rb +252 -56
- data/lib/sass/script/tree/funcall.rb +13 -6
- data/lib/sass/script/tree/interpolation.rb +127 -4
- data/lib/sass/script/tree/list_literal.rb +31 -4
- data/lib/sass/script/tree/literal.rb +4 -0
- data/lib/sass/script/tree/node.rb +21 -3
- data/lib/sass/script/tree/operation.rb +54 -1
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +59 -38
- data/lib/sass/script/tree/variable.rb +1 -1
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/value/base.rb +17 -14
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +78 -42
- data/lib/sass/script/value/helpers.rb +119 -2
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/map.rb +1 -1
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +112 -31
- data/lib/sass/script/value/string.rb +102 -13
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/script.rb +3 -3
- data/lib/sass/scss/css_parser.rb +24 -4
- data/lib/sass/scss/parser.rb +290 -383
- data/lib/sass/scss/rx.rb +17 -9
- data/lib/sass/scss/static_parser.rb +306 -4
- data/lib/sass/scss.rb +0 -2
- data/lib/sass/selector/abstract_sequence.rb +35 -18
- data/lib/sass/selector/comma_sequence.rb +114 -19
- data/lib/sass/selector/pseudo.rb +266 -0
- data/lib/sass/selector/sequence.rb +146 -40
- data/lib/sass/selector/simple.rb +22 -33
- data/lib/sass/selector/simple_sequence.rb +122 -39
- data/lib/sass/selector.rb +57 -197
- data/lib/sass/shared.rb +2 -2
- data/lib/sass/source/map.rb +31 -14
- data/lib/sass/source/position.rb +4 -4
- data/lib/sass/stack.rb +2 -8
- data/lib/sass/supports.rb +10 -13
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +1 -1
- data/lib/sass/tree/css_import_node.rb +9 -1
- data/lib/sass/tree/directive_node.rb +8 -2
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +9 -0
- data/lib/sass/tree/import_node.rb +6 -5
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/node.rb +5 -3
- data/lib/sass/tree/prop_node.rb +6 -7
- data/lib/sass/tree/rule_node.rb +26 -11
- data/lib/sass/tree/visitors/check_nesting.rb +56 -32
- data/lib/sass/tree/visitors/convert.rb +59 -44
- data/lib/sass/tree/visitors/cssize.rb +34 -30
- data/lib/sass/tree/visitors/deep_copy.rb +6 -1
- data/lib/sass/tree/visitors/extend.rb +15 -13
- data/lib/sass/tree/visitors/perform.rb +87 -50
- data/lib/sass/tree/visitors/set_options.rb +15 -1
- data/lib/sass/tree/visitors/to_css.rb +72 -43
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/normalized_map.rb +0 -1
- data/lib/sass/util/subset_map.rb +2 -3
- data/lib/sass/util.rb +334 -154
- data/lib/sass/version.rb +7 -7
- data/lib/sass.rb +10 -8
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +24 -11
- data/test/sass/conversion_test.rb +241 -50
- data/test/sass/css2sass_test.rb +73 -5
- data/test/sass/css_variable_test.rb +132 -0
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +343 -260
- data/test/sass/exec_test.rb +12 -2
- data/test/sass/extend_test.rb +333 -44
- data/test/sass/functions_test.rb +353 -260
- data/test/sass/importer_test.rb +40 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/more_templates/more1.sass +10 -10
- data/test/sass/more_templates/more_import.sass +2 -2
- data/test/sass/plugin_test.rb +24 -21
- 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 +5 -5
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +71 -39
- data/test/sass/script_test.rb +714 -123
- data/test/sass/scss/css_test.rb +213 -30
- data/test/sass/scss/rx_test.rb +8 -4
- data/test/sass/scss/scss_test.rb +766 -22
- data/test/sass/source_map_test.rb +263 -95
- data/test/sass/superselector_test.rb +210 -0
- data/test/sass/templates/_partial.sass +1 -1
- data/test/sass/templates/basic.sass +10 -10
- data/test/sass/templates/bork1.sass +1 -1
- data/test/sass/templates/bork5.sass +1 -1
- data/test/sass/templates/compact.sass +10 -10
- data/test/sass/templates/complex.sass +187 -187
- data/test/sass/templates/compressed.sass +10 -10
- data/test/sass/templates/expanded.sass +10 -10
- data/test/sass/templates/import.sass +2 -2
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/mixins.sass +22 -22
- data/test/sass/templates/multiline.sass +4 -4
- data/test/sass/templates/nested.sass +13 -13
- data/test/sass/templates/parent_ref.sass +12 -12
- data/test/sass/templates/script.sass +70 -70
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
- data/test/sass/templates/subdir/subdir.sass +3 -3
- data/test/sass/templates/units.sass +10 -10
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
- data/test/sass/util/normalized_map_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +46 -45
- data/test/sass/value_helpers_test.rb +5 -7
- data/test/sass-spec.yml +3 -0
- data/test/test_helper.rb +7 -6
- data/vendor/listen/CHANGELOG.md +1 -228
- data/vendor/listen/Gemfile +5 -15
- data/vendor/listen/README.md +111 -77
- data/vendor/listen/Rakefile +0 -42
- data/vendor/listen/lib/listen/adapter.rb +195 -82
- data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
- data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
- data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
- data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
- data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
- data/vendor/listen/lib/listen/directory_record.rb +96 -61
- data/vendor/listen/lib/listen/listener.rb +135 -37
- data/vendor/listen/lib/listen/turnstile.rb +9 -5
- data/vendor/listen/lib/listen/version.rb +1 -1
- data/vendor/listen/lib/listen.rb +33 -19
- data/vendor/listen/listen.gemspec +6 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
- data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
- data/vendor/listen/spec/listen/listener_spec.rb +128 -39
- data/vendor/listen/spec/listen_spec.rb +15 -21
- data/vendor/listen/spec/spec_helper.rb +4 -0
- data/vendor/listen/spec/support/adapter_helper.rb +52 -15
- data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
- data/vendor/listen/spec/support/listeners_helper.rb +30 -7
- metadata +310 -300
- data/CONTRIBUTING +0 -3
- data/ext/mkrf_conf.rb +0 -27
- data/lib/sass/script/value/deprecated_false.rb +0 -55
- data/lib/sass/scss/script_lexer.rb +0 -15
- data/lib/sass/scss/script_parser.rb +0 -25
- data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
- data/vendor/listen/lib/listen/multi_listener.rb +0 -143
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
- data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -23,6 +23,21 @@ module Sass::Script::Tree
|
|
23
23
|
# SassScript.
|
24
24
|
attr_reader :originally_text
|
25
25
|
|
26
|
+
# @return [Boolean] Whether a color value passed to the interpolation should
|
27
|
+
# generate a warning.
|
28
|
+
attr_reader :warn_for_color
|
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
|
+
|
26
41
|
# Interpolation in a property is of the form `before #{mid} after`.
|
27
42
|
#
|
28
43
|
# @param before [Node] See {Interpolation#before}
|
@@ -31,16 +46,19 @@ module Sass::Script::Tree
|
|
31
46
|
# @param wb [Boolean] See {Interpolation#whitespace_before}
|
32
47
|
# @param wa [Boolean] See {Interpolation#whitespace_after}
|
33
48
|
# @param originally_text [Boolean] See {Interpolation#originally_text}
|
49
|
+
# @param warn_for_color [Boolean] See {Interpolation#warn_for_color}
|
34
50
|
# @comment
|
35
51
|
# rubocop:disable ParameterLists
|
36
|
-
def initialize(before, mid, after, wb, wa,
|
52
|
+
def initialize(before, mid, after, wb, wa, opts = {})
|
37
53
|
# rubocop:enable ParameterLists
|
38
54
|
@before = before
|
39
55
|
@mid = mid
|
40
56
|
@after = after
|
41
57
|
@whitespace_before = wb
|
42
58
|
@whitespace_after = wa
|
43
|
-
@originally_text = originally_text
|
59
|
+
@originally_text = opts[:originally_text] || false
|
60
|
+
@warn_for_color = opts[:warn_for_color] || false
|
61
|
+
@deprecation = opts[:deprecation] || :none
|
44
62
|
end
|
45
63
|
|
46
64
|
# @return [String] A human-readable s-expression representation of the interpolation
|
@@ -50,6 +68,8 @@ module Sass::Script::Tree
|
|
50
68
|
|
51
69
|
# @see Node#to_sass
|
52
70
|
def to_sass(opts = {})
|
71
|
+
return to_quoted_equivalent.to_sass if deprecation == :immediate
|
72
|
+
|
53
73
|
res = ""
|
54
74
|
res << @before.to_sass(opts) if @before
|
55
75
|
res << ' ' if @before && @whitespace_before
|
@@ -61,6 +81,19 @@ module Sass::Script::Tree
|
|
61
81
|
res
|
62
82
|
end
|
63
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
|
+
|
64
97
|
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
|
65
98
|
#
|
66
99
|
# @return [Array<Node>]
|
@@ -81,6 +114,49 @@ module Sass::Script::Tree
|
|
81
114
|
|
82
115
|
protected
|
83
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
|
+
|
84
160
|
# Evaluates the interpolation.
|
85
161
|
#
|
86
162
|
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
@@ -90,11 +166,58 @@ module Sass::Script::Tree
|
|
90
166
|
res = ""
|
91
167
|
res << @before.perform(environment).to_s if @before
|
92
168
|
res << " " if @before && @whitespace_before
|
169
|
+
|
93
170
|
val = @mid.perform(environment)
|
94
|
-
|
171
|
+
if @warn_for_color && val.is_a?(Sass::Script::Value::Color) && val.name
|
172
|
+
alternative = Operation.new(Sass::Script::Value::String.new("", :string), @mid, :plus)
|
173
|
+
Sass::Util.sass_warn <<MESSAGE
|
174
|
+
WARNING on line #{line}, column #{source_range.start_pos.offset}#{" of #{filename}" if filename}:
|
175
|
+
You probably don't mean to use the color value `#{val}' in interpolation here.
|
176
|
+
It may end up represented as #{val.inspect}, which will likely produce invalid CSS.
|
177
|
+
Always quote color names when using them as strings (for example, "#{val}").
|
178
|
+
If you really want to use the color value here, use `#{alternative.to_sass}'.
|
179
|
+
MESSAGE
|
180
|
+
end
|
181
|
+
|
182
|
+
res << val.to_s(:quote => :none)
|
95
183
|
res << " " if @after && @whitespace_after
|
96
184
|
res << @after.perform(environment).to_s if @after
|
97
|
-
|
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))
|
98
221
|
end
|
99
222
|
end
|
100
223
|
end
|
@@ -28,11 +28,8 @@ module Sass::Script::Tree
|
|
28
28
|
# @see Value#to_sass
|
29
29
|
def to_sass(opts = {})
|
30
30
|
return "()" if elements.empty?
|
31
|
-
precedence = Sass::Script::Parser.precedence_of(separator)
|
32
31
|
members = elements.map do |v|
|
33
|
-
if
|
34
|
-
separator == :space && v.is_a?(UnaryOperation) &&
|
35
|
-
(v.operator == :minus || v.operator == :plus)
|
32
|
+
if element_needs_parens?(v)
|
36
33
|
"(#{v.to_sass(opts)})"
|
37
34
|
else
|
38
35
|
v.to_sass(opts)
|
@@ -55,6 +52,10 @@ module Sass::Script::Tree
|
|
55
52
|
"(#{elements.map {|e| e.inspect}.join(separator == :space ? ' ' : ', ')})"
|
56
53
|
end
|
57
54
|
|
55
|
+
def force_division!
|
56
|
+
# Do nothing. Lists prevent division propagation.
|
57
|
+
end
|
58
|
+
|
58
59
|
protected
|
59
60
|
|
60
61
|
def _perform(environment)
|
@@ -68,6 +69,32 @@ module Sass::Script::Tree
|
|
68
69
|
|
69
70
|
private
|
70
71
|
|
72
|
+
# Returns whether an element in the list should be wrapped in parentheses
|
73
|
+
# when serialized to Sass.
|
74
|
+
def element_needs_parens?(element)
|
75
|
+
if element.is_a?(ListLiteral)
|
76
|
+
return Sass::Script::Parser.precedence_of(element.separator) <=
|
77
|
+
Sass::Script::Parser.precedence_of(separator)
|
78
|
+
end
|
79
|
+
|
80
|
+
return false unless separator == :space
|
81
|
+
|
82
|
+
if element.is_a?(UnaryOperation)
|
83
|
+
return element.operator == :minus || element.operator == :plus
|
84
|
+
end
|
85
|
+
|
86
|
+
return false unless element.is_a?(Operation)
|
87
|
+
return true unless element.operator == :div
|
88
|
+
!(is_literal_number?(element.operand1) && is_literal_number?(element.operand2))
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns whether a value is a number literal that shouldn't be divided.
|
92
|
+
def is_literal_number?(value)
|
93
|
+
value.is_a?(Literal) &&
|
94
|
+
value.value.is_a?((Sass::Script::Value::Number)) &&
|
95
|
+
!value.value.original.nil?
|
96
|
+
end
|
97
|
+
|
71
98
|
def sep_str(opts = options)
|
72
99
|
return ' ' if separator == :space
|
73
100
|
return ',' if opts && opts[:style] == :compressed
|
@@ -10,7 +10,7 @@ module Sass::Script::Tree
|
|
10
10
|
|
11
11
|
# The line of the document on which this node appeared.
|
12
12
|
#
|
13
|
-
# @return [
|
13
|
+
# @return [Integer]
|
14
14
|
attr_accessor :line
|
15
15
|
|
16
16
|
# The source range in the document on which this node appeared.
|
@@ -23,9 +23,17 @@ module Sass::Script::Tree
|
|
23
23
|
# @return [String]
|
24
24
|
attr_accessor :filename
|
25
25
|
|
26
|
+
# The warning that this node should emit if it executes in a way that's not
|
27
|
+
# safe for a CSS variable value.
|
28
|
+
#
|
29
|
+
# This is `nil` if this is not in a CSS variable value.
|
30
|
+
#
|
31
|
+
# @return [Sass::Script::CssVariableWarning]
|
32
|
+
attr_accessor :css_variable_warning
|
33
|
+
|
26
34
|
# Sets the options hash for this node,
|
27
35
|
# as well as for all child nodes.
|
28
|
-
# See {file:SASS_REFERENCE.md#
|
36
|
+
# See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
29
37
|
#
|
30
38
|
# @param options [{Symbol => Object}] The options
|
31
39
|
def options=(options)
|
@@ -62,6 +70,10 @@ module Sass::Script::Tree
|
|
62
70
|
|
63
71
|
# Returns the text of this SassScript expression.
|
64
72
|
#
|
73
|
+
# @options opts :quote [String]
|
74
|
+
# The preferred quote style for quoted strings. If `:none`, strings are
|
75
|
+
# always emitted unquoted.
|
76
|
+
#
|
65
77
|
# @return [String]
|
66
78
|
def to_sass(opts = {})
|
67
79
|
Sass::Util.abstract(self)
|
@@ -75,12 +87,18 @@ module Sass::Script::Tree
|
|
75
87
|
Sass::Util.abstract(self)
|
76
88
|
end
|
77
89
|
|
90
|
+
# Forces any division operations with number literals in this expression to
|
91
|
+
# do real division, rather than returning strings.
|
92
|
+
def force_division!
|
93
|
+
children.each {|c| c.force_division!}
|
94
|
+
end
|
95
|
+
|
78
96
|
protected
|
79
97
|
|
80
98
|
# Converts underscores to dashes if the :dasherize option is set.
|
81
99
|
def dasherize(s, opts)
|
82
100
|
if opts[:dasherize]
|
83
|
-
s.
|
101
|
+
s.tr('_', '-')
|
84
102
|
else
|
85
103
|
s
|
86
104
|
end
|
@@ -2,6 +2,9 @@ module Sass::Script::Tree
|
|
2
2
|
# A SassScript parse node representing a binary operation,
|
3
3
|
# such as `$a + $b` or `"foo" + 1`.
|
4
4
|
class Operation < Node
|
5
|
+
@@color_arithmetic_deprecation = Sass::Deprecation.new
|
6
|
+
@@unitless_equals_deprecation = Sass::Deprecation.new
|
7
|
+
|
5
8
|
attr_reader :operand1
|
6
9
|
attr_reader :operand2
|
7
10
|
attr_reader :operator
|
@@ -78,16 +81,66 @@ module Sass::Script::Tree
|
|
78
81
|
"Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
|
79
82
|
end
|
80
83
|
|
84
|
+
if css_variable_warning && @operator == :div &&
|
85
|
+
!(value1.is_a?(Sass::Script::Value::Number) && value1.original &&
|
86
|
+
value2.is_a?(Sass::Script::Value::Number) && value2.original) &&
|
87
|
+
!(value1.is_a?(Sass::Script::Value::String) && value2.is_a?(Sass::Script::Value::String))
|
88
|
+
css_variable_warning.warn!
|
89
|
+
end
|
90
|
+
|
81
91
|
begin
|
82
|
-
opts(value1.send(@operator, value2))
|
92
|
+
result = opts(value1.send(@operator, value2))
|
83
93
|
rescue NoMethodError => e
|
84
94
|
raise e unless e.name.to_s == @operator.to_s
|
85
95
|
raise Sass::SyntaxError.new("Undefined operation: \"#{value1} #{@operator} #{value2}\".")
|
86
96
|
end
|
97
|
+
|
98
|
+
warn_for_color_arithmetic(value1, value2)
|
99
|
+
warn_for_unitless_equals(value1, value2, result)
|
100
|
+
|
101
|
+
result
|
87
102
|
end
|
88
103
|
|
89
104
|
private
|
90
105
|
|
106
|
+
def warn_for_color_arithmetic(value1, value2)
|
107
|
+
return unless @operator == :plus || @operator == :times || @operator == :minus ||
|
108
|
+
@operator == :div || @operator == :mod
|
109
|
+
|
110
|
+
if value1.is_a?(Sass::Script::Value::Number)
|
111
|
+
return unless value2.is_a?(Sass::Script::Value::Color)
|
112
|
+
elsif value1.is_a?(Sass::Script::Value::Color)
|
113
|
+
return unless value2.is_a?(Sass::Script::Value::Color) || value2.is_a?(Sass::Script::Value::Number)
|
114
|
+
else
|
115
|
+
return
|
116
|
+
end
|
117
|
+
|
118
|
+
@@color_arithmetic_deprecation.warn(filename, line, <<WARNING)
|
119
|
+
The operation `#{value1} #{@operator} #{value2}` is deprecated and will be an error in future versions.
|
120
|
+
Consider using Sass's color functions instead.
|
121
|
+
http://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions
|
122
|
+
WARNING
|
123
|
+
end
|
124
|
+
|
125
|
+
def warn_for_unitless_equals(value1, value2, result)
|
126
|
+
return unless @operator == :eq || @operator == :neq
|
127
|
+
return unless value1.is_a?(Sass::Script::Value::Number)
|
128
|
+
return unless value2.is_a?(Sass::Script::Value::Number)
|
129
|
+
return unless value1.unitless? != value2.unitless?
|
130
|
+
return unless result == (if @operator == :eq
|
131
|
+
Sass::Script::Value::Bool::TRUE
|
132
|
+
else
|
133
|
+
Sass::Script::Value::Bool::FALSE
|
134
|
+
end)
|
135
|
+
|
136
|
+
operation = "#{value1.to_sass} #{@operator == :eq ? '==' : '!='} #{value2.to_sass}"
|
137
|
+
future_value = @operator == :neq
|
138
|
+
@@unitless_equals_deprecation.warn(filename, line, <<WARNING)
|
139
|
+
The result of `#{operation}` will be `#{future_value}` in future releases of Sass.
|
140
|
+
Unitless numbers will no longer be equal to the same numbers with units.
|
141
|
+
WARNING
|
142
|
+
end
|
143
|
+
|
91
144
|
def operand_to_sass(op, side, opts)
|
92
145
|
return "(#{op.to_sass(opts)})" if op.is_a?(Sass::Script::Tree::ListLiteral)
|
93
146
|
return op.to_sass(opts) unless op.is_a?(Operation)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# A SassScript node that will resolve to the current selector.
|
3
|
+
class Selector < Node
|
4
|
+
def initialize; end
|
5
|
+
|
6
|
+
def children
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_sass(opts = {})
|
11
|
+
'&'
|
12
|
+
end
|
13
|
+
|
14
|
+
def deep_copy
|
15
|
+
dup
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def _perform(environment)
|
21
|
+
selector = environment.selector
|
22
|
+
return opts(Sass::Script::Value::Null.new) unless selector
|
23
|
+
opts(selector.to_sass_script)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -3,12 +3,39 @@ module Sass::Script::Tree
|
|
3
3
|
#
|
4
4
|
# @see Interpolation
|
5
5
|
class StringInterpolation < Node
|
6
|
+
# @return [Literal] The string literal before this interpolation.
|
7
|
+
attr_reader :before
|
8
|
+
|
9
|
+
# @return [Node] The SassScript within the interpolation
|
10
|
+
attr_reader :mid
|
11
|
+
|
12
|
+
# @return [StringInterpolation, Literal]
|
13
|
+
# The string literal or string interpolation before this interpolation.
|
14
|
+
attr_reader :after
|
15
|
+
|
16
|
+
# Whether this is a CSS string or a CSS identifier. The difference is that
|
17
|
+
# strings are written with double-quotes, while identifiers aren't.
|
18
|
+
#
|
19
|
+
# String interpolations are only ever identifiers if they're quote-like
|
20
|
+
# functions such as `url()`.
|
21
|
+
#
|
22
|
+
# @return [Symbol] `:string` or `:identifier`
|
23
|
+
def type
|
24
|
+
@before.value.type
|
25
|
+
end
|
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
|
+
|
6
33
|
# Interpolation in a string is of the form `"before #{mid} after"`,
|
7
34
|
# where `before` and `after` may include more interpolation.
|
8
35
|
#
|
9
|
-
# @param before [
|
10
|
-
# @param mid [Node]
|
11
|
-
# @param after [
|
36
|
+
# @param before [StringInterpolation, Literal] See {StringInterpolation#before}
|
37
|
+
# @param mid [Node] See {StringInterpolation#mid}
|
38
|
+
# @param after [Literal] See {StringInterpolation#after}
|
12
39
|
def initialize(before, mid, after)
|
13
40
|
@before = before
|
14
41
|
@mid = mid
|
@@ -22,32 +49,15 @@ module Sass::Script::Tree
|
|
22
49
|
|
23
50
|
# @see Node#to_sass
|
24
51
|
def to_sass(opts = {})
|
25
|
-
|
26
|
-
|
27
|
-
before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
|
28
|
-
after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
|
29
|
-
unquote = before_unquote || after_unquote ||
|
30
|
-
(before_quote_char && !after_quote_char && !after_str.empty?) ||
|
31
|
-
(!before_quote_char && after_quote_char && !before_str.empty?)
|
32
|
-
quote_char =
|
33
|
-
if before_quote_char && after_quote_char && before_quote_char != after_quote_char
|
34
|
-
before_str.gsub!("\\'", "'")
|
35
|
-
before_str.gsub!('"', "\\\"")
|
36
|
-
after_str.gsub!("\\'", "'")
|
37
|
-
after_str.gsub!('"', "\\\"")
|
38
|
-
'"'
|
39
|
-
else
|
40
|
-
before_quote_char || after_quote_char
|
41
|
-
end
|
52
|
+
quote = type == :string ? opts[:quote] || quote_for(self) || '"' : :none
|
53
|
+
opts = opts.merge(:quote => quote)
|
42
54
|
|
43
55
|
res = ""
|
44
|
-
res <<
|
45
|
-
res <<
|
46
|
-
res <<
|
47
|
-
res <<
|
48
|
-
res <<
|
49
|
-
res << quote_char if quote_char
|
50
|
-
res << ')' if unquote
|
56
|
+
res << quote if quote != :none
|
57
|
+
res << _to_sass(before, opts)
|
58
|
+
res << '#{' << @mid.to_sass(opts.merge(:quote => nil)) << '}'
|
59
|
+
res << _to_sass(after, opts)
|
60
|
+
res << quote if quote != :none
|
51
61
|
res
|
52
62
|
end
|
53
63
|
|
@@ -81,24 +91,35 @@ module Sass::Script::Tree
|
|
81
91
|
before = @before.perform(environment)
|
82
92
|
res << before.value
|
83
93
|
mid = @mid.perform(environment)
|
84
|
-
res << (mid.is_a?(Sass::Script::Value::String) ? mid.value : mid.to_s)
|
94
|
+
res << (mid.is_a?(Sass::Script::Value::String) ? mid.value : mid.to_s(:quote => :none))
|
85
95
|
res << @after.perform(environment).value
|
86
96
|
opts(Sass::Script::Value::String.new(res, before.type))
|
87
97
|
end
|
88
98
|
|
89
99
|
private
|
90
100
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
return
|
99
|
-
|
100
|
-
return
|
101
|
+
def _to_sass(string_or_interp, opts)
|
102
|
+
result = string_or_interp.to_sass(opts)
|
103
|
+
opts[:quote] == :none ? result : result.slice(1...-1)
|
104
|
+
end
|
105
|
+
|
106
|
+
def quote_for(string_or_interp)
|
107
|
+
if string_or_interp.is_a?(Sass::Script::Tree::Literal)
|
108
|
+
return nil if string_or_interp.value.value.empty?
|
109
|
+
return '"' if string_or_interp.value.value.include?("'")
|
110
|
+
return "'" if string_or_interp.value.value.include?('"')
|
111
|
+
return nil
|
101
112
|
end
|
113
|
+
|
114
|
+
# Double-quotes take precedence over single quotes.
|
115
|
+
before_quote = quote_for(string_or_interp.before)
|
116
|
+
return '"' if before_quote == '"'
|
117
|
+
after_quote = quote_for(string_or_interp.after)
|
118
|
+
return '"' if after_quote == '"'
|
119
|
+
|
120
|
+
# Returns "'" if either or both insist on single quotes, and nil
|
121
|
+
# otherwise.
|
122
|
+
before_quote || after_quote
|
102
123
|
end
|
103
124
|
end
|
104
125
|
end
|
data/lib/sass/script/tree.rb
CHANGED
@@ -20,12 +20,14 @@ module Sass::Script::Value
|
|
20
20
|
#
|
21
21
|
# @param value [Object] The object for \{#value}
|
22
22
|
def initialize(value = nil)
|
23
|
-
|
23
|
+
value.freeze unless value.nil? || value == true || value == false
|
24
|
+
@value = value
|
25
|
+
@options = nil
|
24
26
|
end
|
25
27
|
|
26
28
|
# Sets the options hash for this node,
|
27
29
|
# as well as for all child nodes.
|
28
|
-
# See {file:SASS_REFERENCE.md#
|
30
|
+
# See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
29
31
|
#
|
30
32
|
# @param options [{Symbol => Object}] The options
|
31
33
|
attr_writer :options
|
@@ -86,7 +88,7 @@ MSG
|
|
86
88
|
# @return [Script::Value::String] A string containing both values
|
87
89
|
# separated by `"="`
|
88
90
|
def single_eq(other)
|
89
|
-
Sass::Script::Value::String.new("#{
|
91
|
+
Sass::Script::Value::String.new("#{self}=#{other}")
|
90
92
|
end
|
91
93
|
|
92
94
|
# The SassScript `+` operation.
|
@@ -95,10 +97,8 @@ MSG
|
|
95
97
|
# @return [Script::Value::String] A string containing both values
|
96
98
|
# without any separation
|
97
99
|
def plus(other)
|
98
|
-
|
99
|
-
|
100
|
-
end
|
101
|
-
Sass::Script::Value::String.new(to_s + other.to_s)
|
100
|
+
type = other.is_a?(Sass::Script::Value::String) ? other.type : :identifier
|
101
|
+
Sass::Script::Value::String.new(to_s(:quote => :none) + other.to_s(:quote => :none), type)
|
102
102
|
end
|
103
103
|
|
104
104
|
# The SassScript `-` operation.
|
@@ -107,7 +107,7 @@ MSG
|
|
107
107
|
# @return [Script::Value::String] A string containing both values
|
108
108
|
# separated by `"-"`
|
109
109
|
def minus(other)
|
110
|
-
Sass::Script::Value::String.new("#{
|
110
|
+
Sass::Script::Value::String.new("#{self}-#{other}")
|
111
111
|
end
|
112
112
|
|
113
113
|
# The SassScript `/` operation.
|
@@ -116,7 +116,7 @@ MSG
|
|
116
116
|
# @return [Script::Value::String] A string containing both values
|
117
117
|
# separated by `"/"`
|
118
118
|
def div(other)
|
119
|
-
Sass::Script::Value::String.new("#{
|
119
|
+
Sass::Script::Value::String.new("#{self}/#{other}")
|
120
120
|
end
|
121
121
|
|
122
122
|
# The SassScript unary `+` operation (e.g. `+$a`).
|
@@ -125,7 +125,7 @@ MSG
|
|
125
125
|
# @return [Script::Value::String] A string containing the value
|
126
126
|
# preceded by `"+"`
|
127
127
|
def unary_plus
|
128
|
-
Sass::Script::Value::String.new("+#{
|
128
|
+
Sass::Script::Value::String.new("+#{self}")
|
129
129
|
end
|
130
130
|
|
131
131
|
# The SassScript unary `-` operation (e.g. `-$a`).
|
@@ -134,7 +134,7 @@ MSG
|
|
134
134
|
# @return [Script::Value::String] A string containing the value
|
135
135
|
# preceded by `"-"`
|
136
136
|
def unary_minus
|
137
|
-
Sass::Script::Value::String.new("-#{
|
137
|
+
Sass::Script::Value::String.new("-#{self}")
|
138
138
|
end
|
139
139
|
|
140
140
|
# The SassScript unary `/` operation (e.g. `/$a`).
|
@@ -143,13 +143,13 @@ MSG
|
|
143
143
|
# @return [Script::Value::String] A string containing the value
|
144
144
|
# preceded by `"/"`
|
145
145
|
def unary_div
|
146
|
-
Sass::Script::Value::String.new("/#{
|
146
|
+
Sass::Script::Value::String.new("/#{self}")
|
147
147
|
end
|
148
148
|
|
149
149
|
# Returns the hash code of this value. Two objects' hash codes should be
|
150
150
|
# equal if the objects are equal.
|
151
151
|
#
|
152
|
-
# @return [Fixnum] The hash code.
|
152
|
+
# @return [Integer for Ruby 2.4.0+, Fixnum for earlier Ruby versions] The hash code.
|
153
153
|
def hash
|
154
154
|
value.hash
|
155
155
|
end
|
@@ -176,7 +176,7 @@ MSG
|
|
176
176
|
eq(other).to_bool
|
177
177
|
end
|
178
178
|
|
179
|
-
# @return [
|
179
|
+
# @return [Integer] The integer value of this value
|
180
180
|
# @raise [Sass::SyntaxError] if this value isn't an integer
|
181
181
|
def to_i
|
182
182
|
raise Sass::SyntaxError.new("#{inspect} is not an integer.")
|
@@ -212,6 +212,9 @@ MSG
|
|
212
212
|
# Returns the string representation of this value
|
213
213
|
# as it would be output to the CSS document.
|
214
214
|
#
|
215
|
+
# @options opts :quote [String]
|
216
|
+
# The preferred quote style for quoted strings. If `:none`, strings are
|
217
|
+
# always emitted unquoted.
|
215
218
|
# @return [String]
|
216
219
|
def to_s(opts = {})
|
217
220
|
Sass::Util.abstract(self)
|