sass 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +58 -50
- data/Rakefile +1 -4
- 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/cache_stores/filesystem.rb +6 -2
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +37 -46
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +424 -0
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/features.rb +7 -0
- data/lib/sass/importers/base.rb +7 -2
- data/lib/sass/importers/filesystem.rb +9 -25
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +200 -83
- data/lib/sass/plugin/staleness_checker.rb +1 -1
- data/lib/sass/plugin.rb +3 -3
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +622 -268
- data/lib/sass/script/lexer.rb +99 -34
- data/lib/sass/script/parser.rb +24 -23
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +20 -2
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +1 -1
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/value/base.rb +7 -5
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +39 -21
- 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 +62 -14
- data/lib/sass/script/value/string.rb +59 -11
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +190 -328
- data/lib/sass/scss/rx.rb +15 -6
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +92 -13
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +94 -24
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +97 -33
- data/lib/sass/selector.rb +57 -194
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +26 -12
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +2 -3
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/charset_node.rb +1 -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 +4 -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 +12 -7
- data/lib/sass/tree/visitors/check_nesting.rb +38 -10
- data/lib/sass/tree/visitors/convert.rb +16 -18
- data/lib/sass/tree/visitors/cssize.rb +29 -29
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +45 -33
- data/lib/sass/tree/visitors/set_options.rb +14 -0
- data/lib/sass/tree/visitors/to_css.rb +15 -14
- data/lib/sass/util/subset_map.rb +1 -1
- data/lib/sass/util.rb +222 -99
- data/lib/sass/version.rb +5 -5
- data/lib/sass.rb +0 -5
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +19 -10
- data/test/sass/conversion_test.rb +58 -1
- data/test/sass/css2sass_test.rb +23 -4
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +136 -199
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +236 -19
- data/test/sass/functions_test.rb +295 -253
- 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 +14 -13
- 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 +10 -7
- data/test/sass/script_test.rb +288 -74
- data/test/sass/scss/css_test.rb +141 -24
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +457 -18
- data/test/sass/source_map_test.rb +115 -25
- 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 +31 -1
- data/test/sass/value_helpers_test.rb +5 -7
- data/test/test_helper.rb +2 -2
- 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 +25 -22
- data/ext/mkrf_conf.rb +0 -27
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
- 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
@@ -15,11 +15,21 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
15
15
|
super
|
16
16
|
end
|
17
17
|
|
18
|
+
def visit_comment(node)
|
19
|
+
node.value.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
|
18
23
|
def visit_debug(node)
|
19
24
|
node.expr.options = @options
|
20
25
|
yield
|
21
26
|
end
|
22
27
|
|
28
|
+
def visit_error(node)
|
29
|
+
node.expr.options = @options
|
30
|
+
yield
|
31
|
+
end
|
32
|
+
|
23
33
|
def visit_each(node)
|
24
34
|
node.list.options = @options
|
25
35
|
yield
|
@@ -41,6 +51,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
41
51
|
k.options = @options
|
42
52
|
v.options = @options if v
|
43
53
|
end
|
54
|
+
node.splat.options = @options if node.splat
|
44
55
|
yield
|
45
56
|
end
|
46
57
|
|
@@ -63,12 +74,15 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
63
74
|
k.options = @options
|
64
75
|
v.options = @options if v
|
65
76
|
end
|
77
|
+
node.splat.options = @options if node.splat
|
66
78
|
yield
|
67
79
|
end
|
68
80
|
|
69
81
|
def visit_mixin(node)
|
70
82
|
node.args.each {|a| a.options = @options}
|
71
83
|
node.keywords.each {|k, v| v.options = @options}
|
84
|
+
node.splat.options = @options if node.splat
|
85
|
+
node.kwarg_splat.options = @options if node.kwarg_splat
|
72
86
|
yield
|
73
87
|
end
|
74
88
|
|
@@ -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
|
@@ -235,7 +232,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
235
232
|
|
236
233
|
def visit_media(node)
|
237
234
|
with_tabs(@tabs + node.tabs) {visit_directive(node)}
|
238
|
-
output("\n") if node.group_end
|
235
|
+
output("\n") if node.style != :compressed && node.group_end
|
239
236
|
end
|
240
237
|
|
241
238
|
def visit_supports(node)
|
@@ -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/subset_map.rb
CHANGED
@@ -59,7 +59,7 @@ module Sass
|
|
59
59
|
#
|
60
60
|
# In the worst case, this runs in `O(m*max(n, log m))` time,
|
61
61
|
# where `n` is the size of `set`
|
62
|
-
# and `m` is the number of
|
62
|
+
# and `m` is the number of associations in the map.
|
63
63
|
# However, unless many keys in the map overlap with `set`,
|
64
64
|
# `m` will typically be much smaller.
|
65
65
|
#
|
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'
|
@@ -17,7 +18,7 @@ module Sass
|
|
17
18
|
|
18
19
|
# An array of ints representing the Ruby version number.
|
19
20
|
# @api public
|
20
|
-
|
21
|
+
RUBY_VERSION_COMPONENTS = RUBY_VERSION.split(".").map {|s| s.to_i}
|
21
22
|
|
22
23
|
# The Ruby engine we're running under. Defaults to `"ruby"`
|
23
24
|
# if the top-level constant is undefined.
|
@@ -162,6 +163,43 @@ module Sass
|
|
162
163
|
end
|
163
164
|
end
|
164
165
|
|
166
|
+
# Non-destructively replaces all occurrences of a subsequence in an array
|
167
|
+
# with another subsequence.
|
168
|
+
#
|
169
|
+
# @example
|
170
|
+
# replace_subseq([1, 2, 3, 4, 5], [2, 3], [:a, :b])
|
171
|
+
# #=> [1, :a, :b, 4, 5]
|
172
|
+
#
|
173
|
+
# @param arr [Array] The array whose subsequences will be replaced.
|
174
|
+
# @param subseq [Array] The subsequence to find and replace.
|
175
|
+
# @param replacement [Array] The sequence that `subseq` will be replaced with.
|
176
|
+
# @return [Array] `arr` with `subseq` replaced with `replacement`.
|
177
|
+
def replace_subseq(arr, subseq, replacement)
|
178
|
+
new = []
|
179
|
+
matched = []
|
180
|
+
i = 0
|
181
|
+
arr.each do |elem|
|
182
|
+
if elem != subseq[i]
|
183
|
+
new.push(*matched)
|
184
|
+
matched = []
|
185
|
+
i = 0
|
186
|
+
new << elem
|
187
|
+
next
|
188
|
+
end
|
189
|
+
|
190
|
+
if i == subseq.length - 1
|
191
|
+
matched = []
|
192
|
+
i = 0
|
193
|
+
new.push(*replacement)
|
194
|
+
else
|
195
|
+
matched << elem
|
196
|
+
i += 1
|
197
|
+
end
|
198
|
+
end
|
199
|
+
new.push(*matched)
|
200
|
+
new
|
201
|
+
end
|
202
|
+
|
165
203
|
# Intersperses a value in an enumerable, as would be done with `Array#join`
|
166
204
|
# but without concatenating the array together afterwards.
|
167
205
|
#
|
@@ -300,6 +338,18 @@ module Sass
|
|
300
338
|
minuend.select {|e| set.include?(e)}
|
301
339
|
end
|
302
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
|
+
|
303
353
|
# Returns a string description of the character that caused an
|
304
354
|
# `Encoding::UndefinedConversionError`.
|
305
355
|
#
|
@@ -507,8 +557,14 @@ module Sass
|
|
507
557
|
#
|
508
558
|
# @return [Boolean]
|
509
559
|
def listen_geq_2?
|
510
|
-
|
511
|
-
|
560
|
+
return @listen_geq_2 unless @listen_geq_2.nil?
|
561
|
+
@listen_geq_2 =
|
562
|
+
begin
|
563
|
+
require 'listen/version'
|
564
|
+
version_geq(::Listen::VERSION, '2.0.0')
|
565
|
+
rescue LoadError
|
566
|
+
false
|
567
|
+
end
|
512
568
|
end
|
513
569
|
|
514
570
|
# Returns an ActionView::Template* class.
|
@@ -583,13 +639,41 @@ module Sass
|
|
583
639
|
# Like `Pathname.new`, but normalizes Windows paths to always use backslash
|
584
640
|
# separators.
|
585
641
|
#
|
586
|
-
# `Pathname
|
642
|
+
# `Pathname#relative_path_from` can break if the two pathnames aren't
|
587
643
|
# consistent in their slash style.
|
644
|
+
#
|
645
|
+
# @param path [String]
|
646
|
+
# @return [Pathname]
|
588
647
|
def pathname(path)
|
589
648
|
path = path.tr("/", "\\") if windows?
|
590
649
|
Pathname.new(path)
|
591
650
|
end
|
592
651
|
|
652
|
+
# Like `Pathname#cleanpath`, but normalizes Windows paths to always use
|
653
|
+
# backslash separators. Normally, `Pathname#cleanpath` actually does the
|
654
|
+
# reverse -- it will convert backslashes to forward slashes, which can break
|
655
|
+
# `Pathname#relative_path_from`.
|
656
|
+
#
|
657
|
+
# @param path [String, Pathname]
|
658
|
+
# @return [Pathname]
|
659
|
+
def cleanpath(path)
|
660
|
+
path = Pathname.new(path) unless path.is_a?(Pathname)
|
661
|
+
pathname(path.cleanpath.to_s)
|
662
|
+
end
|
663
|
+
|
664
|
+
# Converts `path` to a "file:" URI. This handles Windows paths correctly.
|
665
|
+
#
|
666
|
+
# @param path [String, Pathname]
|
667
|
+
# @return [String]
|
668
|
+
def file_uri_from_path(path)
|
669
|
+
path = path.to_s if path.is_a?(Pathname)
|
670
|
+
path = Sass::Util.escape_uri(path)
|
671
|
+
return path.start_with?('/') ? "file://" + path : path unless windows?
|
672
|
+
return "file:///" + path.tr("\\", "/") if path =~ /^[a-zA-Z]:[\/\\]/
|
673
|
+
return "file://" + path.tr("\\", "/") if path =~ /\\\\[^\\]+\\[^\\\/]+/
|
674
|
+
path.tr("\\", "/")
|
675
|
+
end
|
676
|
+
|
593
677
|
# Prepare a value for a destructuring assignment (e.g. `a, b =
|
594
678
|
# val`). This works around a performance bug when using
|
595
679
|
# ActiveSupport, and only needs to be called when `val` is likely
|
@@ -610,7 +694,7 @@ module Sass
|
|
610
694
|
# @return [Boolean]
|
611
695
|
def ruby1?
|
612
696
|
return @ruby1 if defined?(@ruby1)
|
613
|
-
@ruby1 =
|
697
|
+
@ruby1 = RUBY_VERSION_COMPONENTS[0] <= 1
|
614
698
|
end
|
615
699
|
|
616
700
|
# Whether or not this is running under Ruby 1.8 or lower.
|
@@ -624,7 +708,7 @@ module Sass
|
|
624
708
|
# We have to fall back to 1.8 behavior.
|
625
709
|
return @ruby1_8 if defined?(@ruby1_8)
|
626
710
|
@ruby1_8 = ironruby? ||
|
627
|
-
(
|
711
|
+
(RUBY_VERSION_COMPONENTS[0] == 1 && RUBY_VERSION_COMPONENTS[1] < 9)
|
628
712
|
end
|
629
713
|
|
630
714
|
# Whether or not this is running under Ruby 1.8.6 or lower.
|
@@ -633,7 +717,15 @@ module Sass
|
|
633
717
|
# @return [Boolean]
|
634
718
|
def ruby1_8_6?
|
635
719
|
return @ruby1_8_6 if defined?(@ruby1_8_6)
|
636
|
-
@ruby1_8_6 = ruby1_8? &&
|
720
|
+
@ruby1_8_6 = ruby1_8? && RUBY_VERSION_COMPONENTS[2] < 7
|
721
|
+
end
|
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]
|
637
729
|
end
|
638
730
|
|
639
731
|
# Wehter or not this is running under JRuby 1.6 or lower.
|
@@ -680,114 +772,76 @@ module Sass
|
|
680
772
|
(pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
|
681
773
|
end
|
682
774
|
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
# @param str [String] The string of which to check the encoding
|
689
|
-
# @yield [msg] A block in which an encoding error can be raised.
|
690
|
-
# Only yields if there is an encoding error
|
691
|
-
# @yieldparam msg [String] The error message to be raised
|
692
|
-
# @return [String] `str`, potentially with encoding gotchas like BOMs removed
|
693
|
-
def check_encoding(str)
|
694
|
-
if ruby1_8?
|
695
|
-
return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
|
696
|
-
elsif str.valid_encoding?
|
697
|
-
# Get rid of the Unicode BOM if possible
|
698
|
-
if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
|
699
|
-
return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
|
700
|
-
else
|
701
|
-
return str
|
702
|
-
end
|
703
|
-
end
|
704
|
-
|
705
|
-
encoding = str.encoding
|
706
|
-
newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
|
707
|
-
str.force_encoding("binary").split(newlines).each_with_index do |line, i|
|
708
|
-
begin
|
709
|
-
line.encode(encoding)
|
710
|
-
rescue Encoding::UndefinedConversionError => e
|
711
|
-
yield <<MSG.rstrip, i + 1
|
712
|
-
Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}
|
713
|
-
MSG
|
714
|
-
end
|
715
|
-
end
|
716
|
-
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')
|
717
780
|
end
|
718
781
|
|
719
782
|
# Like {\#check\_encoding}, but also checks for a `@charset` declaration
|
720
783
|
# at the beginning of the file and uses that encoding if it exists.
|
721
784
|
#
|
722
|
-
#
|
723
|
-
# If a `@charset` declaration exists,
|
724
|
-
# we assume that that's the original encoding of the document.
|
725
|
-
# Otherwise, we use whatever encoding Ruby has.
|
726
|
-
# Then we convert that to UTF-8 to process internally.
|
727
|
-
# The UTF-8 end result is what's returned by this method.
|
785
|
+
# Sass follows CSS's decoding rules.
|
728
786
|
#
|
729
787
|
# @param str [String] The string of which to check the encoding
|
730
|
-
# @yield [msg] A block in which an encoding error can be raised.
|
731
|
-
# Only yields if there is an encoding error
|
732
|
-
# @yieldparam msg [String] The error message to be raised
|
733
788
|
# @return [(String, Encoding)] The original string encoded as UTF-8,
|
734
789
|
# and the source encoding of the string (or `nil` under Ruby 1.8)
|
735
790
|
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
736
791
|
# cannot be converted to UTF-8
|
737
792
|
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
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
|
745
804
|
end
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
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')
|
754
828
|
end
|
755
829
|
end
|
756
|
-
str.force_encoding(
|
757
|
-
elsif
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
return str.encode("UTF-8"), str.encoding
|
763
|
-
end
|
764
|
-
|
765
|
-
unless ruby1_8?
|
766
|
-
# @private
|
767
|
-
def _enc(string, encoding)
|
768
|
-
string.encode(encoding).force_encoding("BINARY")
|
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')
|
769
836
|
end
|
770
837
|
|
771
|
-
|
772
|
-
# but there's not really a good way to do that
|
773
|
-
# without manually checking that each encoding
|
774
|
-
# encodes all ASCII characters properly,
|
775
|
-
# which takes long enough to affect the startup time of the CLI.
|
776
|
-
ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
|
838
|
+
find_encoding_error(str) unless str.valid_encoding?
|
777
839
|
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
_enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
|
784
|
-
_enc("\uFEFF", e)})/)
|
785
|
-
rescue Encoding::ConverterNotFoundError => _
|
786
|
-
nil # JRuby on Java 5 doesn't support UTF-32
|
787
|
-
rescue
|
788
|
-
# /\A@charset "(.*?)"/
|
789
|
-
Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
|
790
|
-
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)
|
791
845
|
end
|
792
846
|
end
|
793
847
|
|
@@ -919,7 +973,7 @@ MSG
|
|
919
973
|
# @param obj {Object}
|
920
974
|
# @return {String}
|
921
975
|
def inspect_obj(obj)
|
922
|
-
return obj.inspect unless version_geq(
|
976
|
+
return obj.inspect unless version_geq(RUBY_VERSION, "1.9.2")
|
923
977
|
return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
|
924
978
|
return obj.inspect unless obj.is_a?(String)
|
925
979
|
'"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
|
@@ -1128,15 +1182,25 @@ MSG
|
|
1128
1182
|
# rename operation.
|
1129
1183
|
#
|
1130
1184
|
# @param filename [String] The file to write to.
|
1185
|
+
# @param perms [Integer] The permissions used for creating this file.
|
1186
|
+
# Will be masked by the process umask. Defaults to readable/writeable
|
1187
|
+
# by all users however the umask usually changes this to only be writable
|
1188
|
+
# by the process's user.
|
1131
1189
|
# @yieldparam tmpfile [Tempfile] The temp file that can be written to.
|
1132
1190
|
# @return The value returned by the block.
|
1133
|
-
def atomic_create_and_write_file(filename)
|
1191
|
+
def atomic_create_and_write_file(filename, perms = 0666)
|
1134
1192
|
require 'tempfile'
|
1135
1193
|
tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
|
1136
1194
|
tmpfile.binmode if tmpfile.respond_to?(:binmode)
|
1137
1195
|
result = yield tmpfile
|
1138
1196
|
tmpfile.close
|
1139
1197
|
ATOMIC_WRITE_MUTEX.synchronize do
|
1198
|
+
begin
|
1199
|
+
File.chmod(perms & ~File.umask, tmpfile.path)
|
1200
|
+
rescue Errno::EPERM
|
1201
|
+
# If we don't have permissions to chmod the file, don't let that crash
|
1202
|
+
# the compilation. See issue 1215.
|
1203
|
+
end
|
1140
1204
|
File.rename tmpfile.path, filename
|
1141
1205
|
end
|
1142
1206
|
result
|
@@ -1147,8 +1211,67 @@ MSG
|
|
1147
1211
|
tmpfile.unlink if tmpfile
|
1148
1212
|
end
|
1149
1213
|
|
1214
|
+
def load_listen!
|
1215
|
+
if defined?(gem)
|
1216
|
+
begin
|
1217
|
+
gem 'listen', '>= 1.1.0', '< 3.0.0'
|
1218
|
+
require 'listen'
|
1219
|
+
rescue Gem::LoadError
|
1220
|
+
dir = scope("vendor/listen/lib")
|
1221
|
+
$LOAD_PATH.unshift dir
|
1222
|
+
begin
|
1223
|
+
require 'listen'
|
1224
|
+
rescue LoadError => e
|
1225
|
+
if version_geq(RUBY_VERSION, "1.9.3")
|
1226
|
+
version_constraint = "~> 2.7"
|
1227
|
+
else
|
1228
|
+
version_constraint = "~> 1.1"
|
1229
|
+
end
|
1230
|
+
e.message << "\n" <<
|
1231
|
+
"Run \"gem install listen --version '#{version_constraint}'\" to get it."
|
1232
|
+
raise e
|
1233
|
+
end
|
1234
|
+
end
|
1235
|
+
else
|
1236
|
+
begin
|
1237
|
+
require 'listen'
|
1238
|
+
rescue LoadError => e
|
1239
|
+
dir = scope("vendor/listen/lib")
|
1240
|
+
if $LOAD_PATH.include?(dir)
|
1241
|
+
raise e unless File.exist?(scope(".git"))
|
1242
|
+
e.message << "\n" <<
|
1243
|
+
'Run "git submodule update --init" to get the bundled version.'
|
1244
|
+
else
|
1245
|
+
$LOAD_PATH.unshift dir
|
1246
|
+
retry
|
1247
|
+
end
|
1248
|
+
end
|
1249
|
+
end
|
1250
|
+
end
|
1251
|
+
|
1150
1252
|
private
|
1151
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
|
+
|
1152
1275
|
# rubocop:disable LineLength
|
1153
1276
|
|
1154
1277
|
# Calculates the memoization table for the Least Common Subsequence algorithm.
|
data/lib/sass/version.rb
CHANGED
@@ -85,20 +85,20 @@ module Sass
|
|
85
85
|
private
|
86
86
|
|
87
87
|
def revision_number
|
88
|
-
if File.
|
88
|
+
if File.exist?(Sass::Util.scope('REVISION'))
|
89
89
|
rev = File.read(Sass::Util.scope('REVISION')).strip
|
90
90
|
return rev unless rev =~ /^([a-f0-9]+|\(.*\))$/ || rev == '(unknown)'
|
91
91
|
end
|
92
92
|
|
93
|
-
return unless File.
|
93
|
+
return unless File.exist?(Sass::Util.scope('.git/HEAD'))
|
94
94
|
rev = File.read(Sass::Util.scope('.git/HEAD')).strip
|
95
95
|
return rev unless rev =~ /^ref: (.*)$/
|
96
96
|
|
97
97
|
ref_name = $1
|
98
98
|
ref_file = Sass::Util.scope(".git/#{ref_name}")
|
99
99
|
info_file = Sass::Util.scope(".git/info/refs")
|
100
|
-
return File.read(ref_file).strip if File.
|
101
|
-
return unless File.
|
100
|
+
return File.read(ref_file).strip if File.exist?(ref_file)
|
101
|
+
return unless File.exist?(info_file)
|
102
102
|
File.open(info_file) do |f|
|
103
103
|
f.each do |l|
|
104
104
|
sha, ref = l.strip.split("\t", 2)
|
@@ -110,7 +110,7 @@ module Sass
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def version_date
|
113
|
-
return unless File.
|
113
|
+
return unless File.exist?(Sass::Util.scope('VERSION_DATE'))
|
114
114
|
DateTime.parse(File.read(Sass::Util.scope('VERSION_DATE')).strip)
|
115
115
|
end
|
116
116
|
end
|
data/lib/sass.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
dir = File.dirname(__FILE__)
|
2
2
|
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
|
3
3
|
|
4
|
-
# This is necessary to set so that the Haml code that tries to load Sass
|
5
|
-
# knows that Sass is indeed loading,
|
6
|
-
# even if there's some crazy autoload stuff going on.
|
7
|
-
SASS_BEGUN_TO_LOAD = true unless defined?(SASS_BEGUN_TO_LOAD)
|
8
|
-
|
9
4
|
require 'sass/version'
|
10
5
|
|
11
6
|
# The module that contains everything Sass-related:
|