sass 3.3.0.rc.2 → 3.3.0.rc.3
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 +15 -0
- data/CONTRIBUTING +1 -1
- data/README.md +7 -7
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/bin/sass +5 -1
- data/bin/sass-convert +5 -1
- data/bin/scss +5 -1
- data/ext/mkrf_conf.rb +23 -0
- data/lib/sass/css.rb +1 -1
- data/lib/sass/engine.rb +19 -9
- data/lib/sass/environment.rb +8 -0
- data/lib/sass/exec.rb +4 -4
- data/lib/sass/features.rb +0 -1
- data/lib/sass/importers/base.rb +13 -6
- data/lib/sass/importers/deprecated_path.rb +8 -2
- data/lib/sass/importers/filesystem.rb +33 -7
- data/lib/sass/logger.rb +1 -4
- data/lib/sass/logger/base.rb +0 -2
- data/lib/sass/logger/log_level.rb +0 -2
- data/lib/sass/plugin.rb +2 -2
- data/lib/sass/plugin/compiler.rb +23 -11
- data/lib/sass/plugin/configuration.rb +0 -1
- data/lib/sass/railtie.rb +1 -0
- data/lib/sass/script/css_lexer.rb +0 -1
- data/lib/sass/script/css_parser.rb +0 -1
- data/lib/sass/script/functions.rb +158 -96
- data/lib/sass/script/lexer.rb +29 -35
- data/lib/sass/script/parser.rb +10 -3
- data/lib/sass/script/tree.rb +0 -1
- data/lib/sass/script/tree/funcall.rb +21 -5
- data/lib/sass/script/tree/list_literal.rb +0 -1
- data/lib/sass/script/value/arg_list.rb +7 -3
- data/lib/sass/script/value/bool.rb +0 -1
- data/lib/sass/script/value/null.rb +0 -1
- data/lib/sass/script/value/number.rb +2 -6
- data/lib/sass/scss/css_parser.rb +0 -1
- data/lib/sass/scss/parser.rb +5 -5
- data/lib/sass/scss/script_lexer.rb +0 -1
- data/lib/sass/scss/script_parser.rb +0 -1
- data/lib/sass/selector.rb +11 -1
- data/lib/sass/selector/comma_sequence.rb +3 -4
- data/lib/sass/selector/sequence.rb +11 -7
- data/lib/sass/selector/simple_sequence.rb +42 -11
- data/lib/sass/source/map.rb +6 -19
- data/lib/sass/tree/at_root_node.rb +1 -1
- data/lib/sass/tree/mixin_node.rb +2 -2
- data/lib/sass/tree/prop_node.rb +0 -1
- data/lib/sass/tree/variable_node.rb +0 -5
- data/lib/sass/tree/visitors/check_nesting.rb +0 -1
- data/lib/sass/tree/visitors/convert.rb +2 -2
- data/lib/sass/tree/visitors/cssize.rb +184 -84
- data/lib/sass/tree/visitors/deep_copy.rb +0 -1
- data/lib/sass/tree/visitors/perform.rb +14 -44
- data/lib/sass/util.rb +59 -12
- data/lib/sass/util/cross_platform_random.rb +19 -0
- data/lib/sass/util/normalized_map.rb +17 -1
- data/test/sass/compiler_test.rb +10 -0
- data/test/sass/conversion_test.rb +36 -0
- data/test/sass/css2sass_test.rb +19 -0
- data/test/sass/engine_test.rb +54 -105
- data/test/sass/functions_test.rb +233 -26
- data/test/sass/importer_test.rb +72 -10
- data/test/sass/plugin_test.rb +14 -0
- data/test/sass/script_conversion_test.rb +4 -4
- data/test/sass/script_test.rb +58 -21
- data/test/sass/scss/css_test.rb +8 -1
- data/test/sass/scss/scss_test.rb +376 -179
- data/test/sass/source_map_test.rb +8 -0
- data/test/sass/templates/subdir/import_up1.scss +1 -0
- data/test/sass/templates/subdir/import_up2.scss +1 -0
- data/test/sass/util_test.rb +16 -0
- data/test/test_helper.rb +12 -4
- metadata +269 -287
- data/lib/sass/script/tree/selector.rb +0 -30
@@ -55,15 +55,14 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
55
55
|
splat_sep = splat.separator
|
56
56
|
end
|
57
57
|
|
58
|
-
keywords = keywords.dup
|
59
58
|
env = Sass::Environment.new(callable.environment)
|
60
59
|
callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
|
61
|
-
if value && keywords.
|
60
|
+
if value && keywords.has_key?(var.name)
|
62
61
|
raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name} " +
|
63
62
|
"both by position and by name.")
|
64
63
|
end
|
65
64
|
|
66
|
-
value ||= keywords.delete(var.
|
65
|
+
value ||= keywords.delete(var.name)
|
67
66
|
value ||= default && default.perform(env)
|
68
67
|
raise Sass::SyntaxError.new("#{desc} is missing argument #{var.inspect}.") unless value
|
69
68
|
env.set_local_var(var.name, value)
|
@@ -71,7 +70,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
71
70
|
|
72
71
|
if callable.splat
|
73
72
|
rest = args[callable.args.length..-1] || []
|
74
|
-
arg_list = Sass::Script::Value::ArgList.new(rest, keywords
|
73
|
+
arg_list = Sass::Script::Value::ArgList.new(rest, keywords, splat_sep)
|
75
74
|
arg_list.options = env.options
|
76
75
|
env.set_local_var(callable.splat.name, arg_list)
|
77
76
|
end
|
@@ -113,7 +112,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
113
112
|
args = splat.to_a
|
114
113
|
end
|
115
114
|
end
|
116
|
-
kwargs ||= Sass::Util.
|
115
|
+
kwargs ||= Sass::Util::NormalizedMap.new
|
117
116
|
kwargs.update(performed_keywords)
|
118
117
|
|
119
118
|
if kwarg_splat
|
@@ -243,12 +242,14 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
243
242
|
to.assert_int!
|
244
243
|
|
245
244
|
to = to.coerce(from.numerator_units, from.denominator_units)
|
246
|
-
|
245
|
+
direction = from.to_i > to.to_i ? -1 : 1
|
246
|
+
range = Range.new(direction * from.to_i, direction * to.to_i, node.exclusive)
|
247
247
|
|
248
248
|
with_environment Sass::Environment.new(@environment) do
|
249
249
|
range.map do |i|
|
250
250
|
@environment.set_local_var(node.var,
|
251
|
-
Sass::Script::Value::Number.new(i,
|
251
|
+
Sass::Script::Value::Number.new(direction * i,
|
252
|
+
from.numerator_units, from.denominator_units))
|
252
253
|
node.children.map {|c| visit(c)}
|
253
254
|
end.flatten
|
254
255
|
end
|
@@ -314,12 +315,6 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
314
315
|
|
315
316
|
# Runs a mixin.
|
316
317
|
def visit_mixin(node)
|
317
|
-
include_loop = true
|
318
|
-
if @environment.stack.frames.any? {|f| f.is_mixin? && f.name == node.name}
|
319
|
-
handle_include_loop!(node)
|
320
|
-
end
|
321
|
-
include_loop = false
|
322
|
-
|
323
318
|
@environment.stack.with_mixin(node.filename, node.line, node.name) do
|
324
319
|
mixin = @environment.mixin(node.name)
|
325
320
|
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin
|
@@ -342,10 +337,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
342
337
|
end
|
343
338
|
end
|
344
339
|
rescue Sass::SyntaxError => e
|
345
|
-
|
346
|
-
|
347
|
-
e.add_backtrace(:line => node.line)
|
348
|
-
end
|
340
|
+
e.modify_backtrace(:mixin => node.name, :line => node.line)
|
341
|
+
e.add_backtrace(:line => node.line)
|
349
342
|
raise e
|
350
343
|
end
|
351
344
|
|
@@ -421,10 +414,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
421
414
|
# Loads the new variable value into the environment.
|
422
415
|
def visit_variable(node)
|
423
416
|
env = @environment
|
417
|
+
identifier = [node.name, node.filename, node.line]
|
424
418
|
if node.global
|
425
419
|
env = env.global_env
|
426
|
-
elsif env.parent && env.is_var_global?(node.name) &&
|
427
|
-
|
420
|
+
elsif env.parent && env.is_var_global?(node.name) &&
|
421
|
+
!env.global_env.global_warning_given.include?(identifier)
|
422
|
+
env.global_env.global_warning_given.add(identifier)
|
428
423
|
var_expr = "$#{node.name}: #{node.expr.to_sass(env.options)} !global"
|
429
424
|
var_expr << " !default" if node.guarded
|
430
425
|
location = "on line #{node.line}"
|
@@ -515,31 +510,6 @@ WARNING
|
|
515
510
|
run_interp_no_strip(text).strip
|
516
511
|
end
|
517
512
|
|
518
|
-
def handle_include_loop!(node)
|
519
|
-
msg = "An @include loop has been found:"
|
520
|
-
content_count = 0
|
521
|
-
mixins = @environment.stack.frames.select {|f| f.is_mixin?}.reverse!.map! {|f| f.name}
|
522
|
-
mixins = mixins.select do |name|
|
523
|
-
if name == '@content'
|
524
|
-
content_count += 1
|
525
|
-
false
|
526
|
-
elsif content_count > 0
|
527
|
-
content_count -= 1
|
528
|
-
false
|
529
|
-
else
|
530
|
-
true
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
|
-
return unless mixins.include?(node.name)
|
535
|
-
raise Sass::SyntaxError.new("#{msg} #{node.name} includes itself") if mixins.size == 1
|
536
|
-
|
537
|
-
msg << "\n" << Sass::Util.enum_cons(mixins.reverse + [node.name], 2).map do |m1, m2|
|
538
|
-
" #{m1} includes #{m2}"
|
539
|
-
end.join("\n")
|
540
|
-
raise Sass::SyntaxError.new(msg)
|
541
|
-
end
|
542
|
-
|
543
513
|
def handle_import_loop!(node)
|
544
514
|
msg = "An @import loop has been found:"
|
545
515
|
files = @environment.stack.frames.select {|f| f.is_import?}.map {|f| f.filename}.compact
|
data/lib/sass/util.rb
CHANGED
@@ -74,6 +74,7 @@ module Sass
|
|
74
74
|
# We don't delegate to map_hash for performance here
|
75
75
|
# because map_hash does more than is necessary.
|
76
76
|
rv = hash.class.new
|
77
|
+
hash = hash.as_stored if hash.is_a?(NormalizedMap)
|
77
78
|
hash.each do |k, v|
|
78
79
|
rv[k] = yield(v)
|
79
80
|
end
|
@@ -99,7 +100,7 @@ module Sass
|
|
99
100
|
rv = hash.class.new
|
100
101
|
hash.each do |k, v|
|
101
102
|
new_key, new_value = yield(k, v)
|
102
|
-
|
103
|
+
new_key = hash.denormalize(new_key) if hash.is_a?(NormalizedMap) && new_key == k
|
103
104
|
rv[new_key] = new_value
|
104
105
|
end
|
105
106
|
rv
|
@@ -170,6 +171,19 @@ module Sass
|
|
170
171
|
enum.inject([]) {|a, e| a << e << val}[0...-1]
|
171
172
|
end
|
172
173
|
|
174
|
+
def slice_by(enum)
|
175
|
+
results = []
|
176
|
+
enum.each do |value|
|
177
|
+
key = yield(value)
|
178
|
+
if !results.empty? && results.last.first == key
|
179
|
+
results.last.last << value
|
180
|
+
else
|
181
|
+
results << [key, [value]]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
results
|
185
|
+
end
|
186
|
+
|
173
187
|
# Substitutes a sub-array of one array with another sub-array.
|
174
188
|
#
|
175
189
|
# @param ary [Array] The array in which to make the substitution
|
@@ -487,6 +501,15 @@ module Sass
|
|
487
501
|
version_geq(ActionPack::VERSION::STRING, version)
|
488
502
|
end
|
489
503
|
|
504
|
+
# Returns whether this environment is using Listen
|
505
|
+
# version 2.0.0 or greater.
|
506
|
+
#
|
507
|
+
# @return [Boolean]
|
508
|
+
def listen_geq_2?
|
509
|
+
require 'listen/version'
|
510
|
+
version_geq(::Listen::VERSION, '2.0.0')
|
511
|
+
end
|
512
|
+
|
490
513
|
# Returns an ActionView::Template* class.
|
491
514
|
# In pre-3.0 versions of Rails, most of these classes
|
492
515
|
# were of the form `ActionView::TemplateFoo`,
|
@@ -537,15 +560,10 @@ module Sass
|
|
537
560
|
@jruby = RUBY_PLATFORM =~ /java/
|
538
561
|
end
|
539
562
|
|
540
|
-
# @see #jruby_version-class_method
|
541
|
-
def jruby_version
|
542
|
-
Sass::Util.jruby_version
|
543
|
-
end
|
544
|
-
|
545
563
|
# Returns an array of ints representing the JRuby version number.
|
546
564
|
#
|
547
565
|
# @return [Array<Fixnum>]
|
548
|
-
def
|
566
|
+
def jruby_version
|
549
567
|
@jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
|
550
568
|
end
|
551
569
|
|
@@ -651,7 +669,6 @@ module Sass
|
|
651
669
|
(pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
|
652
670
|
end
|
653
671
|
|
654
|
-
|
655
672
|
# Checks that the encoding of a string is valid in Ruby 1.9
|
656
673
|
# and cleans up potential encoding gotchas like the UTF-8 BOM.
|
657
674
|
# If it's not, yields an error string describing the invalid character
|
@@ -844,6 +861,24 @@ MSG
|
|
844
861
|
arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
|
845
862
|
end
|
846
863
|
|
864
|
+
# Flattens the first level of nested arrays in `arrs`. Unlike
|
865
|
+
# `Array#flatten`, this orders the result by taking the first
|
866
|
+
# values from each array in order, then the second, and so on.
|
867
|
+
#
|
868
|
+
# @param arrs [Array] The array to flatten.
|
869
|
+
# @return [Array] The flattened array.
|
870
|
+
def flatten_vertically(arrs)
|
871
|
+
result = []
|
872
|
+
arrs = arrs.map {|sub| sub.is_a?(Array) ? sub.dup : Array(sub)}
|
873
|
+
until arrs.empty?
|
874
|
+
arrs.reject! do |arr|
|
875
|
+
result << arr.shift
|
876
|
+
arr.empty?
|
877
|
+
end
|
878
|
+
end
|
879
|
+
result
|
880
|
+
end
|
881
|
+
|
847
882
|
# Returns the hash code for a set in a cross-version manner.
|
848
883
|
# Aggravatingly, this is order-dependent in Ruby 1.8.6.
|
849
884
|
#
|
@@ -1040,6 +1075,20 @@ MSG
|
|
1040
1075
|
URI_ESCAPE.escape string
|
1041
1076
|
end
|
1042
1077
|
|
1078
|
+
# A cross-platform implementation of `File.absolute_path`.
|
1079
|
+
#
|
1080
|
+
# @param path [String]
|
1081
|
+
# @param dir_string [String] The directory to consider [path] relative to.
|
1082
|
+
# @return [String] The absolute version of `path`.
|
1083
|
+
def absolute_path(path, dir_string = nil)
|
1084
|
+
# Ruby 1.8 doesn't support File.absolute_path.
|
1085
|
+
return File.absolute_path(path, dir_string) unless ruby1_8?
|
1086
|
+
|
1087
|
+
# File.expand_path expands "~", which we don't want.
|
1088
|
+
return File.expand_path(path, dir_string) unless path[0] == ?~
|
1089
|
+
File.expand_path(File.join(".", path), dir_string)
|
1090
|
+
end
|
1091
|
+
|
1043
1092
|
## Static Method Stuff
|
1044
1093
|
|
1045
1094
|
# The context in which the ERB for \{#def\_static\_method} will be run.
|
@@ -1091,7 +1140,6 @@ MSG
|
|
1091
1140
|
|
1092
1141
|
# rubocop:disable LineLength
|
1093
1142
|
|
1094
|
-
|
1095
1143
|
# Calculates the memoization table for the Least Common Subsequence algorithm.
|
1096
1144
|
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
|
1097
1145
|
def lcs_table(x, y)
|
@@ -1112,10 +1160,8 @@ MSG
|
|
1112
1160
|
end
|
1113
1161
|
c
|
1114
1162
|
end
|
1115
|
-
|
1116
1163
|
# rubocop:disable ParameterLists, LineLength
|
1117
1164
|
|
1118
|
-
|
1119
1165
|
# Computes a single longest common subsequence for arrays x and y.
|
1120
1166
|
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
|
1121
1167
|
def lcs_backtrace(c, x, y, i, j, &block)
|
@@ -1129,9 +1175,10 @@ MSG
|
|
1129
1175
|
lcs_backtrace(c, x, y, i - 1, j, &block)
|
1130
1176
|
end
|
1131
1177
|
|
1132
|
-
|
1178
|
+
singleton_methods.each {|method| module_function method}
|
1133
1179
|
end
|
1134
1180
|
end
|
1135
1181
|
|
1136
1182
|
require 'sass/util/multibyte_string_scanner'
|
1137
1183
|
require 'sass/util/normalized_map'
|
1184
|
+
require 'sass/util/cross_platform_random'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sass
|
2
|
+
module Util
|
3
|
+
# Ruby 1.8 doesn't support an actual Random class with a settable seed.
|
4
|
+
class CrossPlatformRandom
|
5
|
+
def initialize(seed = nil)
|
6
|
+
if Sass::Util.ruby1_8?
|
7
|
+
srand(seed) if seed
|
8
|
+
else
|
9
|
+
@random = seed ? ::Random.new(seed) : ::Random.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def rand(*args)
|
14
|
+
return @random.rand(*args) if @random
|
15
|
+
Kernel.rand(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -9,9 +9,11 @@ module Sass
|
|
9
9
|
require 'sass/util/ordered_hash' if ruby1_8?
|
10
10
|
class NormalizedMap
|
11
11
|
# Create a normalized map
|
12
|
-
def initialize
|
12
|
+
def initialize(map = nil)
|
13
13
|
@key_strings = {}
|
14
14
|
@map = Util.ruby1_8? ? OrderedHash.new : {}
|
15
|
+
|
16
|
+
map.each {|key, value| self[key] = value} if map
|
15
17
|
end
|
16
18
|
|
17
19
|
# Specifies how to transform the key.
|
@@ -21,6 +23,15 @@ module Sass
|
|
21
23
|
key.tr("-", "_")
|
22
24
|
end
|
23
25
|
|
26
|
+
# Returns the version of `key` as it was stored before
|
27
|
+
# normalization. If `key` isn't in the map, returns it as it was
|
28
|
+
# passed in.
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
def denormalize(key)
|
32
|
+
@key_strings[normalize(key)] || key
|
33
|
+
end
|
34
|
+
|
24
35
|
# @private
|
25
36
|
def []=(k, v)
|
26
37
|
normalized = normalize(k)
|
@@ -93,6 +104,11 @@ module Sass
|
|
93
104
|
@map.sort_by {|k, v| yield k, v}
|
94
105
|
end
|
95
106
|
|
107
|
+
def update(map)
|
108
|
+
map = map.as_stored if map.is_a?(NormalizedMap)
|
109
|
+
map.each {|k, v| self[k] = v}
|
110
|
+
end
|
111
|
+
|
96
112
|
def method_missing(method, *args, &block)
|
97
113
|
if Sass.tests_running
|
98
114
|
raise ArgumentError.new("The method #{method} must be implemented explicitly")
|
data/test/sass/compiler_test.rb
CHANGED
@@ -9,6 +9,7 @@ class CompilerTest < Test::Unit::TestCase
|
|
9
9
|
attr_accessor :options
|
10
10
|
attr_accessor :directories
|
11
11
|
attr_reader :start_called
|
12
|
+
attr_reader :thread
|
12
13
|
|
13
14
|
def initialize(*args, &on_filesystem_event)
|
14
15
|
self.options = args.last.is_a?(Hash) ? args.pop : {}
|
@@ -39,10 +40,19 @@ class CompilerTest < Test::Unit::TestCase
|
|
39
40
|
@run_during_start = run_during_start
|
40
41
|
end
|
41
42
|
|
43
|
+
# used for Listen < 2.0
|
42
44
|
def start!
|
43
45
|
@run_during_start.call(self) if @run_during_start
|
44
46
|
end
|
45
47
|
|
48
|
+
# used for Listen >= 2.0
|
49
|
+
def start
|
50
|
+
@thread = Thread.new {@run_during_start.call(self) if @run_during_start}
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop
|
54
|
+
end
|
55
|
+
|
46
56
|
def reset_events!
|
47
57
|
@modified = []
|
48
58
|
@added = []
|
@@ -499,6 +499,29 @@ foo
|
|
499
499
|
SASS
|
500
500
|
end
|
501
501
|
|
502
|
+
def test_loud_comment_containing_silent_comment
|
503
|
+
assert_scss_to_sass <<SASS, <<SCSS
|
504
|
+
/*
|
505
|
+
*// foo bar
|
506
|
+
SASS
|
507
|
+
/*
|
508
|
+
// foo bar
|
509
|
+
*/
|
510
|
+
SCSS
|
511
|
+
end
|
512
|
+
|
513
|
+
def test_silent_comment_containing_loud_comment
|
514
|
+
assert_scss_to_sass <<SASS, <<SCSS
|
515
|
+
// /*
|
516
|
+
// * foo bar
|
517
|
+
// */
|
518
|
+
SASS
|
519
|
+
// /*
|
520
|
+
// * foo bar
|
521
|
+
// */
|
522
|
+
SCSS
|
523
|
+
end
|
524
|
+
|
502
525
|
def test_immediately_preceding_comments
|
503
526
|
assert_renders <<SASS, <<SCSS
|
504
527
|
/* Foo
|
@@ -1009,6 +1032,19 @@ foo {
|
|
1009
1032
|
SCSS
|
1010
1033
|
end
|
1011
1034
|
|
1035
|
+
def test_mixin_include_with_hyphen_conversion_keyword_arg
|
1036
|
+
assert_renders <<SASS, <<SCSS
|
1037
|
+
foo
|
1038
|
+
+foo-bar($a-b_c: val)
|
1039
|
+
a: blip
|
1040
|
+
SASS
|
1041
|
+
foo {
|
1042
|
+
@include foo-bar($a-b_c: val);
|
1043
|
+
a: blip;
|
1044
|
+
}
|
1045
|
+
SCSS
|
1046
|
+
end
|
1047
|
+
|
1012
1048
|
def test_argless_function_definition
|
1013
1049
|
assert_renders <<SASS, <<SCSS
|
1014
1050
|
@function foo()
|
data/test/sass/css2sass_test.rb
CHANGED
@@ -265,6 +265,25 @@ CSS
|
|
265
265
|
|
266
266
|
# Regressions
|
267
267
|
|
268
|
+
def test_empty_rule
|
269
|
+
assert_equal(<<SASS, css2sass(<<CSS))
|
270
|
+
a
|
271
|
+
SASS
|
272
|
+
a {}
|
273
|
+
CSS
|
274
|
+
end
|
275
|
+
|
276
|
+
def test_empty_rule_with_selector_combinator
|
277
|
+
assert_equal(<<SASS, css2sass(<<CSS))
|
278
|
+
a
|
279
|
+
color: red
|
280
|
+
> b
|
281
|
+
SASS
|
282
|
+
a {color: red}
|
283
|
+
a > b {}
|
284
|
+
CSS
|
285
|
+
end
|
286
|
+
|
268
287
|
def test_nesting_within_media
|
269
288
|
assert_equal(<<SASS, css2sass(<<CSS))
|
270
289
|
@media all
|
data/test/sass/engine_test.rb
CHANGED
@@ -67,7 +67,7 @@ MSG
|
|
67
67
|
"$a: 1b <= 2c" => "Incompatible units: 'c' and 'b'.",
|
68
68
|
"$a: 1b >= 2c" => "Incompatible units: 'c' and 'b'.",
|
69
69
|
"a\n b: 1b * 2c" => "2b*c isn't a valid CSS value.",
|
70
|
-
"a\n b: 1b % 2c" => "
|
70
|
+
"a\n b: 1b % 2c" => "Incompatible units: 'c' and 'b'.",
|
71
71
|
"$a: 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
|
72
72
|
"$a: #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
|
73
73
|
"& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
|
@@ -158,19 +158,19 @@ MSG
|
|
158
158
|
"$var: true\n@while $var\n @extend .bar\n $var: false" => ["Extend directives may only be used within rules.", 3],
|
159
159
|
"@for $i from 0 to 1\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
|
160
160
|
"@mixin foo\n @extend .bar\n@include foo" => ["Extend directives may only be used within rules.", 2],
|
161
|
-
"foo\n &a\n b: c" => ["Invalid CSS after \"&\": expected \"{\", was \"a\"\n\n\"a\" may only be used at the beginning of a compound selector.", 2],
|
162
|
-
"foo\n &1\n b: c" => ["Invalid CSS after \"&\": expected \"{\", was \"1\"\n\n\"1\" may only be used at the beginning of a compound selector.", 2],
|
163
161
|
"foo %\n a: b" => ['Invalid CSS after "foo %": expected placeholder name, was ""', 1],
|
164
162
|
"=foo\n @content error" => "Invalid content directive. Trailing characters found: \"error\".",
|
165
163
|
"=foo\n @content\n b: c" => "Illegal nesting: Nothing may be nested beneath @content directives.",
|
166
164
|
"@content" => '@content may only be used within a mixin.',
|
167
165
|
"=simple\n .simple\n color: red\n+simple\n color: blue" => ['Mixin "simple" does not accept a content block.', 4],
|
168
166
|
"@import \"foo\" // bar" => "Invalid CSS after \"\"foo\" \": expected media query list, was \"// bar\"",
|
167
|
+
"@at-root\n a: b" => "Properties are only allowed within rules, directives, mixin includes, or other properties.",
|
169
168
|
|
170
169
|
# Regression tests
|
171
170
|
"a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3],
|
172
171
|
"& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
|
173
172
|
"a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3],
|
173
|
+
"@" => "Invalid directive: '@'.",
|
174
174
|
}
|
175
175
|
|
176
176
|
def teardown
|
@@ -467,89 +467,25 @@ SASS
|
|
467
467
|
assert_hash_has(err.sass_backtrace[4], :filename => nil, :mixin => nil, :line => 1)
|
468
468
|
end
|
469
469
|
|
470
|
-
def
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
@
|
485
|
-
|
486
|
-
@mixin bar
|
487
|
-
@include foo
|
488
|
-
@include foo
|
489
|
-
SASS
|
490
|
-
assert(false, "Exception not raised")
|
491
|
-
rescue Sass::SyntaxError => err
|
492
|
-
assert_equal(<<MESSAGE.rstrip, err.message)
|
493
|
-
An @include loop has been found:
|
494
|
-
foo includes bar
|
495
|
-
bar includes foo
|
496
|
-
MESSAGE
|
497
|
-
assert_hash_has(err.sass_backtrace[0], :mixin => "bar", :line => 4)
|
498
|
-
assert_hash_has(err.sass_backtrace[1], :mixin => "foo", :line => 2)
|
499
|
-
end
|
500
|
-
|
501
|
-
def test_deep_mixin_loop_exception
|
502
|
-
render <<SASS
|
503
|
-
@mixin foo
|
504
|
-
@include bar
|
505
|
-
|
506
|
-
@mixin bar
|
507
|
-
@include baz
|
508
|
-
|
509
|
-
@mixin baz
|
510
|
-
@include foo
|
470
|
+
def test_recursive_mixin
|
471
|
+
assert_equal <<CSS, render(<<SASS)
|
472
|
+
.foo .bar .baz {
|
473
|
+
color: blue; }
|
474
|
+
.foo .bar .qux {
|
475
|
+
color: red; }
|
476
|
+
.foo .zap {
|
477
|
+
color: green; }
|
478
|
+
CSS
|
479
|
+
@mixin map-to-rule($map-or-color)
|
480
|
+
@if type-of($map-or-color) == map
|
481
|
+
@each $key, $value in $map-or-color
|
482
|
+
.\#{$key}
|
483
|
+
@include map-to-rule($value)
|
484
|
+
@else
|
485
|
+
color: $map-or-color
|
511
486
|
|
512
|
-
@include foo
|
513
|
-
SASS
|
514
|
-
assert(false, "Exception not raised")
|
515
|
-
rescue Sass::SyntaxError => err
|
516
|
-
assert_equal(<<MESSAGE.rstrip, err.message)
|
517
|
-
An @include loop has been found:
|
518
|
-
foo includes bar
|
519
|
-
bar includes baz
|
520
|
-
baz includes foo
|
521
|
-
MESSAGE
|
522
|
-
assert_hash_has(err.sass_backtrace[0], :mixin => "baz", :line => 8)
|
523
|
-
assert_hash_has(err.sass_backtrace[1], :mixin => "bar", :line => 5)
|
524
|
-
assert_hash_has(err.sass_backtrace[2], :mixin => "foo", :line => 2)
|
525
|
-
end
|
526
|
-
|
527
|
-
def test_mixin_loop_with_content
|
528
|
-
render <<SASS
|
529
|
-
=foo
|
530
|
-
@content
|
531
|
-
=bar
|
532
|
-
+foo
|
533
|
-
+bar
|
534
|
-
+bar
|
487
|
+
@include map-to-rule((foo: (bar: (baz: blue, qux: red), zap: green)))
|
535
488
|
SASS
|
536
|
-
assert(false, "Exception not raised")
|
537
|
-
rescue Sass::SyntaxError => err
|
538
|
-
assert_equal("An @include loop has been found: bar includes itself", err.message)
|
539
|
-
assert_hash_has(err.sass_backtrace[0], :mixin => "@content", :line => 5)
|
540
|
-
end
|
541
|
-
|
542
|
-
def test_basic_import_loop_exception
|
543
|
-
import = filename_for_test
|
544
|
-
importer = MockImporter.new
|
545
|
-
importer.add_import(import, "@import '#{import}'")
|
546
|
-
|
547
|
-
engine = Sass::Engine.new("@import '#{import}'", :filename => import,
|
548
|
-
:load_paths => [importer])
|
549
|
-
|
550
|
-
assert_raise_message(Sass::SyntaxError, <<ERR.rstrip) {engine.render}
|
551
|
-
An @import loop has been found: #{import} imports itself
|
552
|
-
ERR
|
553
489
|
end
|
554
490
|
|
555
491
|
def test_double_import_loop_exception
|
@@ -2282,6 +2218,36 @@ CSS
|
|
2282
2218
|
SASS
|
2283
2219
|
end
|
2284
2220
|
|
2221
|
+
def test_double_media_bubbling_with_surrounding_rules
|
2222
|
+
assert_equal <<CSS, render(<<SASS)
|
2223
|
+
@media (min-width: 0) {
|
2224
|
+
a {
|
2225
|
+
a: a; }
|
2226
|
+
|
2227
|
+
b {
|
2228
|
+
before: b;
|
2229
|
+
after: b; } }
|
2230
|
+
@media (min-width: 0) and (max-width: 5000px) {
|
2231
|
+
b {
|
2232
|
+
x: x; } }
|
2233
|
+
|
2234
|
+
@media (min-width: 0) {
|
2235
|
+
c {
|
2236
|
+
c: c; } }
|
2237
|
+
CSS
|
2238
|
+
@media (min-width: 0)
|
2239
|
+
a
|
2240
|
+
a: a
|
2241
|
+
b
|
2242
|
+
before: b
|
2243
|
+
@media (max-width: 5000px)
|
2244
|
+
x: x
|
2245
|
+
after: b
|
2246
|
+
c
|
2247
|
+
c: c
|
2248
|
+
SASS
|
2249
|
+
end
|
2250
|
+
|
2285
2251
|
def test_rule_media_rule_bubbling
|
2286
2252
|
assert_equal <<CSS, render(<<SASS)
|
2287
2253
|
@media bar {
|
@@ -2308,9 +2274,10 @@ SASS
|
|
2308
2274
|
@media print {
|
2309
2275
|
.outside {
|
2310
2276
|
color: black; } }
|
2311
|
-
|
2312
|
-
|
2313
|
-
|
2277
|
+
@media print and (a: b) {
|
2278
|
+
.outside .inside {
|
2279
|
+
border: 1px solid black; } }
|
2280
|
+
|
2314
2281
|
.outside .middle {
|
2315
2282
|
display: block; }
|
2316
2283
|
CSS
|
@@ -2588,24 +2555,6 @@ body
|
|
2588
2555
|
SASS
|
2589
2556
|
end
|
2590
2557
|
|
2591
|
-
def test_tricky_mixin_loop_exception
|
2592
|
-
render <<SASS
|
2593
|
-
@mixin foo($a)
|
2594
|
-
@if $a
|
2595
|
-
@include foo(false)
|
2596
|
-
@include foo(true)
|
2597
|
-
@else
|
2598
|
-
a: b
|
2599
|
-
|
2600
|
-
a
|
2601
|
-
@include foo(true)
|
2602
|
-
SASS
|
2603
|
-
assert(false, "Exception not raised")
|
2604
|
-
rescue Sass::SyntaxError => err
|
2605
|
-
assert_equal("An @include loop has been found: foo includes itself", err.message)
|
2606
|
-
assert_hash_has(err.sass_backtrace[0], :mixin => "foo", :line => 3)
|
2607
|
-
end
|
2608
|
-
|
2609
2558
|
def test_interpolated_comment_in_mixin
|
2610
2559
|
assert_equal <<CSS, render(<<SASS)
|
2611
2560
|
/*! color: red */
|