sass 3.3.14 → 3.4.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +5 -5
- 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/lib/sass.rb +0 -5
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +28 -39
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +419 -0
- data/lib/sass/features.rb +6 -0
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/importers/base.rb +5 -1
- data/lib/sass/importers/filesystem.rb +4 -21
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +32 -136
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +363 -39
- data/lib/sass/script/lexer.rb +68 -50
- data/lib/sass/script/parser.rb +29 -14
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +19 -1
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +32 -12
- data/lib/sass/script/value/helpers.rb +107 -0
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +60 -14
- data/lib/sass/script/value/string.rb +53 -9
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +175 -319
- data/lib/sass/scss/rx.rb +14 -5
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector.rb +56 -193
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +91 -12
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +99 -31
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +101 -37
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +23 -9
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +1 -1
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/directive_node.rb +7 -1
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/prop_node.rb +1 -1
- data/lib/sass/tree/rule_node.rb +11 -6
- data/lib/sass/tree/visitors/check_nesting.rb +3 -4
- data/lib/sass/tree/visitors/convert.rb +8 -17
- data/lib/sass/tree/visitors/cssize.rb +12 -24
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +43 -28
- data/lib/sass/tree/visitors/set_options.rb +5 -0
- data/lib/sass/tree/visitors/to_css.rb +14 -13
- data/lib/sass/util.rb +94 -90
- data/test/sass/cache_test.rb +1 -1
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +5 -14
- data/test/sass/conversion_test.rb +47 -1
- data/test/sass/css2sass_test.rb +3 -3
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +128 -191
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +234 -17
- data/test/sass/functions_test.rb +268 -213
- data/test/sass/importer_test.rb +31 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +12 -11
- 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 -4
- data/test/sass/script_test.rb +202 -79
- data/test/sass/scss/css_test.rb +95 -25
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +363 -19
- data/test/sass/source_map_test.rb +48 -41
- data/test/sass/superselector_test.rb +191 -0
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- 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 +1 -1
- data/test/sass/value_helpers_test.rb +3 -3
- data/test/test_helper.rb +2 -2
- metadata +30 -7
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sass::Tree
|
2
|
+
class KeyframeRuleNode < Node
|
3
|
+
# The text of the directive after any interpolated SassScript has been resolved.
|
4
|
+
# Since this is only a static node, this is the only value property.
|
5
|
+
#
|
6
|
+
# @return [String]
|
7
|
+
attr_accessor :resolved_value
|
8
|
+
|
9
|
+
# @param resolved_value [String] See \{#resolved_value}
|
10
|
+
def initialize(resolved_value)
|
11
|
+
@resolved_value = resolved_value
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/sass/tree/prop_node.rb
CHANGED
@@ -97,7 +97,7 @@ module Sass::Tree
|
|
97
97
|
# @param opts [{Symbol => Object}] The options hash for the tree.
|
98
98
|
# @param fmt [Symbol] `:scss` or `:sass`.
|
99
99
|
def declaration(opts = {:old => @prop_syntax == :old}, fmt = :sass)
|
100
|
-
name = self.name.map {|n| n.is_a?(String) ? n :
|
100
|
+
name = self.name.map {|n| n.is_a?(String) ? n : n.to_sass(opts)}.join
|
101
101
|
if name[0] == ?:
|
102
102
|
raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\"" +
|
103
103
|
" hack is not allowed in the Sass indented syntax")
|
data/lib/sass/tree/rule_node.rb
CHANGED
@@ -60,15 +60,20 @@ module Sass::Tree
|
|
60
60
|
# @return [String]
|
61
61
|
attr_accessor :stack_trace
|
62
62
|
|
63
|
-
# @param rule [Array<String, Sass::Script::Tree::Node
|
63
|
+
# @param rule [Array<String, Sass::Script::Tree::Node>, Sass::Selector::CommaSequence]
|
64
|
+
# The CSS rule, either unparsed or parsed.
|
64
65
|
# @param selector_source_range [Sass::Source::Range]
|
65
|
-
# The CSS rule. See \{#rule}
|
66
66
|
def initialize(rule, selector_source_range = nil)
|
67
|
-
|
68
|
-
|
67
|
+
if rule.is_a?(Sass::Selector::CommaSequence)
|
68
|
+
@rule = [rule.to_s]
|
69
|
+
@parsed_rules = rule
|
70
|
+
else
|
71
|
+
merged = Sass::Util.merge_adjacent_strings(rule)
|
72
|
+
@rule = Sass::Util.strip_string_array(merged)
|
73
|
+
try_to_parse_non_interpolated_rules
|
74
|
+
end
|
69
75
|
@selector_source_range = selector_source_range
|
70
76
|
@tabs = 0
|
71
|
-
try_to_parse_non_interpolated_rules
|
72
77
|
super()
|
73
78
|
end
|
74
79
|
|
@@ -130,7 +135,7 @@ module Sass::Tree
|
|
130
135
|
if @rule.all? {|t| t.kind_of?(String)}
|
131
136
|
# We don't use real filename/line info because we don't have it yet.
|
132
137
|
# When we get it, we'll set it on the parsed rules if possible.
|
133
|
-
parser = Sass::SCSS::StaticParser.new(@rule.join.strip,
|
138
|
+
parser = Sass::SCSS::StaticParser.new(@rule.join.strip, nil, nil, 1)
|
134
139
|
# rubocop:disable RescueModifier
|
135
140
|
@parsed_rules = parser.parse_selector rescue nil
|
136
141
|
# rubocop:enable RescueModifier
|
@@ -123,7 +123,7 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
123
123
|
|
124
124
|
VALID_FUNCTION_CHILDREN = [
|
125
125
|
Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::ReturnNode,
|
126
|
-
Sass::Tree::VariableNode, Sass::Tree::WarnNode
|
126
|
+
Sass::Tree::VariableNode, Sass::Tree::WarnNode, Sass::Tree::ErrorNode
|
127
127
|
] + CONTROL_NODES
|
128
128
|
def invalid_function_child?(parent, child)
|
129
129
|
unless is_any_of?(child, VALID_FUNCTION_CHILDREN)
|
@@ -140,9 +140,8 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
-
VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
|
144
|
-
Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode,
|
145
|
-
Sass::Tree::MixinNode]
|
143
|
+
VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::KeyframeRuleNode, Sass::Tree::PropNode,
|
144
|
+
Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode, Sass::Tree::MixinNode]
|
146
145
|
def invalid_prop_parent?(parent, child)
|
147
146
|
unless is_any_of?(parent, VALID_PROP_PARENTS)
|
148
147
|
"Properties are only allowed within rules, directives, mixin includes, or other properties." +
|
@@ -96,6 +96,10 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
96
96
|
"#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
|
97
97
|
end
|
98
98
|
|
99
|
+
def visit_error(node)
|
100
|
+
"#{tab_str}@error #{node.expr.to_sass(@options)}#{semi}\n"
|
101
|
+
end
|
102
|
+
|
99
103
|
def visit_directive(node)
|
100
104
|
res = "#{tab_str}#{interp_to_src(node.value)}"
|
101
105
|
res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
|
@@ -236,7 +240,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
236
240
|
end
|
237
241
|
|
238
242
|
def visit_rule(node)
|
239
|
-
rule = node.parsed_rules ? node.parsed_rules.
|
243
|
+
rule = node.parsed_rules ? [node.parsed_rules.to_s] : node.rule
|
240
244
|
if @format == :sass
|
241
245
|
name = selector_to_sass(rule)
|
242
246
|
name = "\\" + name if name[0] == ?:
|
@@ -279,10 +283,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
279
283
|
private
|
280
284
|
|
281
285
|
def interp_to_src(interp)
|
282
|
-
interp.map
|
283
|
-
next r if r.is_a?(String)
|
284
|
-
"\#{#{r.to_sass(@options)}}"
|
285
|
-
end.join
|
286
|
+
interp.map {|r| r.is_a?(String) ? r : r.to_sass(@options)}.join
|
286
287
|
end
|
287
288
|
|
288
289
|
# Like interp_to_src, but removes the unnecessary `#{}` around the keys and
|
@@ -294,17 +295,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
294
295
|
e.value.value
|
295
296
|
end
|
296
297
|
|
297
|
-
|
298
|
-
next r if r.is_a?(String)
|
299
|
-
before, after = interp[i - 1], interp[i + 1]
|
300
|
-
if before.is_a?(String) && after.is_a?(String) &&
|
301
|
-
((before[-1] == ?( && after[0] == ?:) ||
|
302
|
-
(before =~ /:\s*/ && after[0] == ?)))
|
303
|
-
r.to_sass(@options)
|
304
|
-
else
|
305
|
-
"\#{#{r.to_sass(@options)}}"
|
306
|
-
end
|
307
|
-
end.join
|
298
|
+
interp_to_src(interp)
|
308
299
|
end
|
309
300
|
|
310
301
|
def selector_to_src(sel)
|
@@ -316,7 +307,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
316
307
|
if r.is_a?(String)
|
317
308
|
r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
|
318
309
|
else
|
319
|
-
|
310
|
+
r.to_sass(@options)
|
320
311
|
end
|
321
312
|
end.join
|
322
313
|
end
|
@@ -123,29 +123,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
123
123
|
|
124
124
|
# Registers an extension in the `@extends` subset map.
|
125
125
|
def visit_extend(node)
|
126
|
-
node.resolved_selector
|
127
|
-
|
128
|
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
|
129
|
-
end
|
130
|
-
|
131
|
-
sseq = seq.members.first
|
132
|
-
if !sseq.is_a?(Sass::Selector::SimpleSequence)
|
133
|
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
|
134
|
-
elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
|
135
|
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend parent selectors")
|
136
|
-
end
|
137
|
-
|
138
|
-
sel = sseq.members
|
139
|
-
parent.resolved_rules.members.each do |member|
|
140
|
-
unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
|
141
|
-
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
|
142
|
-
end
|
143
|
-
|
144
|
-
parent_directives = @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)}
|
145
|
-
@extends[sel] = Extend.new(member, sel, node, parent_directives, :not_found)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
126
|
+
parent.resolved_rules.populate_extends(@extends, node.resolved_selector, node,
|
127
|
+
@parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)})
|
149
128
|
[]
|
150
129
|
end
|
151
130
|
|
@@ -232,6 +211,14 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
232
211
|
rules
|
233
212
|
end
|
234
213
|
|
214
|
+
def visit_keyframerule(node)
|
215
|
+
return node unless node.has_children
|
216
|
+
|
217
|
+
yield
|
218
|
+
|
219
|
+
debubble(node.children, node)
|
220
|
+
end
|
221
|
+
|
235
222
|
# Bubbles a directive up through RuleNodes.
|
236
223
|
def visit_directive(node)
|
237
224
|
return node unless node.has_children
|
@@ -249,7 +236,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
249
236
|
child.node.resolved_value == node.resolved_value
|
250
237
|
end
|
251
238
|
|
252
|
-
|
239
|
+
# We know empty @keyframes directives do nothing.
|
240
|
+
if directive_exists || node.name == '@keyframes'
|
253
241
|
[]
|
254
242
|
else
|
255
243
|
empty_node = node.dup
|
@@ -207,6 +207,17 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
207
207
|
[]
|
208
208
|
end
|
209
209
|
|
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
|
+
|
210
221
|
# Runs the child nodes once for each value in the list.
|
211
222
|
def visit_each(node)
|
212
223
|
list = node.list.perform(@environment)
|
@@ -377,18 +388,31 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
377
388
|
# Runs SassScript interpolation in the selector,
|
378
389
|
# and then parses the result into a {Sass::Selector::CommaSequence}.
|
379
390
|
def visit_rule(node)
|
380
|
-
old_at_root_without_rule
|
391
|
+
old_at_root_without_rule = @at_root_without_rule
|
381
392
|
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
|
382
393
|
node.filename, node.options[:importer], node.line)
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
394
|
+
if @in_keyframes
|
395
|
+
keyframe_rule_node = Sass::Tree::KeyframeRuleNode.new(parser.parse_keyframes_selector)
|
396
|
+
keyframe_rule_node.options = node.options
|
397
|
+
keyframe_rule_node.line = node.line
|
398
|
+
keyframe_rule_node.filename = node.filename
|
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
|
390
415
|
end
|
391
|
-
node
|
392
416
|
ensure
|
393
417
|
@at_root_without_rule = old_at_root_without_rule
|
394
418
|
end
|
@@ -405,36 +429,24 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
405
429
|
end
|
406
430
|
|
407
431
|
old_at_root_without_rule = @at_root_without_rule
|
432
|
+
old_in_keyframes = @in_keyframes
|
408
433
|
@at_root_without_rule = true if node.exclude?('rule')
|
434
|
+
@in_keyframes = false if node.exclude?('keyframes')
|
409
435
|
yield
|
410
436
|
ensure
|
437
|
+
@in_keyframes = old_in_keyframes
|
411
438
|
@at_root_without_rule = old_at_root_without_rule
|
412
439
|
end
|
413
440
|
|
414
441
|
# Loads the new variable value into the environment.
|
415
442
|
def visit_variable(node)
|
416
443
|
env = @environment
|
417
|
-
|
418
|
-
if node.
|
419
|
-
|
420
|
-
|
421
|
-
!env.global_env.global_warning_given.include?(identifier)
|
422
|
-
env.global_env.global_warning_given.add(identifier)
|
423
|
-
var_expr = "$#{node.name}: #{node.expr.to_sass(env.options)} !global"
|
424
|
-
var_expr << " !default" if node.guarded
|
425
|
-
location = "on line #{node.line}"
|
426
|
-
location << " of #{node.filename}" if node.filename
|
427
|
-
Sass::Util.sass_warn <<WARNING
|
428
|
-
DEPRECATION WARNING #{location}:
|
429
|
-
Assigning to global variable "$#{node.name}" by default is deprecated.
|
430
|
-
In future versions of Sass, this will create a new local variable.
|
431
|
-
If you want to assign to the global variable, use "#{var_expr}" instead.
|
432
|
-
Note that this will be incompatible with Sass 3.2.
|
433
|
-
WARNING
|
444
|
+
env = env.global_env if node.global
|
445
|
+
if node.guarded
|
446
|
+
var = env.var(node.name)
|
447
|
+
return [] if var && !var.null?
|
434
448
|
end
|
435
449
|
|
436
|
-
var = env.var(node.name)
|
437
|
-
return [] if node.guarded && var && !var.null?
|
438
450
|
val = node.expr.perform(@environment)
|
439
451
|
if node.expr.source_range
|
440
452
|
val.source_range = node.expr.source_range
|
@@ -466,10 +478,13 @@ WARNING
|
|
466
478
|
|
467
479
|
def visit_directive(node)
|
468
480
|
node.resolved_value = run_interp(node.value)
|
481
|
+
old_in_keyframes, @in_keyframes = @in_keyframes, node.normalized_name == "@keyframes"
|
469
482
|
with_environment Sass::Environment.new(@environment) do
|
470
483
|
node.children = node.children.map {|c| visit(c)}.flatten
|
471
484
|
node
|
472
485
|
end
|
486
|
+
ensure
|
487
|
+
@in_keyframes = old_in_keyframes
|
473
488
|
end
|
474
489
|
|
475
490
|
def visit_media(node)
|
@@ -127,21 +127,18 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
127
127
|
return "" if @result.empty?
|
128
128
|
|
129
129
|
output "\n"
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
#
|
136
|
-
|
137
|
-
|
138
|
-
|
130
|
+
|
131
|
+
unless Sass::Util.ruby1_8? || @result.ascii_only?
|
132
|
+
if node.style == :compressed
|
133
|
+
# A byte order mark is sufficient to tell browsers that this
|
134
|
+
# file is UTF-8 encoded, and will override any other detection
|
135
|
+
# methods as per http://encoding.spec.whatwg.org/#decode-and-encode.
|
136
|
+
prepend! "\uFEFF"
|
137
|
+
else
|
138
|
+
prepend! "@charset \"UTF-8\";\n"
|
139
139
|
end
|
140
140
|
end
|
141
141
|
|
142
|
-
prepend! "@charset \"#{@result.encoding.name}\";#{
|
143
|
-
node.style == :compressed ? '' : "\n"
|
144
|
-
}".encode(@result.encoding)
|
145
142
|
@result
|
146
143
|
rescue Sass::SyntaxError => e
|
147
144
|
e.sass_template ||= node.template
|
@@ -281,7 +278,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
281
278
|
|
282
279
|
joined_rules = node.resolved_rules.members.map do |seq|
|
283
280
|
next if seq.has_placeholder?
|
284
|
-
rule_part = seq.
|
281
|
+
rule_part = seq.to_s
|
285
282
|
if node.style == :compressed
|
286
283
|
rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
|
287
284
|
rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
|
@@ -353,6 +350,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
353
350
|
# @comment
|
354
351
|
# rubocop:enable MethodLength
|
355
352
|
|
353
|
+
def visit_keyframerule(node)
|
354
|
+
visit_directive(node)
|
355
|
+
end
|
356
|
+
|
356
357
|
private
|
357
358
|
|
358
359
|
def debug_info_rule(debug_info, options)
|
data/lib/sass/util.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
require 'erb'
|
2
3
|
require 'set'
|
3
4
|
require 'enumerator'
|
@@ -337,6 +338,18 @@ module Sass
|
|
337
338
|
minuend.select {|e| set.include?(e)}
|
338
339
|
end
|
339
340
|
|
341
|
+
# Returns the maximum of `val1` and `val2`. We use this over \{Array.max} to
|
342
|
+
# avoid unnecessary garbage collection.
|
343
|
+
def max(val1, val2)
|
344
|
+
val1 > val2 ? val1 : val2
|
345
|
+
end
|
346
|
+
|
347
|
+
# Returns the minimum of `val1` and `val2`. We use this over \{Array.min} to
|
348
|
+
# avoid unnecessary garbage collection.
|
349
|
+
def min(val1, val2)
|
350
|
+
val1 <= val2 ? val1 : val2
|
351
|
+
end
|
352
|
+
|
340
353
|
# Returns a string description of the character that caused an
|
341
354
|
# `Encoding::UndefinedConversionError`.
|
342
355
|
#
|
@@ -707,6 +720,14 @@ module Sass
|
|
707
720
|
@ruby1_8_6 = ruby1_8? && RUBY_VERSION_COMPONENTS[2] < 7
|
708
721
|
end
|
709
722
|
|
723
|
+
# Whether or not this is running under Ruby 1.9.2 exactly.
|
724
|
+
#
|
725
|
+
# @return [Boolean]
|
726
|
+
def ruby1_9_2?
|
727
|
+
return @ruby1_9_2 if defined?(@ruby1_9_2)
|
728
|
+
@ruby1_9_2 = RUBY_VERSION_COMPONENTS == [1, 9, 2]
|
729
|
+
end
|
730
|
+
|
710
731
|
# Wehter or not this is running under JRuby 1.6 or lower.
|
711
732
|
def jruby1_6?
|
712
733
|
return @jruby1_6 if defined?(@jruby1_6)
|
@@ -751,114 +772,76 @@ module Sass
|
|
751
772
|
(pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
|
752
773
|
end
|
753
774
|
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
# @param str [String] The string of which to check the encoding
|
760
|
-
# @yield [msg] A block in which an encoding error can be raised.
|
761
|
-
# Only yields if there is an encoding error
|
762
|
-
# @yieldparam msg [String] The error message to be raised
|
763
|
-
# @return [String] `str`, potentially with encoding gotchas like BOMs removed
|
764
|
-
def check_encoding(str)
|
765
|
-
if ruby1_8?
|
766
|
-
return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
|
767
|
-
elsif str.valid_encoding?
|
768
|
-
# Get rid of the Unicode BOM if possible
|
769
|
-
if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
|
770
|
-
return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
|
771
|
-
else
|
772
|
-
return str
|
773
|
-
end
|
774
|
-
end
|
775
|
-
|
776
|
-
encoding = str.encoding
|
777
|
-
newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
|
778
|
-
str.force_encoding("binary").split(newlines).each_with_index do |line, i|
|
779
|
-
begin
|
780
|
-
line.encode(encoding)
|
781
|
-
rescue Encoding::UndefinedConversionError => e
|
782
|
-
yield <<MSG.rstrip, i + 1
|
783
|
-
Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}
|
784
|
-
MSG
|
785
|
-
end
|
786
|
-
end
|
787
|
-
str
|
775
|
+
unless ruby1_8?
|
776
|
+
CHARSET_REGEXP = /\A@charset "([^"]+)"/
|
777
|
+
UTF_8_BOM = "\xEF\xBB\xBF".force_encoding('BINARY')
|
778
|
+
UTF_16BE_BOM = "\xFE\xFF".force_encoding('BINARY')
|
779
|
+
UTF_16LE_BOM = "\xFF\xFE".force_encoding('BINARY')
|
788
780
|
end
|
789
781
|
|
790
782
|
# Like {\#check\_encoding}, but also checks for a `@charset` declaration
|
791
783
|
# at the beginning of the file and uses that encoding if it exists.
|
792
784
|
#
|
793
|
-
#
|
794
|
-
# If a `@charset` declaration exists,
|
795
|
-
# we assume that that's the original encoding of the document.
|
796
|
-
# Otherwise, we use whatever encoding Ruby has.
|
797
|
-
# Then we convert that to UTF-8 to process internally.
|
798
|
-
# The UTF-8 end result is what's returned by this method.
|
785
|
+
# Sass follows CSS's decoding rules.
|
799
786
|
#
|
800
787
|
# @param str [String] The string of which to check the encoding
|
801
|
-
# @yield [msg] A block in which an encoding error can be raised.
|
802
|
-
# Only yields if there is an encoding error
|
803
|
-
# @yieldparam msg [String] The error message to be raised
|
804
788
|
# @return [(String, Encoding)] The original string encoded as UTF-8,
|
805
789
|
# and the source encoding of the string (or `nil` under Ruby 1.8)
|
806
790
|
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
807
791
|
# cannot be converted to UTF-8
|
808
792
|
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
793
|
+
# @raise [Sass::SyntaxError] If the document declares an encoding that
|
794
|
+
# doesn't match its contents, or it doesn't declare an encoding and its
|
795
|
+
# contents are invalid in the native encoding.
|
796
|
+
def check_sass_encoding(str)
|
797
|
+
# On Ruby 1.8 we can't do anything complicated with encodings.
|
798
|
+
# Instead, we just strip out a UTF-8 BOM if it exists and
|
799
|
+
# sanitize according to Section 3.3 of CSS Syntax Level 3. We
|
800
|
+
# don't sanitize null characters since they might be components
|
801
|
+
# of other characters.
|
802
|
+
if ruby1_8?
|
803
|
+
return str.gsub(/\A\xEF\xBB\xBF/, '').gsub(/\r\n?|\f/, "\n"), nil
|
816
804
|
end
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
805
|
+
|
806
|
+
# Determine the fallback encoding following section 3.2 of CSS Syntax Level 3 and Encodings:
|
807
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#determine-the-fallback-encoding
|
808
|
+
# http://encoding.spec.whatwg.org/#decode
|
809
|
+
binary = str.dup.force_encoding("BINARY")
|
810
|
+
if binary.start_with?(UTF_8_BOM)
|
811
|
+
binary.slice! 0, UTF_8_BOM.length
|
812
|
+
str = binary.force_encoding('UTF-8')
|
813
|
+
elsif binary.start_with?(UTF_16BE_BOM)
|
814
|
+
binary.slice! 0, UTF_16BE_BOM.length
|
815
|
+
str = binary.force_encoding('UTF-16BE')
|
816
|
+
elsif binary.start_with?(UTF_16LE_BOM)
|
817
|
+
binary.slice! 0, UTF_16LE_BOM.length
|
818
|
+
str = binary.force_encoding('UTF-16LE')
|
819
|
+
elsif binary =~ CHARSET_REGEXP
|
820
|
+
charset = $1.force_encoding('US-ASCII')
|
821
|
+
# Ruby 1.9.2 doesn't recognize a UTF-16 encoding without an endian marker.
|
822
|
+
if ruby1_9_2? && charset.downcase == 'utf-16'
|
823
|
+
encoding = Encoding.find('UTF-8')
|
824
|
+
else
|
825
|
+
encoding = Encoding.find(charset)
|
826
|
+
if encoding.name == 'UTF-16' || encoding.name == 'UTF-16BE'
|
827
|
+
encoding = Encoding.find('UTF-8')
|
825
828
|
end
|
826
829
|
end
|
827
|
-
str.force_encoding(
|
828
|
-
elsif
|
829
|
-
|
830
|
+
str = binary.force_encoding(encoding)
|
831
|
+
elsif str.encoding.name == "ASCII-8BIT"
|
832
|
+
# Normally we want to fall back on believing the Ruby string
|
833
|
+
# encoding, but if that's just binary we want to make sure
|
834
|
+
# it's valid UTF-8.
|
835
|
+
str = str.force_encoding('utf-8')
|
830
836
|
end
|
831
837
|
|
832
|
-
str
|
833
|
-
return str.encode("UTF-8"), str.encoding
|
834
|
-
end
|
838
|
+
find_encoding_error(str) unless str.valid_encoding?
|
835
839
|
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
# We could automatically add in any non-ASCII-compatible encodings here,
|
843
|
-
# but there's not really a good way to do that
|
844
|
-
# without manually checking that each encoding
|
845
|
-
# encodes all ASCII characters properly,
|
846
|
-
# which takes long enough to affect the startup time of the CLI.
|
847
|
-
ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
|
848
|
-
|
849
|
-
CHARSET_REGEXPS = Hash.new do |h, e|
|
850
|
-
h[e] =
|
851
|
-
begin
|
852
|
-
# /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
|
853
|
-
Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
|
854
|
-
_enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
|
855
|
-
_enc("\uFEFF", e)})/)
|
856
|
-
rescue Encoding::ConverterNotFoundError => _
|
857
|
-
nil # JRuby on Java 5 doesn't support UTF-32
|
858
|
-
rescue
|
859
|
-
# /\A@charset "(.*?)"/
|
860
|
-
Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
|
861
|
-
end
|
840
|
+
begin
|
841
|
+
# If the string is valid, preprocess it according to section 3.3 of CSS Syntax Level 3.
|
842
|
+
return str.encode("UTF-8").gsub(/\r\n?|\f/, "\n").tr("\u0000", "�"), str.encoding
|
843
|
+
rescue EncodingError
|
844
|
+
find_encoding_error(str)
|
862
845
|
end
|
863
846
|
end
|
864
847
|
|
@@ -1268,6 +1251,27 @@ MSG
|
|
1268
1251
|
|
1269
1252
|
private
|
1270
1253
|
|
1254
|
+
def find_encoding_error(str)
|
1255
|
+
encoding = str.encoding
|
1256
|
+
cr = Regexp.quote("\r".encode(encoding).force_encoding('BINARY'))
|
1257
|
+
lf = Regexp.quote("\n".encode(encoding).force_encoding('BINARY'))
|
1258
|
+
ff = Regexp.quote("\f".encode(encoding).force_encoding('BINARY'))
|
1259
|
+
line_break = /#{cr}#{lf}?|#{ff}|#{lf}/
|
1260
|
+
|
1261
|
+
str.force_encoding("binary").split(line_break).each_with_index do |line, i|
|
1262
|
+
begin
|
1263
|
+
line.encode(encoding)
|
1264
|
+
rescue Encoding::UndefinedConversionError => e
|
1265
|
+
raise Sass::SyntaxError.new(
|
1266
|
+
"Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}",
|
1267
|
+
:line => i + 1)
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
# We shouldn't get here, but it's possible some weird encoding stuff causes it.
|
1272
|
+
return str, str.encoding
|
1273
|
+
end
|
1274
|
+
|
1271
1275
|
# rubocop:disable LineLength
|
1272
1276
|
|
1273
1277
|
# Calculates the memoization table for the Least Common Subsequence algorithm.
|