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.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
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'
@@ -12,12 +13,14 @@ require 'sass/util/subset_map'
12
13
 
13
14
  module Sass
14
15
  # A module containing various useful functions.
16
+ # @comment
17
+ # rubocop:disable ModuleLength
15
18
  module Util
16
19
  extend self
17
20
 
18
21
  # An array of ints representing the Ruby version number.
19
22
  # @api public
20
- RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
23
+ RUBY_VERSION_COMPONENTS = RUBY_VERSION.split(".").map {|s| s.to_i}
21
24
 
22
25
  # The Ruby engine we're running under. Defaults to `"ruby"`
23
26
  # if the top-level constant is undefined.
@@ -137,6 +140,18 @@ module Sass
137
140
  [[value, range.first].max, range.last].min
138
141
  end
139
142
 
143
+ # Like [Fixnum.round], but leaves rooms for slight floating-point
144
+ # differences.
145
+ #
146
+ # @param value [Numeric]
147
+ # @return [Numeric]
148
+ def round(value)
149
+ # If the number is within epsilon of X.5, round up (or down for negative
150
+ # numbers).
151
+ return value.round if (value % 1) - 0.5 <= -1 * Script::Value::Number.epsilon
152
+ value > 0 ? value.ceil : value.floor
153
+ end
154
+
140
155
  # Concatenates all strings that are adjacent in an array,
141
156
  # while leaving other elements as they are.
142
157
  #
@@ -162,6 +177,43 @@ module Sass
162
177
  end
163
178
  end
164
179
 
180
+ # Non-destructively replaces all occurrences of a subsequence in an array
181
+ # with another subsequence.
182
+ #
183
+ # @example
184
+ # replace_subseq([1, 2, 3, 4, 5], [2, 3], [:a, :b])
185
+ # #=> [1, :a, :b, 4, 5]
186
+ #
187
+ # @param arr [Array] The array whose subsequences will be replaced.
188
+ # @param subseq [Array] The subsequence to find and replace.
189
+ # @param replacement [Array] The sequence that `subseq` will be replaced with.
190
+ # @return [Array] `arr` with `subseq` replaced with `replacement`.
191
+ def replace_subseq(arr, subseq, replacement)
192
+ new = []
193
+ matched = []
194
+ i = 0
195
+ arr.each do |elem|
196
+ if elem != subseq[i]
197
+ new.push(*matched)
198
+ matched = []
199
+ i = 0
200
+ new << elem
201
+ next
202
+ end
203
+
204
+ if i == subseq.length - 1
205
+ matched = []
206
+ i = 0
207
+ new.push(*replacement)
208
+ else
209
+ matched << elem
210
+ i += 1
211
+ end
212
+ end
213
+ new.push(*matched)
214
+ new
215
+ end
216
+
165
217
  # Intersperses a value in an enumerable, as would be done with `Array#join`
166
218
  # but without concatenating the array together afterwards.
167
219
  #
@@ -227,7 +279,7 @@ module Sass
227
279
  # # [2, 4, 5]]
228
280
  def paths(arrs)
229
281
  arrs.inject([[]]) do |paths, arr|
230
- flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
282
+ arr.map {|e| paths.map {|path| path + [e]}}.flatten(1)
231
283
  end
232
284
  end
233
285
 
@@ -260,7 +312,7 @@ module Sass
260
312
  # @return [Array]
261
313
  def hash_to_a(hash)
262
314
  return hash.to_a unless ruby1_8? || defined?(Test::Unit)
263
- hash.sort_by {|k, v| k}
315
+ hash.sort_by {|k, _v| k}
264
316
  end
265
317
 
266
318
  # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
@@ -287,6 +339,18 @@ module Sass
287
339
  arr
288
340
  end
289
341
 
342
+ # Like `String.upcase`, but only ever upcases ASCII letters.
343
+ def upcase(string)
344
+ return string.upcase unless ruby2_4?
345
+ string.upcase(:ascii)
346
+ end
347
+
348
+ # Like `String.downcase`, but only ever downcases ASCII letters.
349
+ def downcase(string)
350
+ return string.downcase unless ruby2_4?
351
+ string.downcase(:ascii)
352
+ end
353
+
290
354
  # Returns a sub-array of `minuend` containing only elements that are also in
291
355
  # `subtrahend`. Ensures that the return value has the same order as
292
356
  # `minuend`, even on Rubinius where that's not guaranteed by `Array#-`.
@@ -300,6 +364,18 @@ module Sass
300
364
  minuend.select {|e| set.include?(e)}
301
365
  end
302
366
 
367
+ # Returns the maximum of `val1` and `val2`. We use this over \{Array.max} to
368
+ # avoid unnecessary garbage collection.
369
+ def max(val1, val2)
370
+ val1 > val2 ? val1 : val2
371
+ end
372
+
373
+ # Returns the minimum of `val1` and `val2`. We use this over \{Array.min} to
374
+ # avoid unnecessary garbage collection.
375
+ def min(val1, val2)
376
+ val1 <= val2 ? val1 : val2
377
+ end
378
+
303
379
  # Returns a string description of the character that caused an
304
380
  # `Encoding::UndefinedConversionError`.
305
381
  #
@@ -354,13 +430,13 @@ module Sass
354
430
  # Returns information about the caller of the previous method.
355
431
  #
356
432
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
357
- # @return [[String, Fixnum, (String, nil)]]
433
+ # @return [[String, Integer, (String, nil)]]
358
434
  # An array containing the filename, line, and method name of the caller.
359
435
  # The method name may be nil
360
436
  def caller_info(entry = nil)
361
437
  # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
362
438
  entry ||= caller[1]
363
- info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
439
+ info = entry.scan(/^((?:[A-Za-z]:)?.*?):(-?.*?)(?::.*`(.+)')?$/).first
364
440
  info[1] = info[1].to_i
365
441
  # This is added by Rubinius to designate a block, but we don't care about it.
366
442
  info[2].sub!(/ \{\}\Z/, '') if info[2]
@@ -448,7 +524,7 @@ module Sass
448
524
  #
449
525
  # @param msg [String]
450
526
  def sass_warn(msg)
451
- msg = msg + "\n" unless ruby1?
527
+ msg += "\n" unless ruby1?
452
528
  Sass.logger.warn(msg)
453
529
  end
454
530
 
@@ -507,8 +583,17 @@ module Sass
507
583
  #
508
584
  # @return [Boolean]
509
585
  def listen_geq_2?
510
- require 'listen/version'
511
- version_geq(::Listen::VERSION, '2.0.0')
586
+ return @listen_geq_2 if defined?(@listen_geq_2)
587
+ @listen_geq_2 =
588
+ begin
589
+ # Make sure we're loading listen/version from the same place that
590
+ # we're loading listen itself.
591
+ load_listen!
592
+ require 'listen/version'
593
+ version_geq(::Listen::VERSION, '2.0.0')
594
+ rescue LoadError
595
+ false
596
+ end
512
597
  end
513
598
 
514
599
  # Returns an ActionView::Template* class.
@@ -563,7 +648,7 @@ module Sass
563
648
 
564
649
  # Returns an array of ints representing the JRuby version number.
565
650
  #
566
- # @return [Array<Fixnum>]
651
+ # @return [Array<Integer>]
567
652
  def jruby_version
568
653
  @jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
569
654
  end
@@ -572,7 +657,7 @@ module Sass
572
657
  #
573
658
  # @param path [String]
574
659
  def glob(path)
575
- path = path.gsub('\\', '/') if windows?
660
+ path = path.tr('\\', '/') if windows?
576
661
  if block_given?
577
662
  Dir.glob(path) {|f| yield(f)}
578
663
  else
@@ -583,13 +668,99 @@ module Sass
583
668
  # Like `Pathname.new`, but normalizes Windows paths to always use backslash
584
669
  # separators.
585
670
  #
586
- # `Pathname.relative_path_from` can break if the two pathnames aren't
671
+ # `Pathname#relative_path_from` can break if the two pathnames aren't
587
672
  # consistent in their slash style.
673
+ #
674
+ # @param path [String]
675
+ # @return [Pathname]
588
676
  def pathname(path)
589
677
  path = path.tr("/", "\\") if windows?
590
678
  Pathname.new(path)
591
679
  end
592
680
 
681
+ # Like `Pathname#cleanpath`, but normalizes Windows paths to always use
682
+ # backslash separators. Normally, `Pathname#cleanpath` actually does the
683
+ # reverse -- it will convert backslashes to forward slashes, which can break
684
+ # `Pathname#relative_path_from`.
685
+ #
686
+ # @param path [String, Pathname]
687
+ # @return [Pathname]
688
+ def cleanpath(path)
689
+ path = Pathname.new(path) unless path.is_a?(Pathname)
690
+ pathname(path.cleanpath.to_s)
691
+ end
692
+
693
+ # Returns `path` with all symlinks resolved.
694
+ #
695
+ # @param path [String, Pathname]
696
+ # @return [Pathname]
697
+ def realpath(path)
698
+ path = Pathname.new(path) unless path.is_a?(Pathname)
699
+
700
+ # Explicitly DON'T run #pathname here. We don't want to convert
701
+ # to Windows directory separators because we're comparing these
702
+ # against the paths returned by Listen, which use forward
703
+ # slashes everywhere.
704
+ begin
705
+ path.realpath
706
+ rescue SystemCallError
707
+ # If [path] doesn't actually exist, don't bail, just
708
+ # return the original.
709
+ path
710
+ end
711
+ end
712
+
713
+ # Returns `path` relative to `from`.
714
+ #
715
+ # This is like `Pathname#relative_path_from` except it accepts both strings
716
+ # and pathnames, it handles Windows path separators correctly, and it throws
717
+ # an error rather than crashing if the paths use different encodings
718
+ # (https://github.com/ruby/ruby/pull/713).
719
+ #
720
+ # @param path [String, Pathname]
721
+ # @param from [String, Pathname]
722
+ # @return [Pathname?]
723
+ def relative_path_from(path, from)
724
+ pathname(path.to_s).relative_path_from(pathname(from.to_s))
725
+ rescue NoMethodError => e
726
+ raise e unless e.name == :zero?
727
+
728
+ # Work around https://github.com/ruby/ruby/pull/713.
729
+ path = path.to_s
730
+ from = from.to_s
731
+ raise ArgumentError("Incompatible path encodings: #{path.inspect} is #{path.encoding}, " +
732
+ "#{from.inspect} is #{from.encoding}")
733
+ end
734
+
735
+ # Converts `path` to a "file:" URI. This handles Windows paths correctly.
736
+ #
737
+ # @param path [String, Pathname]
738
+ # @return [String]
739
+ def file_uri_from_path(path)
740
+ path = path.to_s if path.is_a?(Pathname)
741
+ path = path.tr('\\', '/') if windows?
742
+ path = Sass::Util.escape_uri(path)
743
+ return path.start_with?('/') ? "file://" + path : path unless windows?
744
+ return "file:///" + path.tr("\\", "/") if path =~ %r{^[a-zA-Z]:[/\\]}
745
+ return "file:" + path.tr("\\", "/") if path =~ %r{\\\\[^\\]+\\[^\\/]+}
746
+ path.tr("\\", "/")
747
+ end
748
+
749
+ # Retries a filesystem operation if it fails on Windows. Windows
750
+ # has weird and flaky locking rules that can cause operations to fail.
751
+ #
752
+ # @yield [] The filesystem operation.
753
+ def retry_on_windows
754
+ return yield unless windows?
755
+
756
+ begin
757
+ yield
758
+ rescue SystemCallError
759
+ sleep 0.1
760
+ yield
761
+ end
762
+ end
763
+
593
764
  # Prepare a value for a destructuring assignment (e.g. `a, b =
594
765
  # val`). This works around a performance bug when using
595
766
  # ActiveSupport, and only needs to be called when `val` is likely
@@ -610,7 +781,7 @@ module Sass
610
781
  # @return [Boolean]
611
782
  def ruby1?
612
783
  return @ruby1 if defined?(@ruby1)
613
- @ruby1 = Sass::Util::RUBY_VERSION[0] <= 1
784
+ @ruby1 = RUBY_VERSION_COMPONENTS[0] <= 1
614
785
  end
615
786
 
616
787
  # Whether or not this is running under Ruby 1.8 or lower.
@@ -624,16 +795,28 @@ module Sass
624
795
  # We have to fall back to 1.8 behavior.
625
796
  return @ruby1_8 if defined?(@ruby1_8)
626
797
  @ruby1_8 = ironruby? ||
627
- (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
798
+ (RUBY_VERSION_COMPONENTS[0] == 1 && RUBY_VERSION_COMPONENTS[1] < 9)
799
+ end
800
+
801
+ # Whether or not this is running under Ruby 1.9.2 exactly.
802
+ #
803
+ # @return [Boolean]
804
+ def ruby1_9_2?
805
+ return @ruby1_9_2 if defined?(@ruby1_9_2)
806
+ @ruby1_9_2 = RUBY_VERSION_COMPONENTS == [1, 9, 2]
628
807
  end
629
808
 
630
- # Whether or not this is running under Ruby 1.8.6 or lower.
631
- # Note that lower versions are not officially supported.
809
+ # Whether or not this is running under Ruby 2.4 or higher.
632
810
  #
633
811
  # @return [Boolean]
634
- def ruby1_8_6?
635
- return @ruby1_8_6 if defined?(@ruby1_8_6)
636
- @ruby1_8_6 = ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
812
+ def ruby2_4?
813
+ return @ruby2_4 if defined?(@ruby2_4)
814
+ @ruby2_4 =
815
+ if RUBY_VERSION_COMPONENTS[0] == 2
816
+ RUBY_VERSION_COMPONENTS[1] >= 4
817
+ else
818
+ RUBY_VERSION_COMPONENTS[0] > 2
819
+ end
637
820
  end
638
821
 
639
822
  # Wehter or not this is running under JRuby 1.6 or lower.
@@ -677,117 +860,80 @@ module Sass
677
860
  end
678
861
 
679
862
  return Hash[pairs_or_hash] unless ruby1_8?
680
- (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
863
+ (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*pairs_or_hash.flatten(1)]
681
864
  end
682
865
 
683
- # Checks that the encoding of a string is valid in Ruby 1.9
684
- # and cleans up potential encoding gotchas like the UTF-8 BOM.
685
- # If it's not, yields an error string describing the invalid character
686
- # and the line on which it occurrs.
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
866
+ unless ruby1_8?
867
+ CHARSET_REGEXP = /\A@charset "([^"]+)"/
868
+ bom = "\uFEFF"
869
+ UTF_8_BOM = bom.encode("UTF-8").force_encoding('BINARY')
870
+ UTF_16BE_BOM = bom.encode("UTF-16BE").force_encoding('BINARY')
871
+ UTF_16LE_BOM = bom.encode("UTF-16LE").force_encoding('BINARY')
717
872
  end
718
873
 
719
874
  # Like {\#check\_encoding}, but also checks for a `@charset` declaration
720
875
  # at the beginning of the file and uses that encoding if it exists.
721
876
  #
722
- # The Sass encoding rules are simple.
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.
877
+ # Sass follows CSS's decoding rules.
728
878
  #
729
879
  # @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
880
  # @return [(String, Encoding)] The original string encoded as UTF-8,
734
881
  # and the source encoding of the string (or `nil` under Ruby 1.8)
735
882
  # @raise [Encoding::UndefinedConversionError] if the source encoding
736
883
  # cannot be converted to UTF-8
737
884
  # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
738
- def check_sass_encoding(str, &block)
739
- return check_encoding(str, &block), nil if ruby1_8?
740
- # We allow any printable ASCII characters but double quotes in the charset decl
741
- bin = str.dup.force_encoding("BINARY")
742
- encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
743
- re = Sass::Util::CHARSET_REGEXPS[enc]
744
- re && bin =~ re
885
+ # @raise [Sass::SyntaxError] If the document declares an encoding that
886
+ # doesn't match its contents, or it doesn't declare an encoding and its
887
+ # contents are invalid in the native encoding.
888
+ def check_sass_encoding(str)
889
+ # On Ruby 1.8 we can't do anything complicated with encodings.
890
+ # Instead, we just strip out a UTF-8 BOM if it exists and
891
+ # sanitize according to Section 3.3 of CSS Syntax Level 3. We
892
+ # don't sanitize null characters since they might be components
893
+ # of other characters.
894
+ if ruby1_8?
895
+ return str.gsub(/\A\xEF\xBB\xBF/, '').gsub(/\r\n?|\f/, "\n"), nil
745
896
  end
746
- charset, bom = $1, $2
747
- if charset
748
- charset = charset.force_encoding(encoding).encode("UTF-8")
749
- if (endianness = encoding[/[BL]E$/])
750
- begin
751
- Encoding.find(charset + endianness)
752
- charset << endianness
753
- rescue ArgumentError # Encoding charset + endianness doesn't exist
897
+
898
+ # Determine the fallback encoding following section 3.2 of CSS Syntax Level 3 and Encodings:
899
+ # http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#determine-the-fallback-encoding
900
+ # http://encoding.spec.whatwg.org/#decode
901
+ binary = str.dup.force_encoding("BINARY")
902
+ if binary.start_with?(UTF_8_BOM)
903
+ binary.slice! 0, UTF_8_BOM.length
904
+ str = binary.force_encoding('UTF-8')
905
+ elsif binary.start_with?(UTF_16BE_BOM)
906
+ binary.slice! 0, UTF_16BE_BOM.length
907
+ str = binary.force_encoding('UTF-16BE')
908
+ elsif binary.start_with?(UTF_16LE_BOM)
909
+ binary.slice! 0, UTF_16LE_BOM.length
910
+ str = binary.force_encoding('UTF-16LE')
911
+ elsif binary =~ CHARSET_REGEXP
912
+ charset = $1.force_encoding('US-ASCII')
913
+ # Ruby 1.9.2 doesn't recognize a UTF-16 encoding without an endian marker.
914
+ if ruby1_9_2? && charset.downcase == 'utf-16'
915
+ encoding = Encoding.find('UTF-8')
916
+ else
917
+ encoding = Encoding.find(charset)
918
+ if encoding.name == 'UTF-16' || encoding.name == 'UTF-16BE'
919
+ encoding = Encoding.find('UTF-8')
754
920
  end
755
921
  end
756
- str.force_encoding(charset)
757
- elsif bom
758
- str.force_encoding(encoding)
922
+ str = binary.force_encoding(encoding)
923
+ elsif str.encoding.name == "ASCII-8BIT"
924
+ # Normally we want to fall back on believing the Ruby string
925
+ # encoding, but if that's just binary we want to make sure
926
+ # it's valid UTF-8.
927
+ str = str.force_encoding('utf-8')
759
928
  end
760
929
 
761
- str = check_encoding(str, &block)
762
- return str.encode("UTF-8"), str.encoding
763
- end
930
+ find_encoding_error(str) unless str.valid_encoding?
764
931
 
765
- unless ruby1_8?
766
- # @private
767
- def _enc(string, encoding)
768
- string.encode(encoding).force_encoding("BINARY")
769
- end
770
-
771
- # We could automatically add in any non-ASCII-compatible encodings here,
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]
777
-
778
- CHARSET_REGEXPS = Hash.new do |h, e|
779
- h[e] =
780
- begin
781
- # /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
782
- Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
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
932
+ begin
933
+ # If the string is valid, preprocess it according to section 3.3 of CSS Syntax Level 3.
934
+ return str.encode("UTF-8").gsub(/\r\n?|\f/, "\n").tr("\u0000", "�"), str.encoding
935
+ rescue EncodingError
936
+ find_encoding_error(str)
791
937
  end
792
938
  end
793
939
 
@@ -820,7 +966,7 @@ MSG
820
966
  # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
821
967
  #
822
968
  # @param enum [Enumerable] The enumerable to get the enumerator for
823
- # @param n [Fixnum] The size of each cons
969
+ # @param n [Integer] The size of each cons
824
970
  # @return [Enumerator] The consed enumerator
825
971
  def enum_cons(enum, n)
826
972
  ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
@@ -829,7 +975,7 @@ MSG
829
975
  # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
830
976
  #
831
977
  # @param enum [Enumerable] The enumerable to get the enumerator for
832
- # @param n [Fixnum] The size of each slice
978
+ # @param n [Integer] The size of each slice
833
979
  # @return [Enumerator] The consed enumerator
834
980
  def enum_slice(enum, n)
835
981
  ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
@@ -856,22 +1002,11 @@ MSG
856
1002
  # Returns the ASCII code of the given character.
857
1003
  #
858
1004
  # @param c [String] All characters but the first are ignored.
859
- # @return [Fixnum] The ASCII code of `c`.
1005
+ # @return [Integer] The ASCII code of `c`.
860
1006
  def ord(c)
861
1007
  ruby1_8? ? c[0] : c.ord
862
1008
  end
863
1009
 
864
- # Flattens the first `n` nested arrays in a cross-version manner.
865
- #
866
- # @param arr [Array] The array to flatten
867
- # @param n [Fixnum] The number of levels to flatten
868
- # @return [Array] The flattened array
869
- def flatten(arr, n)
870
- return arr.flatten(n) unless ruby1_8_6?
871
- return arr if n == 0
872
- arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
873
- end
874
-
875
1010
  # Flattens the first level of nested arrays in `arrs`. Unlike
876
1011
  # `Array#flatten`, this orders the result by taking the first
877
1012
  # values from each array in order, then the second, and so on.
@@ -890,27 +1025,6 @@ MSG
890
1025
  result
891
1026
  end
892
1027
 
893
- # Returns the hash code for a set in a cross-version manner.
894
- # Aggravatingly, this is order-dependent in Ruby 1.8.6.
895
- #
896
- # @param set [Set]
897
- # @return [Fixnum] The order-independent hashcode of `set`
898
- def set_hash(set)
899
- return set.hash unless ruby1_8_6?
900
- set.map {|e| e.hash}.uniq.sort.hash
901
- end
902
-
903
- # Tests the hash-equality of two sets in a cross-version manner.
904
- # Aggravatingly, this is order-dependent in Ruby 1.8.6.
905
- #
906
- # @param set1 [Set]
907
- # @param set2 [Set]
908
- # @return [Boolean] Whether or not the sets are hashcode equal
909
- def set_eql?(set1, set2)
910
- return set1.eql?(set2) unless ruby1_8_6?
911
- set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
912
- end
913
-
914
1028
  # Like `Object#inspect`, but preserves non-ASCII characters rather than
915
1029
  # escaping them under Ruby 1.9.2. This is necessary so that the
916
1030
  # precompiled Haml template can be `#encode`d into `@options[:encoding]`
@@ -919,7 +1033,7 @@ MSG
919
1033
  # @param obj {Object}
920
1034
  # @return {String}
921
1035
  def inspect_obj(obj)
922
- return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
1036
+ return obj.inspect unless version_geq(RUBY_VERSION, "1.9.2")
923
1037
  return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
924
1038
  return obj.inspect unless obj.is_a?(String)
925
1039
  '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
@@ -1013,11 +1127,11 @@ MSG
1013
1127
 
1014
1128
  # Converts the argument into a valid JSON value.
1015
1129
  #
1016
- # @param v [Fixnum, String, Array, Boolean, nil]
1130
+ # @param v [Integer, String, Array, Boolean, nil]
1017
1131
  # @return [String]
1018
1132
  def json_value_of(v)
1019
1133
  case v
1020
- when Fixnum
1134
+ when Integer
1021
1135
  v.to_s
1022
1136
  when String
1023
1137
  "\"" + json_escape_string(v) + "\""
@@ -1039,7 +1153,7 @@ MSG
1039
1153
  VLQ_BASE_MASK = VLQ_BASE - 1
1040
1154
  VLQ_CONTINUATION_BIT = VLQ_BASE
1041
1155
 
1042
- BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1156
+ BASE64_DIGITS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']
1043
1157
  BASE64_DIGIT_MAP = begin
1044
1158
  map = {}
1045
1159
  Sass::Util.enum_with_index(BASE64_DIGITS).map do |digit, i|
@@ -1050,7 +1164,7 @@ MSG
1050
1164
 
1051
1165
  # Encodes `value` as VLQ (http://en.wikipedia.org/wiki/VLQ).
1052
1166
  #
1053
- # @param value [Fixnum]
1167
+ # @param value [Integer]
1054
1168
  # @return [String] The encoded value
1055
1169
  def encode_vlq(value)
1056
1170
  if value < 0
@@ -1128,15 +1242,25 @@ MSG
1128
1242
  # rename operation.
1129
1243
  #
1130
1244
  # @param filename [String] The file to write to.
1245
+ # @param perms [Integer] The permissions used for creating this file.
1246
+ # Will be masked by the process umask. Defaults to readable/writeable
1247
+ # by all users however the umask usually changes this to only be writable
1248
+ # by the process's user.
1131
1249
  # @yieldparam tmpfile [Tempfile] The temp file that can be written to.
1132
1250
  # @return The value returned by the block.
1133
- def atomic_create_and_write_file(filename)
1251
+ def atomic_create_and_write_file(filename, perms = 0666)
1134
1252
  require 'tempfile'
1135
1253
  tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
1136
1254
  tmpfile.binmode if tmpfile.respond_to?(:binmode)
1137
1255
  result = yield tmpfile
1138
1256
  tmpfile.close
1139
1257
  ATOMIC_WRITE_MUTEX.synchronize do
1258
+ begin
1259
+ File.chmod(perms & ~File.umask, tmpfile.path)
1260
+ rescue Errno::EPERM
1261
+ # If we don't have permissions to chmod the file, don't let that crash
1262
+ # the compilation. See issue 1215.
1263
+ end
1140
1264
  File.rename tmpfile.path, filename
1141
1265
  end
1142
1266
  result
@@ -1147,15 +1271,71 @@ MSG
1147
1271
  tmpfile.unlink if tmpfile
1148
1272
  end
1149
1273
 
1274
+ def load_listen!
1275
+ if defined?(gem)
1276
+ begin
1277
+ gem 'listen', '>= 1.1.0', '< 3.0.0'
1278
+ require 'listen'
1279
+ rescue Gem::LoadError
1280
+ dir = scope("vendor/listen/lib")
1281
+ $LOAD_PATH.unshift dir
1282
+ begin
1283
+ require 'listen'
1284
+ rescue LoadError => e
1285
+ if version_geq(RUBY_VERSION, "1.9.3")
1286
+ version_constraint = "~> 3.0"
1287
+ else
1288
+ version_constraint = "~> 1.1"
1289
+ end
1290
+ e.message << "\n" <<
1291
+ "Run \"gem install listen --version '#{version_constraint}'\" to get it."
1292
+ raise e
1293
+ end
1294
+ end
1295
+ else
1296
+ begin
1297
+ require 'listen'
1298
+ rescue LoadError => e
1299
+ dir = scope("vendor/listen/lib")
1300
+ if $LOAD_PATH.include?(dir)
1301
+ raise e unless File.exist?(scope(".git"))
1302
+ e.message << "\n" <<
1303
+ 'Run "git submodule update --init" to get the bundled version.'
1304
+ else
1305
+ $LOAD_PATH.unshift dir
1306
+ retry
1307
+ end
1308
+ end
1309
+ end
1310
+ end
1311
+
1150
1312
  private
1151
1313
 
1152
- # rubocop:disable LineLength
1314
+ def find_encoding_error(str)
1315
+ encoding = str.encoding
1316
+ cr = Regexp.quote("\r".encode(encoding).force_encoding('BINARY'))
1317
+ lf = Regexp.quote("\n".encode(encoding).force_encoding('BINARY'))
1318
+ ff = Regexp.quote("\f".encode(encoding).force_encoding('BINARY'))
1319
+ line_break = /#{cr}#{lf}?|#{ff}|#{lf}/
1320
+
1321
+ str.force_encoding("binary").split(line_break).each_with_index do |line, i|
1322
+ begin
1323
+ line.encode(encoding)
1324
+ rescue Encoding::UndefinedConversionError => e
1325
+ raise Sass::SyntaxError.new(
1326
+ "Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}",
1327
+ :line => i + 1)
1328
+ end
1329
+ end
1330
+
1331
+ # We shouldn't get here, but it's possible some weird encoding stuff causes it.
1332
+ return str, str.encoding
1333
+ end
1153
1334
 
1154
1335
  # Calculates the memoization table for the Least Common Subsequence algorithm.
1155
1336
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
1156
1337
  def lcs_table(x, y)
1157
1338
  # This method does not take a block as an explicit parameter for performance reasons.
1158
- # rubocop:enable LineLength
1159
1339
  c = Array.new(x.size) {[]}
1160
1340
  x.size.times {|i| c[i][0] = 0}
1161
1341
  y.size.times {|j| c[0][j] = 0}
@@ -1171,12 +1351,12 @@ MSG
1171
1351
  end
1172
1352
  c
1173
1353
  end
1174
- # rubocop:disable ParameterLists, LineLength
1354
+ # rubocop:disable ParameterLists
1175
1355
 
1176
1356
  # Computes a single longest common subsequence for arrays x and y.
1177
1357
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
1178
1358
  def lcs_backtrace(c, x, y, i, j, &block)
1179
- # rubocop:enable ParameterList, LineLengths
1359
+ # rubocop:enable ParameterList
1180
1360
  return [] if i == 0 || j == 0
1181
1361
  if (v = yield(x[i], y[j]))
1182
1362
  return lcs_backtrace(c, x, y, i - 1, j - 1, &block) << v