sass 3.2.0.alpha.104 → 3.2.0.alpha.236

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/REVISION +1 -1
  2. data/Rakefile +2 -2
  3. data/VERSION +1 -1
  4. data/lib/sass/engine.rb +8 -1
  5. data/lib/sass/exec.rb +2 -2
  6. data/lib/sass/plugin/compiler.rb +51 -43
  7. data/lib/sass/script/color.rb +4 -9
  8. data/lib/sass/script/funcall.rb +11 -2
  9. data/lib/sass/script/functions.rb +16 -30
  10. data/lib/sass/script/lexer.rb +7 -1
  11. data/lib/sass/script/list.rb +2 -2
  12. data/lib/sass/script/literal.rb +8 -0
  13. data/lib/sass/script/null.rb +34 -0
  14. data/lib/sass/script/operation.rb +4 -0
  15. data/lib/sass/script/parser.rb +4 -2
  16. data/lib/sass/scss/parser.rb +1 -5
  17. data/lib/sass/scss/rx.rb +1 -1
  18. data/lib/sass/tree/directive_node.rb +5 -0
  19. data/lib/sass/tree/media_node.rb +3 -0
  20. data/lib/sass/tree/prop_node.rb +7 -3
  21. data/lib/sass/tree/visitors/base.rb +1 -1
  22. data/lib/sass/tree/visitors/check_nesting.rb +21 -18
  23. data/lib/sass/tree/visitors/convert.rb +1 -0
  24. data/lib/sass/tree/visitors/perform.rb +3 -2
  25. data/lib/sass/tree/visitors/to_css.rb +1 -0
  26. data/lib/sass/util.rb +28 -0
  27. data/test/sass/conversion_test.rb +33 -0
  28. data/test/sass/engine_test.rb +118 -3
  29. data/test/sass/functions_test.rb +56 -24
  30. data/test/sass/script_test.rb +60 -4
  31. data/test/sass/scss/scss_test.rb +10 -0
  32. data/vendor/listen/CHANGELOG.md +19 -1
  33. data/vendor/listen/README.md +27 -12
  34. data/vendor/listen/lib/listen/adapter.rb +9 -1
  35. data/vendor/listen/lib/listen/adapters/linux.rb +18 -7
  36. data/vendor/listen/lib/listen/adapters/windows.rb +7 -8
  37. data/vendor/listen/lib/listen/directory_record.rb +108 -48
  38. data/vendor/listen/lib/listen/listener.rb +28 -11
  39. data/vendor/listen/lib/listen/multi_listener.rb +6 -6
  40. data/vendor/listen/lib/listen/version.rb +1 -1
  41. data/vendor/listen/spec/listen/adapters/linux_spec.rb +11 -0
  42. data/vendor/listen/spec/listen/directory_record_spec.rb +268 -41
  43. data/vendor/listen/spec/listen/listener_spec.rb +8 -4
  44. data/vendor/listen/spec/listen/multi_listener_spec.rb +9 -4
  45. data/vendor/listen/spec/support/adapter_helper.rb +178 -0
  46. data/vendor/listen/spec/support/directory_record_helper.rb +24 -4
  47. data/vendor/listen/spec/support/listeners_helper.rb +11 -0
  48. metadata +11 -11
  49. data/lib/sass/plugin/listener.rb +0 -61
@@ -381,7 +381,9 @@ RUBY
381
381
 
382
382
  other_args, other_keywords = assert_expr(type)
383
383
  if keywords
384
- if other_keywords[name.underscored_name]
384
+ if !other_args.empty?
385
+ raise SyntaxError.new("Positional arguments must come before keyword arguments")
386
+ elsif other_keywords[name.underscored_name]
385
387
  raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
386
388
  end
387
389
  return other_args, keywords.merge(other_keywords)
@@ -453,7 +455,7 @@ RUBY
453
455
  end
454
456
 
455
457
  def literal
456
- (t = try_tok(:color, :bool)) && (return t.value)
458
+ (t = try_tok(:color, :bool, :null)) && (return t.value)
457
459
  end
458
460
 
459
461
  # It would be possible to have unified #assert and #try methods,
@@ -721,11 +721,7 @@ module Sass
721
721
  tok(SUBSTRINGMATCH)
722
722
  @expected = "identifier or string"
723
723
  ss
724
- if val = tok(IDENT)
725
- val = [val]
726
- else
727
- val = expr!(:interp_string)
728
- end
724
+ val = interp_ident || expr!(:interp_string)
729
725
  ss
730
726
  end
731
727
  tok(/\]/)
@@ -130,7 +130,7 @@ module Sass
130
130
  # about 50 characters. This mitigates the problem of exponential parsing
131
131
  # time when a value has a long string of valid, parsable content followed
132
132
  # by something invalid.
133
- STATIC_VALUE = /(-?#{NMSTART}|#{STRING_NOINTERP}|[ \t](?!%)|#[a-f0-9]|[,%]|#{NUM}|\!important){0,50}([;}])/i
133
+ STATIC_VALUE = /(-?#{NMSTART}|#{STRING_NOINTERP}|[ \t](?!%)|#[a-f0-9]|[,%]|#{NUM}|\!important){1,50}([;}])/i
134
134
  STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){0,50}([{])/i
135
135
  end
136
136
  end
@@ -33,5 +33,10 @@ module Sass::Tree
33
33
  node.resolved_value = value
34
34
  node
35
35
  end
36
+
37
+ # @return [String] The name of the directive, including `@`.
38
+ def name
39
+ value.first.gsub(/ .*$/, '')
40
+ end
36
41
  end
37
42
  end
@@ -27,6 +27,9 @@ module Sass::Tree
27
27
  # @see DirectiveNode#value
28
28
  def value; raise NotImplementedError; end
29
29
 
30
+ # @see DirectiveNode#name
31
+ def name; '@media'; end
32
+
30
33
  # @see DirectiveNode#resolved_value
31
34
  def resolved_value
32
35
  @resolved_value ||= "@media #{query.to_css}"
@@ -92,15 +92,19 @@ module Sass::Tree
92
92
  "#{initial}#{name}#{mid} #{self.class.val_to_sass(value, opts)}".rstrip
93
93
  end
94
94
 
95
+ # A property node is invisible if its value is empty.
96
+ #
97
+ # @return [Boolean]
98
+ def invisible?
99
+ resolved_value.empty?
100
+ end
101
+
95
102
  private
96
103
 
97
104
  def check!
98
105
  if @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
99
106
  raise Sass::SyntaxError.new(
100
107
  "Illegal property syntax: can't use #{@prop_syntax} syntax when :property_syntax => #{@options[:property_syntax].inspect} is set.")
101
- elsif resolved_value.empty?
102
- raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
103
- pseudo_class_selector_message)
104
108
  end
105
109
  end
106
110
 
@@ -33,7 +33,7 @@ module Sass::Tree::Visitors
33
33
  # @return [Object] The return value of the `visit_*` method for this node.
34
34
  def visit(node)
35
35
  method = "visit_#{node_name node}"
36
- if self.respond_to?(method)
36
+ if self.respond_to?(method, true)
37
37
  self.send(method, node) {visit_children(node)}
38
38
  else
39
39
  visit_children(node)
@@ -2,13 +2,14 @@
2
2
  class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
3
3
  protected
4
4
 
5
+ def initialize
6
+ @parents = []
7
+ end
8
+
5
9
  def visit(node)
6
- if error = (@parent && (
7
- try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
8
- try_send("invalid_#{node_name node}_parent?", @parent, node))) ||
9
- (@real_parent && (
10
- try_send("invalid_#{node_name @real_parent}_real_child?", @real_parent, node) ||
11
- try_send("invalid_#{node_name node}_real_parent?", @real_parent, node)))
10
+ if error = @parent && (
11
+ try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
12
+ try_send("invalid_#{node_name node}_parent?", @parent, node))
12
13
  raise Sass::SyntaxError.new(error)
13
14
  end
14
15
  super
@@ -23,11 +24,11 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
23
24
  def visit_children(parent)
24
25
  old_parent = @parent
25
26
  @parent = parent unless is_any_of?(parent, SCRIPT_NODES)
26
- old_real_parent, @real_parent = @real_parent, parent
27
+ @parents.push parent
27
28
  super
28
29
  ensure
29
30
  @parent = old_parent
30
- @real_parent = old_real_parent
31
+ @parents.pop
31
32
  end
32
33
 
33
34
  def visit_root(node)
@@ -68,7 +69,15 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
68
69
  VALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
69
70
  def invalid_extend_parent?(parent, child)
70
71
  unless is_any_of?(parent, VALID_EXTEND_PARENTS)
71
- "Extend directives may only be used within rules."
72
+ return "Extend directives may only be used within rules."
73
+ end
74
+
75
+ if directive = @parents.find {|p| p.is_a?(Sass::Tree::DirectiveNode)}
76
+ return <<ERR.rstrip
77
+ @extend may not be used within directives (e.g. #{directive.name}).
78
+
79
+ This will only work once @extend is supported natively in the browser.
80
+ ERR
72
81
  end
73
82
  end
74
83
 
@@ -86,12 +95,10 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
86
95
  end
87
96
  end
88
97
 
89
- VALID_IMPORT_PARENTS = [
90
- Sass::Tree::IfNode, Sass::Tree::ForNode, Sass::Tree::WhileNode,
91
- Sass::Tree::EachNode, Sass::Tree::MixinDefNode, Sass::Tree::MixinNode
92
- ]
98
+ INVALID_IMPORT_PARENTS = CONTROL_NODES +
99
+ [Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
93
100
  def invalid_import_parent?(parent, child)
94
- if is_any_of?(@real_parent, VALID_IMPORT_PARENTS)
101
+ unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
95
102
  return "Import directives may not be used within control directives or mixins."
96
103
  end
97
104
  return if parent.is_a?(Sass::Tree::RootNode)
@@ -106,10 +113,6 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
106
113
  raise e
107
114
  end
108
115
 
109
- def invalid_import_real_parent?(parent, child)
110
-
111
- end
112
-
113
116
  def invalid_mixindef_parent?(parent, child)
114
117
  "Mixins may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
115
118
  end
@@ -131,6 +131,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
131
131
  elsif node.expr; "else if"
132
132
  else; "else"
133
133
  end
134
+ @is_else = false
134
135
  str = "#{tab_str}@#{name}"
135
136
  str << " #{node.expr.to_sass(@options)}" if node.expr
136
137
  str << yield
@@ -198,7 +198,7 @@ END
198
198
  elsif default
199
199
  default.perform(env)
200
200
  end)
201
- raise Sass::SyntaxError.new("Mixin #{node.name} is missing parameter #{var.inspect}.") unless env.var(var.name)
201
+ raise Sass::SyntaxError.new("Mixin #{node.name} is missing argument #{var.inspect}.") unless env.var(var.name)
202
202
  env
203
203
  end
204
204
  environment.caller = Sass::Environment.new(@environment)
@@ -261,7 +261,8 @@ END
261
261
 
262
262
  # Loads the new variable value into the environment.
263
263
  def visit_variable(node)
264
- return [] if node.guarded && !@environment.var(node.name).nil?
264
+ var = @environment.var(node.name)
265
+ return [] if node.guarded && var && !var.null?
265
266
  val = node.expr.perform(@environment)
266
267
  @environment.set_var(node.name, val)
267
268
  []
@@ -122,6 +122,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
122
122
  end
123
123
 
124
124
  def visit_prop(node)
125
+ return if node.resolved_value.empty?
125
126
  tab_str = ' ' * (@tabs + node.tabs)
126
127
  if node.style == :compressed
127
128
  "#{tab_str}#{node.resolved_name}:#{node.resolved_value}"
@@ -256,6 +256,26 @@ module Sass
256
256
  arr
257
257
  end
258
258
 
259
+ # Asserts that `value` falls within `range` (inclusive), leaving
260
+ # room for slight floating-point errors.
261
+ #
262
+ # @param name [String] The name of the value. Used in the error message.
263
+ # @param range [Range] The allowed range of values.
264
+ # @param value [Numeric, Sass::Script::Number] The value to check.
265
+ # @param unit [String] The unit of the value. Used in error reporting.
266
+ # @return [Numeric] `value` adjusted to fall within range, if it
267
+ # was outside by a floating-point margin.
268
+ def check_range(name, range, value, unit='')
269
+ grace = (-0.00001..0.00001)
270
+ str = value.to_s
271
+ value = value.value if value.is_a?(Sass::Script::Number)
272
+ return value if range.include?(value)
273
+ return range.first if grace.include?(value - range.first)
274
+ return range.last if grace.include?(value - range.last)
275
+ raise ArgumentError.new(
276
+ "#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
277
+ end
278
+
259
279
  # Returns information about the caller of the previous method.
260
280
  #
261
281
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
@@ -422,6 +442,14 @@ module Sass
422
442
  RUBY_ENGINE == "ironruby"
423
443
  end
424
444
 
445
+ # Like `Dir.glob`, but works with backslash-separated paths on Windows.
446
+ #
447
+ # @param path [String]
448
+ def glob(path)
449
+ path = path.gsub('\\', '/') if windows?
450
+ Dir.glob(path)
451
+ end
452
+
425
453
  ## Cross-Ruby-Version Compatibility
426
454
 
427
455
  # Whether or not this is running under Ruby 1.8 or lower.
@@ -1525,6 +1525,39 @@ $foo: ();
1525
1525
  SCSS
1526
1526
  end
1527
1527
 
1528
+ def test_nested_if_statements
1529
+ assert_renders(<<SASS, <<SCSS)
1530
+ @if $foo
1531
+ one
1532
+ a: b
1533
+ @else
1534
+ @if $bar
1535
+ two
1536
+ a: b
1537
+ @else
1538
+ three
1539
+ a: b
1540
+ SASS
1541
+ @if $foo {
1542
+ one {
1543
+ a: b;
1544
+ }
1545
+ }
1546
+ @else {
1547
+ @if $bar {
1548
+ two {
1549
+ a: b;
1550
+ }
1551
+ }
1552
+ @else {
1553
+ three {
1554
+ a: b;
1555
+ }
1556
+ }
1557
+ }
1558
+ SCSS
1559
+ end
1560
+
1528
1561
  private
1529
1562
 
1530
1563
  def assert_sass_to_sass(sass, options = {})
@@ -64,7 +64,9 @@ MSG
64
64
  "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
65
65
  "foo\n @import foo.css" => "CSS import directives may only be used at the root of a document.",
66
66
  "@if true\n @import foo" => "Import directives may not be used within control directives or mixins.",
67
+ "@if true\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
67
68
  "@mixin foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
69
+ "@mixin foo\n .foo\n @import foo" => "Import directives may not be used within control directives or mixins.",
68
70
  "@import foo;" => "Invalid @import: expected end of line, was \";\".",
69
71
  '$foo: "bar" "baz" !' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "!"},
70
72
  '$foo: "bar" "baz" $' => %Q{Invalid CSS after ""bar" "baz" ": expected expression (e.g. 1px, bold), was "$"}, #'
@@ -92,7 +94,7 @@ MSG
92
94
  "=a($b: 1, $c)" => "Required argument $c must come before any optional arguments.",
93
95
  "=a($b: 1)\n a: $b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
94
96
  "=a($b: 1)\n a: $b\ndiv\n +a(1,$c: 3)" => "Mixin a doesn't have an argument named $c",
95
- "=a($b)\n a: $b\ndiv\n +a" => "Mixin a is missing parameter $b.",
97
+ "=a($b)\n a: $b\ndiv\n +a" => "Mixin a is missing argument $b.",
96
98
  "@function foo()\n 1 + 2" => "Functions can only contain variable declarations and control directives.",
97
99
  "@function foo()\n foo: bar" => "Functions can only contain variable declarations and control directives.",
98
100
  "@function foo()\n foo: bar\n @return 3" => ["Functions can only contain variable declarations and control directives.", 2],
@@ -104,7 +106,7 @@ MSG
104
106
  "@function foo()\n @return" => 'Invalid @return: expected expression.',
105
107
  "@function foo()\n @return 1\n $var: val" => 'Illegal nesting: Nothing may be nested beneath return directives.',
106
108
  "foo\n @function bar()\n @return 1" => ['Functions may only be defined at the root of a document.', 2],
107
- "@function foo($a)\n @return 1\na\n b: foo()" => 'Function foo is missing parameter $a.',
109
+ "@function foo($a)\n @return 1\na\n b: foo()" => 'Function foo is missing argument $a',
108
110
  "@function foo()\n @return 1\na\n b: foo(2)" => 'Wrong number of arguments (1 for 0) for `foo\'',
109
111
  "@function foo()\n @return 1\na\n b: foo($a: 1)" => "Function foo doesn't have an argument named $a",
110
112
  "@function foo()\n @return 1\na\n b: foo($a: 1, $b: 2)" => "Function foo doesn't have the following arguments: $a, $b",
@@ -144,6 +146,8 @@ MSG
144
146
  "$var: true\n@while $var\n @extend .bar\n $var: false" => ["Extend directives may only be used within rules.", 3],
145
147
  "@for $i from 0 to 1\n @extend .bar" => ["Extend directives may only be used within rules.", 2],
146
148
  "@mixin foo\n @extend .bar\n@include foo" => ["Extend directives may only be used within rules.", 2],
149
+ "@media screen\n .bar\n @extend .foo" => "@extend may not be used within directives (e.g. @media).\n\nThis will only work once @extend is supported natively in the browser.",
150
+ "@flooblehoof\n .bar\n @extend .foo" => "@extend may not be used within directives (e.g. @flooblehoof).\n\nThis will only work once @extend is supported natively in the browser.",
147
151
  "foo\n &a\n b: c" => ["Invalid CSS after \"&\": expected \"{\", was \"a\"\n\n\"a\" may only be used at the beginning of a selector.", 2],
148
152
  "foo\n &1\n b: c" => ["Invalid CSS after \"&\": expected \"{\", was \"1\"\n\n\"1\" may only be used at the beginning of a selector.", 2],
149
153
  "foo %\n a: b" => ['Invalid CSS after "foo %": expected placeholder name, was ""', 1],
@@ -1011,7 +1015,7 @@ SASS
1011
1015
 
1012
1016
  def test_debug_info_without_filename
1013
1017
  assert_equal(<<CSS, Sass::Engine.new(<<SASS, :debug_info => true).render)
1014
- @media -sass-debug-info{filename{font-family:}line{font-family:\\000031}}
1018
+ @media -sass-debug-info{filename{}line{font-family:\\000031}}
1015
1019
  foo {
1016
1020
  a: b; }
1017
1021
  CSS
@@ -1100,6 +1104,7 @@ SASS
1100
1104
  def test_guarded_assign
1101
1105
  assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b\n$foo: c !default\nfoo\n a: $foo}))
1102
1106
  assert_equal("foo {\n a: b; }\n", render(%Q{$foo: b !default\nfoo\n a: $foo}))
1107
+ assert_equal("foo {\n a: b; }\n", render(%Q{$foo: null\n$foo: b !default\nfoo\n a: $foo}))
1103
1108
  end
1104
1109
 
1105
1110
  def test_mixins
@@ -1185,6 +1190,35 @@ two
1185
1190
  +foo(#fff, 2px)
1186
1191
  three
1187
1192
  +foo(#fff, 2px, 3px)
1193
+ SASS
1194
+ assert_equal(<<CSS, render(<<SASS))
1195
+ one {
1196
+ color: white;
1197
+ padding: 1px;
1198
+ margin: 4px; }
1199
+
1200
+ two {
1201
+ color: white;
1202
+ padding: 2px;
1203
+ margin: 5px; }
1204
+
1205
+ three {
1206
+ color: white;
1207
+ padding: 2px;
1208
+ margin: 3px; }
1209
+ CSS
1210
+ $a: 5px
1211
+ =foo($a, $b: 1px, $c: null)
1212
+ $c: 3px + $b !default
1213
+ color: $a
1214
+ padding: $b
1215
+ margin: $c
1216
+ one
1217
+ +foo(#fff)
1218
+ two
1219
+ +foo(#fff, 2px)
1220
+ three
1221
+ +foo(#fff, 2px, 3px)
1188
1222
  SASS
1189
1223
  end
1190
1224
 
@@ -1271,6 +1305,58 @@ bar
1271
1305
  SASS
1272
1306
  end
1273
1307
 
1308
+ def test_function_with_missing_argument
1309
+ render(<<SASS)
1310
+ @function plus($var1, $var2)
1311
+ @return $var1 + $var2
1312
+
1313
+ bar
1314
+ a: plus($var2: bar)
1315
+ SASS
1316
+ flunk("Expected exception")
1317
+ rescue Sass::SyntaxError => e
1318
+ assert_equal("Function plus is missing argument $var1", e.message)
1319
+ end
1320
+
1321
+ def test_function_with_extra_argument
1322
+ render(<<SASS)
1323
+ @function plus($var1, $var2)
1324
+ @return $var1 + $var2
1325
+
1326
+ bar
1327
+ a: plus($var1: foo, $var2: bar, $var3: baz)
1328
+ SASS
1329
+ flunk("Expected exception")
1330
+ rescue Sass::SyntaxError => e
1331
+ assert_equal("Function plus doesn't have an argument named $var3", e.message)
1332
+ end
1333
+
1334
+ def test_function_with_positional_and_keyword_argument
1335
+ render(<<SASS)
1336
+ @function plus($var1, $var2)
1337
+ @return $var1 + $var2
1338
+
1339
+ bar
1340
+ a: plus(foo, bar, $var2: baz)
1341
+ SASS
1342
+ flunk("Expected exception")
1343
+ rescue Sass::SyntaxError => e
1344
+ assert_equal("Function plus was passed argument $var2 both by position and by name", e.message)
1345
+ end
1346
+
1347
+ def test_function_with_keyword_before_positional_argument
1348
+ render(<<SASS)
1349
+ @function plus($var1, $var2)
1350
+ @return $var1 + $var2
1351
+
1352
+ bar
1353
+ a: plus($var2: foo, bar)
1354
+ SASS
1355
+ flunk("Expected exception")
1356
+ rescue Sass::SyntaxError => e
1357
+ assert_equal("Positional arguments must come before keyword arguments", e.message)
1358
+ end
1359
+
1274
1360
  def test_function_with_if
1275
1361
  assert_equal(<<CSS, render(<<SASS))
1276
1362
  bar {
@@ -1341,6 +1427,15 @@ SASS
1341
1427
  def test_if_directive
1342
1428
  assert_equal("a {\n b: 1; }\n", render(<<SASS))
1343
1429
  $var: true
1430
+ a
1431
+ @if $var
1432
+ b: 1
1433
+ @if not $var
1434
+ b: 2
1435
+ SASS
1436
+
1437
+ assert_equal("a {\n b: 2; }\n", render(<<SASS))
1438
+ $var: null
1344
1439
  a
1345
1440
  @if $var
1346
1441
  b: 1
@@ -1743,6 +1838,26 @@ This selector doesn't have any properties and will not be rendered.
1743
1838
  END
1744
1839
  end
1745
1840
 
1841
+ def test_nonprinting_empty_property
1842
+ assert_equal(<<CSS, render(<<SASS))
1843
+ a {
1844
+ c: "";
1845
+ e: f; }
1846
+ CSS
1847
+ $null-value: null
1848
+ $empty-string: ''
1849
+ $empty-list: (null)
1850
+ a
1851
+ b: $null-value
1852
+ c: $empty-string
1853
+ d: $empty-list
1854
+ e: f
1855
+
1856
+ g
1857
+ h: null
1858
+ SASS
1859
+ end
1860
+
1746
1861
  def test_root_level_pseudo_class_with_new_properties
1747
1862
  assert_equal(<<CSS, render(<<SASS, :property_syntax => :new))
1748
1863
  :focus {