haml-edge 2.3.179 → 2.3.180

Sign up to get free protection for your applications and to get access to all the features.
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