haml-edge 2.3.179 → 2.3.180

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 (92) hide show
  1. data/EDGE_GEM_VERSION +1 -1
  2. data/README.md +88 -149
  3. data/VERSION +1 -1
  4. data/bin/css2sass +7 -1
  5. data/bin/sass-convert +7 -0
  6. data/lib/haml/exec.rb +95 -22
  7. data/lib/haml/template.rb +1 -1
  8. data/lib/haml/util.rb +50 -0
  9. data/lib/sass.rb +1 -1
  10. data/lib/sass/css.rb +38 -210
  11. data/lib/sass/engine.rb +121 -47
  12. data/lib/sass/files.rb +28 -19
  13. data/lib/sass/plugin.rb +32 -43
  14. data/lib/sass/repl.rb +1 -1
  15. data/lib/sass/script.rb +25 -6
  16. data/lib/sass/script/bool.rb +1 -0
  17. data/lib/sass/script/color.rb +2 -2
  18. data/lib/sass/script/css_lexer.rb +22 -0
  19. data/lib/sass/script/css_parser.rb +28 -0
  20. data/lib/sass/script/funcall.rb +17 -9
  21. data/lib/sass/script/functions.rb +46 -1
  22. data/lib/sass/script/interpolation.rb +42 -0
  23. data/lib/sass/script/lexer.rb +142 -34
  24. data/lib/sass/script/literal.rb +28 -12
  25. data/lib/sass/script/node.rb +57 -1
  26. data/lib/sass/script/number.rb +18 -3
  27. data/lib/sass/script/operation.rb +44 -8
  28. data/lib/sass/script/parser.rb +149 -24
  29. data/lib/sass/script/string.rb +50 -2
  30. data/lib/sass/script/unary_operation.rb +25 -10
  31. data/lib/sass/script/variable.rb +20 -11
  32. data/lib/sass/scss.rb +14 -0
  33. data/lib/sass/scss/css_parser.rb +39 -0
  34. data/lib/sass/scss/parser.rb +683 -0
  35. data/lib/sass/scss/rx.rb +112 -0
  36. data/lib/sass/scss/script_lexer.rb +13 -0
  37. data/lib/sass/scss/script_parser.rb +25 -0
  38. data/lib/sass/tree/comment_node.rb +58 -16
  39. data/lib/sass/tree/debug_node.rb +7 -2
  40. data/lib/sass/tree/directive_node.rb +38 -34
  41. data/lib/sass/tree/for_node.rb +6 -0
  42. data/lib/sass/tree/if_node.rb +13 -0
  43. data/lib/sass/tree/import_node.rb +26 -7
  44. data/lib/sass/tree/mixin_def_node.rb +18 -0
  45. data/lib/sass/tree/mixin_node.rb +16 -1
  46. data/lib/sass/tree/node.rb +98 -27
  47. data/lib/sass/tree/prop_node.rb +97 -20
  48. data/lib/sass/tree/root_node.rb +37 -0
  49. data/lib/sass/tree/rule_node.rb +88 -60
  50. data/lib/sass/tree/variable_node.rb +9 -5
  51. data/lib/sass/tree/while_node.rb +4 -0
  52. data/test/haml/results/filters.xhtml +1 -1
  53. data/test/haml/util_test.rb +28 -0
  54. data/test/sass/conversion_test.rb +884 -0
  55. data/test/sass/css2sass_test.rb +46 -21
  56. data/test/sass/engine_test.rb +680 -160
  57. data/test/sass/functions_test.rb +27 -0
  58. data/test/sass/more_results/more_import.css +1 -1
  59. data/test/sass/more_templates/more_import.sass +3 -3
  60. data/test/sass/plugin_test.rb +28 -8
  61. data/test/sass/results/compact.css +1 -1
  62. data/test/sass/results/complex.css +5 -5
  63. data/test/sass/results/compressed.css +1 -1
  64. data/test/sass/results/expanded.css +1 -1
  65. data/test/sass/results/import.css +3 -1
  66. data/test/sass/results/mixins.css +12 -12
  67. data/test/sass/results/nested.css +1 -1
  68. data/test/sass/results/parent_ref.css +4 -4
  69. data/test/sass/results/script.css +3 -3
  70. data/test/sass/results/scss_import.css +15 -0
  71. data/test/sass/results/scss_importee.css +2 -0
  72. data/test/sass/script_conversion_test.rb +153 -0
  73. data/test/sass/script_test.rb +44 -54
  74. data/test/sass/scss/css_test.rb +811 -0
  75. data/test/sass/scss/rx_test.rb +156 -0
  76. data/test/sass/scss/scss_test.rb +871 -0
  77. data/test/sass/scss/test_helper.rb +37 -0
  78. data/test/sass/templates/alt.sass +2 -2
  79. data/test/sass/templates/bork1.sass +1 -1
  80. data/test/sass/templates/import.sass +4 -4
  81. data/test/sass/templates/importee.sass +3 -3
  82. data/test/sass/templates/line_numbers.sass +1 -1
  83. data/test/sass/templates/mixins.sass +2 -2
  84. data/test/sass/templates/nested_mixin_bork.sass +1 -1
  85. data/test/sass/templates/options.sass +1 -1
  86. data/test/sass/templates/parent_ref.sass +2 -2
  87. data/test/sass/templates/script.sass +69 -69
  88. data/test/sass/templates/scss_import.scss +10 -0
  89. data/test/sass/templates/scss_importee.scss +1 -0
  90. data/test/sass/templates/units.sass +10 -10
  91. data/test/test_helper.rb +4 -4
  92. metadata +27 -2
@@ -16,6 +16,24 @@ module Sass
16
16
 
17
17
  protected
18
18
 
19
+ def to_src(tabs, opts, fmt)
20
+ args =
21
+ if @args.empty?
22
+ ""
23
+ else
24
+ '(' + @args.map do |v, d|
25
+ if d
26
+ "#{v.to_sass}: #{d.to_sass}"
27
+ else
28
+ v.to_sass
29
+ end
30
+ end.join(", ") + ')'
31
+ end
32
+
33
+ "#{' ' * tabs}#{fmt == :sass ? '=' : '@mixin '}#{@name}#{args}" +
34
+ children_to_src(tabs, opts, fmt)
35
+ end
36
+
19
37
  # Loads the mixin into the environment.
20
38
  #
21
39
  # @param environment [Sass::Environment] The lexical environment containing
@@ -7,6 +7,12 @@ module Sass::Tree
7
7
  #
8
8
  # @see Sass::Tree
9
9
  class MixinNode < Node
10
+ # @see Node#options=
11
+ def options=(opts)
12
+ super
13
+ @args.each {|a| a.context = :equals} if opts[:sass2]
14
+ end
15
+
10
16
  # @param name [String] The name of the mixin
11
17
  # @param args [Array<Script::Node>] The arguments to the mixin
12
18
  def initialize(name, args)
@@ -22,6 +28,11 @@ module Sass::Tree
22
28
 
23
29
  protected
24
30
 
31
+ def to_src(tabs, opts, fmt)
32
+ args = '(' + @args.map {|a| a.to_sass}.join(", ") + ')' unless @args.empty?
33
+ "#{' ' * tabs}#{fmt == :sass ? '+' : '@include '}#{@name}#{args}#{semi fmt}\n"
34
+ end
35
+
25
36
  # @see Node#_cssize
26
37
  def _cssize(parent)
27
38
  children.map {|c| c.cssize(parent)}.flatten
@@ -52,7 +63,11 @@ END
52
63
  if value
53
64
  value.perform(environment)
54
65
  elsif default
55
- default.perform(env)
66
+ val = default.perform(env)
67
+ if default.context == :equals && val.is_a?(Sass::Script::String)
68
+ val = Sass::Script::String.new(val.value)
69
+ end
70
+ val
56
71
  end)
57
72
  raise Sass::SyntaxError.new("Mixin #{@name} is missing parameter #{var.inspect}.") unless env.var(var.name)
58
73
  env
@@ -26,11 +26,20 @@ module Sass
26
26
  module Tree
27
27
  # The abstract superclass of all parse-tree nodes.
28
28
  class Node
29
+ include Enumerable
30
+
29
31
  # The child nodes of this node.
30
32
  #
31
33
  # @return [Array<Tree::Node>]
32
34
  attr_accessor :children
33
35
 
36
+ # Whether or not this node has child nodes.
37
+ # This may be true even when \{#children} is empty,
38
+ # in which case this node has an empty block (e.g. `{}`).
39
+ #
40
+ # @return [Boolean]
41
+ attr_accessor :has_children
42
+
34
43
  # The line of the document on which this node appeared.
35
44
  #
36
45
  # @return [Fixnum]
@@ -60,6 +69,12 @@ module Sass
60
69
  @options = options
61
70
  end
62
71
 
72
+ # @private
73
+ def children=(children)
74
+ self.has_children ||= !children.empty?
75
+ @children = children
76
+ end
77
+
63
78
  # The name of the document on which this node appeared.
64
79
  #
65
80
  # @return [String]
@@ -73,21 +88,14 @@ module Sass
73
88
  # @raise [Sass::SyntaxError] if `child` is invalid
74
89
  # @see #invalid_child?
75
90
  def <<(child)
91
+ return if child.nil?
76
92
  if msg = invalid_child?(child)
77
93
  raise Sass::SyntaxError.new(msg, :line => child.line)
78
94
  end
95
+ self.has_children = true
79
96
  @children << child
80
97
  end
81
98
 
82
- # Return the last child node.
83
- #
84
- # We need this because {Tree::Node} duck types as an Array for {Sass::Engine}.
85
- #
86
- # @return [Tree::Node] The last child node
87
- def last
88
- children.last
89
- end
90
-
91
99
  # Compares this node and another object (only other {Tree::Node}s will be equal).
92
100
  # This does a structural comparison;
93
101
  # if the contents of the nodes and all the child nodes are equivalent,
@@ -180,6 +188,34 @@ module Sass
180
188
  raise e
181
189
  end
182
190
 
191
+ # Iterates through each node in the tree rooted at this node
192
+ # in a pre-order walk.
193
+ #
194
+ # @yield node
195
+ # @yieldparam node [Node] a node in the tree
196
+ def each(&block)
197
+ yield self
198
+ children.each {|c| c.each(&block)}
199
+ end
200
+
201
+ # Converts a node to Sass code that will generate it.
202
+ #
203
+ # @param tabs [Fixnum] The amount of tabulation to use for the Sass code
204
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
205
+ # @return [String] The Sass code corresponding to the node
206
+ def to_sass(tabs = 0, opts = {})
207
+ to_src(tabs, opts, :sass)
208
+ end
209
+
210
+ # Converts a node to SCSS code that will generate it.
211
+ #
212
+ # @param tabs [Fixnum] The amount of tabulation to use for the SCSS code
213
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
214
+ # @return [String] The Sass code corresponding to the node
215
+ def to_scss(tabs = 0, opts = {})
216
+ to_src(tabs, opts, :scss)
217
+ end
218
+
183
219
  protected
184
220
 
185
221
  # Computes the CSS corresponding to this particular Sass node.
@@ -258,27 +294,21 @@ module Sass
258
294
  children.map {|c| c.perform(environment)}.flatten
259
295
  end
260
296
 
261
- # Replaces SassScript in a chunk of text (via `#{}`)
297
+ # Replaces SassScript in a chunk of text
262
298
  # with the resulting value.
263
299
  #
264
- # @param text [String] The text to interpolate
300
+ # @param text [Array<String, Sass::Script::Node>] The text to interpolate
265
301
  # @param environment [Sass::Environment] The lexical environment containing
266
302
  # variable and mixin values
267
303
  # @return [String] The interpolated text
268
- def interpolate(text, environment)
269
- res = ''
270
- rest = Haml::Shared.handle_interpolation text do |scan|
271
- escapes = scan[2].size
272
- res << scan.matched[0...-2 - escapes]
273
- if escapes % 2 == 1
274
- res << "\\" * (escapes - 1) << '#{'
275
- else
276
- res << "\\" * [0, escapes - 1].max
277
- res << Script::Parser.new(scan, line, scan.pos - scan.matched_size, filename).
278
- parse_interpolated.perform(environment).to_s
279
- end
280
- end
281
- res + rest
304
+ def run_interp(text, environment)
305
+ text.map do |r|
306
+ next r if r.is_a?(String)
307
+ val = r.perform(environment)
308
+ # Interpolated strings should never render with quotes
309
+ next val.value if val.is_a?(Sass::Script::String)
310
+ val.to_s
311
+ end.join.strip
282
312
  end
283
313
 
284
314
  # @see Haml::Shared.balance
@@ -292,7 +322,8 @@ module Sass
292
322
  # Returns an error message if the given child node is invalid,
293
323
  # and false otherwise.
294
324
  #
295
- # By default, all child nodes are valid.
325
+ # By default, all child nodes except those only allowed at root level
326
+ # ({Tree::MixinDefNode}, {Tree::ImportNode}) are valid.
296
327
  # This is expected to be overriden by subclasses
297
328
  # for which some children are invalid.
298
329
  #
@@ -300,7 +331,47 @@ module Sass
300
331
  # @return [Boolean, String] Whether or not the child node is valid,
301
332
  # as well as the error message to display if it is invalid
302
333
  def invalid_child?(child)
303
- false
334
+ case child
335
+ when Tree::MixinDefNode
336
+ "Mixins may only be defined at the root of a document."
337
+ when Tree::ImportNode
338
+ "Import directives may only be used at the root of a document."
339
+ end
340
+ end
341
+
342
+ # Converts a node to Sass or SCSS code that will generate it.
343
+ #
344
+ # This method is called by the default \{#to\_sass} and \{#to\_scss} methods,
345
+ # so that the same code can be used for both with minor variations.
346
+ #
347
+ # @param tabs [Fixnum] The amount of tabulation to use for the SCSS code
348
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
349
+ # @param fmt [Symbol] `:sass` or `:scss`
350
+ # @return [String] The Sass or SCSS code corresponding to the node
351
+ def to_src(tabs, opts, fmt)
352
+ raise NotImplementedError.new("All static-node subclasses of Sass::Tree::Node must override #to_#{fmt}.")
353
+ end
354
+
355
+ # Converts the children of this node to a Sass or SCSS string.
356
+ # This will return the trailing newline for the previous line,
357
+ # including brackets if this is SCSS.
358
+ #
359
+ # @param tabs [Fixnum] The amount of tabulation to use for the Sass code
360
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
361
+ # @param fmt [Symbol] `:sass` or `:scss`
362
+ # @return [String] The Sass or CSS code corresponding to the children
363
+ def children_to_src(tabs, opts, fmt)
364
+ (fmt == :sass ? "\n" : " {\n") +
365
+ children.map {|c| c.send("to_#{fmt}", tabs + 1, opts)}.join.rstrip +
366
+ (fmt == :sass ? "\n" : " }\n")
367
+ end
368
+
369
+ # Returns a semicolon if this is SCSS, or an empty string if this is Sass.
370
+ #
371
+ # @param fmt [Symbol] `:sass` or `:scss`
372
+ # @return [String] A semicolon or the empty string
373
+ def semi(fmt)
374
+ fmt == :sass ? "" : ";"
304
375
  end
305
376
  end
306
377
  end
@@ -3,17 +3,33 @@ module Sass::Tree
3
3
  #
4
4
  # @see Sass::Tree
5
5
  class PropNode < Node
6
- # The name of the property.
6
+ # The name of the property,
7
+ # interspersed with {Sass::Script::Node}s
8
+ # representing `#{}`-interpolation.
9
+ # Any adjacent strings will be merged together.
7
10
  #
8
- # @return [String]
11
+ # @return [Array<String, Sass::Script::Node>]
9
12
  attr_accessor :name
10
13
 
11
- # The value of the property,
12
- # either a plain string or a SassScript parse tree.
14
+ # The name of the property
15
+ # after any interpolated SassScript has been resolved.
16
+ # Only set once \{Tree::Node#perform} has been called.
17
+ #
18
+ # @return [String]
19
+ attr_accessor :resolved_name
20
+
21
+ # The value of the property.
13
22
  #
14
- # @return [String, Script::Node]
23
+ # @return [Sass::Script::Node]
15
24
  attr_accessor :value
16
25
 
26
+ # The value of the property
27
+ # after any interpolated SassScript has been resolved.
28
+ # Only set once \{Tree::Node#perform} has been called.
29
+ #
30
+ # @return [String]
31
+ attr_accessor :resolved_value
32
+
17
33
  # How deep this property is indented
18
34
  # relative to a normal property.
19
35
  # This is only greater than 0 in the case that:
@@ -26,12 +42,13 @@ module Sass::Tree
26
42
  # @return [Fixnum]
27
43
  attr_accessor :tabs
28
44
 
29
- # @param name [String] See \{#name}
30
- # @param value [String] See \{#value}
45
+ # @param name [Array<String, Sass::Script::Node>] See \{#name}
46
+ # @param value [Sass::Script::Node] See \{#value}
31
47
  # @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
32
48
  # `:old` if it uses `:a b`-style syntax
33
49
  def initialize(name, value, prop_syntax)
34
- @name = name
50
+ @name = Haml::Util.strip_string_array(
51
+ Haml::Util.merge_adjacent_strings(name))
35
52
  @value = value
36
53
  @tabs = 0
37
54
  @prop_syntax = prop_syntax
@@ -51,21 +68,33 @@ module Sass::Tree
51
68
  # This only applies for old-style properties with no value,
52
69
  # so returns the empty string if this is new-style.
53
70
  #
71
+ # This should only be called once \{#perform} has been called.
72
+ #
54
73
  # @return [String] The message
55
74
  def pseudo_class_selector_message
56
- return "" if @prop_syntax == :new || !value.empty?
75
+ return "" if @prop_syntax == :new || !resolved_value.empty?
57
76
  "\nIf #{declaration.dump} should be a selector, use \"\\#{declaration}\" instead."
58
77
  end
59
78
 
60
79
  protected
61
80
 
81
+ def to_src(tabs, opts, fmt)
82
+ name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass}}"}.join
83
+ old = opts[:old] && fmt == :sass
84
+ initial = old ? ':' : ''
85
+ mid = old ? '' : ':'
86
+ res = "#{' ' * tabs}#{initial}#{name}#{mid} #{self.class.val_to_sass(value)}"
87
+ return res + "#{semi fmt}\n" if children.empty?
88
+ res.rstrip + children_to_src(tabs, opts, fmt)
89
+ end
90
+
62
91
  # Computes the CSS for the property.
63
92
  #
64
93
  # @param tabs [Fixnum] The level of indentation for the CSS
65
94
  # @return [String] The resulting CSS
66
95
  def _to_s(tabs)
67
- to_return = ' ' * (tabs - 1 + self.tabs) + name + ":" +
68
- (style == :compressed ? '' : ' ') + value + (style == :compressed ? "" : ";")
96
+ to_return = ' ' * (tabs - 1 + self.tabs) + resolved_name + ":" +
97
+ (style == :compressed ? '' : ' ') + resolved_value + (style == :compressed ? "" : ";")
69
98
  end
70
99
 
71
100
  # Converts nested properties into flat properties.
@@ -76,7 +105,7 @@ module Sass::Tree
76
105
  def _cssize(parent)
77
106
  node = super
78
107
  result = node.children.dup
79
- if !node.value.empty? || node.children.empty?
108
+ if !node.resolved_value.empty? || node.children.empty?
80
109
  node.send(:check!)
81
110
  result.unshift(node)
82
111
  end
@@ -89,8 +118,8 @@ module Sass::Tree
89
118
  # @param parent [PropNode, nil] The parent node of this node,
90
119
  # or nil if the parent isn't a {PropNode}
91
120
  def cssize!(parent)
92
- self.name = "#{parent.name}-#{name}" if parent
93
- self.tabs = parent.tabs + (parent.value.empty? ? 0 : 1) if parent && style == :nested
121
+ self.resolved_name = "#{parent.resolved_name}-#{resolved_name}" if parent
122
+ self.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if parent && style == :nested
94
123
  super
95
124
  end
96
125
 
@@ -100,8 +129,14 @@ module Sass::Tree
100
129
  # @param environment [Sass::Environment] The lexical environment containing
101
130
  # variable and mixin values
102
131
  def perform!(environment)
103
- @name = interpolate(@name, environment)
104
- @value = @value.is_a?(String) ? interpolate(@value, environment) : @value.perform(environment).to_s
132
+ @resolved_name = run_interp(@name, environment)
133
+ val = @value.perform(environment)
134
+ @resolved_value =
135
+ if @value.context == :equals && val.is_a?(Sass::Script::String)
136
+ val.value
137
+ else
138
+ val.to_s
139
+ end
105
140
  super
106
141
  end
107
142
 
@@ -124,16 +159,58 @@ module Sass::Tree
124
159
  raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.")
125
160
  elsif @options[:property_syntax] == :new && @prop_syntax == :old
126
161
  raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.")
127
- elsif value[-1] == ?;
128
- raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no \";\" required at end-of-line).")
129
- elsif value.empty?
162
+ elsif resolved_value.empty?
130
163
  raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
131
164
  pseudo_class_selector_message)
132
165
  end
133
166
  end
134
167
 
135
168
  def declaration
136
- (@prop_syntax == :new ? "#{name}: #{value}" : ":#{name} #{value}").strip
169
+ if @prop_syntax == :new
170
+ "#{resolved_name}: #{resolved_value}"
171
+ else
172
+ ":#{resolved_name} #{resolved_value}"
173
+ end.strip
174
+ end
175
+
176
+ class << self
177
+ # @private
178
+ def val_to_sass(value)
179
+ return value.to_sass unless value.context == :equals
180
+ val_to_sass_comma(value).to_sass
181
+ end
182
+
183
+ private
184
+
185
+ def val_to_sass_comma(node)
186
+ return node unless node.is_a?(Sass::Script::Operation)
187
+ return val_to_sass_concat(node) unless node.operator == :comma
188
+
189
+ Sass::Script::Operation.new(
190
+ val_to_sass_concat(node.operand1),
191
+ val_to_sass_comma(node.operand2),
192
+ node.operator)
193
+ end
194
+
195
+ def val_to_sass_concat(node)
196
+ return node unless node.is_a?(Sass::Script::Operation)
197
+ return val_to_sass_div(node) unless node.operator == :concat
198
+
199
+ Sass::Script::Operation.new(
200
+ val_to_sass_div(node.operand1),
201
+ val_to_sass_concat(node.operand2),
202
+ node.operator)
203
+ end
204
+
205
+ def val_to_sass_div(node)
206
+ unless node.is_a?(Sass::Script::Operation) && node.operator == :div &&
207
+ node.operand1.is_a?(Sass::Script::Number) &&
208
+ node.operand2.is_a?(Sass::Script::Number)
209
+ return node
210
+ end
211
+
212
+ Sass::Script::String.new("(#{node.to_sass})")
213
+ end
137
214
  end
138
215
  end
139
216
  end
@@ -44,8 +44,37 @@ module Sass
44
44
  super
45
45
  end
46
46
 
47
+ # Converts a node to Sass code that will generate it.
48
+ #
49
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
50
+ # @return [String] The Sass code corresponding to the node
51
+ def to_sass(opts = {})
52
+ to_src(opts, :sass)
53
+ end
54
+
55
+ # Converts a node to SCSS code that will generate it.
56
+ #
57
+ # @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
58
+ # @return [String] The SCSS code corresponding to the node
59
+ def to_scss(opts = {})
60
+ to_src(opts, :scss)
61
+ end
62
+
47
63
  protected
48
64
 
65
+ def to_src(opts, fmt)
66
+ Haml::Util.enum_cons(children + [nil], 2).map do |child, nxt|
67
+ child.send("to_#{fmt}", 0, opts) +
68
+ if nxt &&
69
+ (child.is_a?(CommentNode) && child.line + child.value.count("\n") + 1 == nxt.line) ||
70
+ (child.is_a?(ImportNode) && nxt.is_a?(ImportNode) && child.line + 1 == nxt.line)
71
+ ""
72
+ else
73
+ "\n"
74
+ end
75
+ end.join.rstrip + "\n"
76
+ end
77
+
49
78
  # Destructively converts this static Sass node into a static CSS node,
50
79
  # and checks that there are no properties at root level.
51
80
  #
@@ -76,6 +105,14 @@ module Sass
76
105
  return "" if result.empty?
77
106
  return result + "\n"
78
107
  end
108
+
109
+ # Returns false, because all nodes are allowed at the root of the document
110
+ # (properties are detected elsewhere post-mixin-resolution).
111
+ #
112
+ # @see Node#invalid_child?
113
+ def invalid_child?(child)
114
+ false
115
+ end
79
116
  end
80
117
  end
81
118
  end