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
@@ -1,5 +1,5 @@
1
1
  module Sass::Tree
2
- # A static node representing an unproccessed Sass `@`-directive.
2
+ # A static node representing an unprocessed Sass `@`-directive.
3
3
  # Directives known to Sass, like `@for` and `@debug`,
4
4
  # are handled by their own nodes;
5
5
  # only CSS directives like `@media` and `@font-face` become {DirectiveNode}s.
@@ -43,7 +43,13 @@ module Sass::Tree
43
43
 
44
44
  # @return [String] The name of the directive, including `@`.
45
45
  def name
46
- value.first.gsub(/ .*$/, '')
46
+ @name ||= value.first.gsub(/ .*$/, '')
47
+ end
48
+
49
+ # Strips out any vendor prefixes and downcases the directive name.
50
+ # @return [String] The normalized name of the directive.
51
+ def normalized_name
52
+ @normalized_name ||= name.gsub(/^(@)(?:-[a-zA-Z0-9]+-)?/, '\1').downcase
47
53
  end
48
54
 
49
55
  def bubbles?
@@ -0,0 +1,18 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a Sass `@error` statement.
4
+ #
5
+ # @see Sass::Tree
6
+ class ErrorNode < Node
7
+ # The expression to print.
8
+ # @return [Script::Tree::Node]
9
+ attr_accessor :expr
10
+
11
+ # @param expr [Script::Tree::Node] The expression to print
12
+ def initialize(expr)
13
+ @expr = expr
14
+ super()
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,7 +1,7 @@
1
1
  require 'sass/tree/node'
2
2
 
3
3
  module Sass::Tree
4
- # A static node reprenting an `@extend` directive.
4
+ # A static node representing an `@extend` directive.
5
5
  #
6
6
  # @see Sass::Tree
7
7
  class ExtendNode < Node
@@ -20,6 +20,12 @@ module Sass
20
20
  # @return [Script::Tree::Node?]
21
21
  attr_accessor :splat
22
22
 
23
+ # Strips out any vendor prefixes.
24
+ # @return [String] The normalized name of the directive.
25
+ def normalized_name
26
+ @normalized_name ||= name.gsub(/^(?:-[a-zA-Z0-9]+-)?/, '\1')
27
+ end
28
+
23
29
  # @param name [String] The function name
24
30
  # @param args [Array<(Script::Tree::Node, Script::Tree::Node)>]
25
31
  # The arguments for the function.
@@ -29,6 +35,9 @@ module Sass
29
35
  @args = args
30
36
  @splat = splat
31
37
  super()
38
+
39
+ return unless %w(and or not).include?(name)
40
+ raise Sass::SyntaxError.new("Invalid function name \"#{name}\".")
32
41
  end
33
42
  end
34
43
  end
@@ -55,13 +55,14 @@ module Sass
55
55
  return f if f
56
56
  end
57
57
 
58
- message = "File to import not found or unreadable: #{@imported_filename}.\n"
58
+ lines = ["File to import not found or unreadable: #{@imported_filename}."]
59
+
59
60
  if paths.size == 1
60
- message << "Load path: #{paths.first}"
61
- else
62
- message << "Load paths:\n " << paths.join("\n ")
61
+ lines << "Load path: #{paths.first}"
62
+ elsif !paths.empty?
63
+ lines << "Load paths:\n #{paths.join("\n ")}"
63
64
  end
64
- raise SyntaxError.new(message)
65
+ raise SyntaxError.new(lines.join("\n"))
65
66
  rescue SyntaxError => e
66
67
  raise SyntaxError.new(e.message, :line => line, :filename => @filename)
67
68
  end
@@ -0,0 +1,15 @@
1
+ module Sass::Tree
2
+ class KeyframeRuleNode < Node
3
+ # The text of the directive after any interpolated SassScript has been resolved.
4
+ # Since this is only a static node, this is the only value property.
5
+ #
6
+ # @return [String]
7
+ attr_accessor :resolved_value
8
+
9
+ # @param resolved_value [String] See \{#resolved_value}
10
+ def initialize(resolved_value)
11
+ @resolved_value = resolved_value
12
+ super()
13
+ end
14
+ end
15
+ end
@@ -69,7 +69,7 @@ module Sass
69
69
 
70
70
  # The line of the document on which this node appeared.
71
71
  #
72
- # @return [Fixnum]
72
+ # @return [Integer]
73
73
  attr_accessor :line
74
74
 
75
75
  # The source range in the document on which this node appeared.
@@ -83,13 +83,15 @@ module Sass
83
83
  attr_writer :filename
84
84
 
85
85
  # The options hash for the node.
86
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
86
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
87
87
  #
88
88
  # @return [{Symbol => Object}]
89
89
  attr_reader :options
90
90
 
91
91
  def initialize
92
92
  @children = []
93
+ @filename = nil
94
+ @options = nil
93
95
  end
94
96
 
95
97
  # Sets the options hash for the node and all its children.
@@ -149,7 +151,7 @@ module Sass
149
151
  # @return [Boolean]
150
152
  def invisible?; false; end
151
153
 
152
- # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
154
+ # The output style. See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
153
155
  #
154
156
  # @return [Symbol]
155
157
  def style
@@ -39,7 +39,7 @@ module Sass::Tree
39
39
  # * This is a child property of another property
40
40
  # * The parent property has a value, and thus will be rendered
41
41
  #
42
- # @return [Fixnum]
42
+ # @return [Integer]
43
43
  attr_accessor :tabs
44
44
 
45
45
  # The source range in which the property name appears.
@@ -97,7 +97,7 @@ module Sass::Tree
97
97
  # @param opts [{Symbol => Object}] The options hash for the tree.
98
98
  # @param fmt [Symbol] `:scss` or `:sass`.
99
99
  def declaration(opts = {:old => @prop_syntax == :old}, fmt = :sass)
100
- name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass(opts)}}"}.join
100
+ name = self.name.map {|n| n.is_a?(String) ? n : n.to_sass(opts)}.join
101
101
  if name[0] == ?:
102
102
  raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\"" +
103
103
  " hack is not allowed in the Sass indented syntax")
@@ -119,11 +119,10 @@ module Sass::Tree
119
119
  private
120
120
 
121
121
  def check!
122
- if @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
123
- raise Sass::SyntaxError.new(
124
- "Illegal property syntax: can't use #{@prop_syntax} syntax when " +
125
- ":property_syntax => #{@options[:property_syntax].inspect} is set.")
126
- end
122
+ return unless @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
123
+ raise Sass::SyntaxError.new(
124
+ "Illegal property syntax: can't use #{@prop_syntax} syntax when " +
125
+ ":property_syntax => #{@options[:property_syntax].inspect} is set.")
127
126
  end
128
127
 
129
128
  class << self
@@ -1,7 +1,7 @@
1
1
  require 'pathname'
2
2
 
3
3
  module Sass::Tree
4
- # A static node reprenting a CSS rule.
4
+ # A static node representing a CSS rule.
5
5
  #
6
6
  # @see Sass::Tree
7
7
  class RuleNode < Node
@@ -40,7 +40,7 @@ module Sass::Tree
40
40
  # * This is a child rule of another rule
41
41
  # * The parent rule has properties, and thus will be rendered
42
42
  #
43
- # @return [Fixnum]
43
+ # @return [Integer]
44
44
  attr_accessor :tabs
45
45
 
46
46
  # The entire selector source range for this rule.
@@ -60,15 +60,20 @@ module Sass::Tree
60
60
  # @return [String]
61
61
  attr_accessor :stack_trace
62
62
 
63
- # @param rule [Array<String, Sass::Script::Tree::Node>]
63
+ # @param rule [Array<String, Sass::Script::Tree::Node>, Sass::Selector::CommaSequence]
64
+ # The CSS rule, either unparsed or parsed.
64
65
  # @param selector_source_range [Sass::Source::Range]
65
- # The CSS rule. See \{#rule}
66
66
  def initialize(rule, selector_source_range = nil)
67
- merged = Sass::Util.merge_adjacent_strings(rule)
68
- @rule = Sass::Util.strip_string_array(merged)
67
+ if rule.is_a?(Sass::Selector::CommaSequence)
68
+ @rule = [rule.to_s]
69
+ @parsed_rules = rule
70
+ else
71
+ merged = Sass::Util.merge_adjacent_strings(rule)
72
+ @rule = Sass::Util.strip_string_array(merged)
73
+ try_to_parse_non_interpolated_rules
74
+ end
69
75
  @selector_source_range = selector_source_range
70
76
  @tabs = 0
71
- try_to_parse_non_interpolated_rules
72
77
  super()
73
78
  end
74
79
 
@@ -127,14 +132,24 @@ module Sass::Tree
127
132
  private
128
133
 
129
134
  def try_to_parse_non_interpolated_rules
130
- if @rule.all? {|t| t.kind_of?(String)}
131
- # We don't use real filename/line info because we don't have it yet.
132
- # When we get it, we'll set it on the parsed rules if possible.
133
- parser = Sass::SCSS::StaticParser.new(@rule.join.strip, '', nil, 1)
135
+ @parsed_rules = nil
136
+ return unless @rule.all? {|t| t.is_a?(String)}
137
+
138
+ # We don't use real filename/line info because we don't have it yet.
139
+ # When we get it, we'll set it on the parsed rules if possible.
140
+ parser = nil
141
+ warnings = Sass::Util.silence_warnings do
142
+ parser = Sass::SCSS::StaticParser.new(@rule.join.strip, nil, nil, 1)
134
143
  # rubocop:disable RescueModifier
135
144
  @parsed_rules = parser.parse_selector rescue nil
136
145
  # rubocop:enable RescueModifier
146
+
147
+ $stderr.string
137
148
  end
149
+
150
+ # If parsing produces a warning, throw away the result so we can parse
151
+ # later with the real filename info.
152
+ @parsed_rules = nil unless warnings.empty?
138
153
  end
139
154
  end
140
155
  end
@@ -4,6 +4,8 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
4
4
 
5
5
  def initialize
6
6
  @parents = []
7
+ @parent = nil
8
+ @current_mixin_def = nil
7
9
  end
8
10
 
9
11
  def visit(node)
@@ -23,13 +25,34 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
23
25
  SCRIPT_NODES = [Sass::Tree::ImportNode] + CONTROL_NODES
24
26
  def visit_children(parent)
25
27
  old_parent = @parent
26
- @parent = parent unless is_any_of?(parent, SCRIPT_NODES) ||
27
- (parent.bubbles? && !old_parent.is_a?(Sass::Tree::RootNode))
28
+
29
+ # When checking a static tree, resolve at-roots to be sure they won't send
30
+ # nodes where they don't belong.
31
+ if parent.is_a?(Sass::Tree::AtRootNode) && parent.resolved_value
32
+ old_parents = @parents
33
+ @parents = @parents.reject {|p| parent.exclude_node?(p)}
34
+ @parent = Sass::Util.enum_with_index(@parents.reverse).
35
+ find {|p, i| !transparent_parent?(p, @parents[-i - 2])}.first
36
+
37
+ begin
38
+ return super
39
+ ensure
40
+ @parents = old_parents
41
+ @parent = old_parent
42
+ end
43
+ end
44
+
45
+ unless transparent_parent?(parent, old_parent)
46
+ @parent = parent
47
+ end
48
+
28
49
  @parents.push parent
29
- super
30
- ensure
31
- @parent = old_parent
32
- @parents.pop
50
+ begin
51
+ super
52
+ ensure
53
+ @parent = old_parent
54
+ @parents.pop
55
+ end
33
56
  end
34
57
 
35
58
  def visit_root(node)
@@ -69,9 +92,8 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
69
92
 
70
93
  VALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
71
94
  def invalid_extend_parent?(parent, child)
72
- unless is_any_of?(parent, VALID_EXTEND_PARENTS)
73
- return "Extend directives may only be used within rules."
74
- end
95
+ return if is_any_of?(parent, VALID_EXTEND_PARENTS)
96
+ "Extend directives may only be used within rules."
75
97
  end
76
98
 
77
99
  INVALID_IMPORT_PARENTS = CONTROL_NODES +
@@ -89,44 +111,38 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
89
111
  end
90
112
 
91
113
  def invalid_mixindef_parent?(parent, child)
92
- unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
93
- return "Mixins may not be defined within control directives or other mixins."
94
- end
114
+ return if (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
115
+ "Mixins may not be defined within control directives or other mixins."
95
116
  end
96
117
 
97
118
  def invalid_function_parent?(parent, child)
98
- unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
99
- return "Functions may not be defined within control directives or other mixins."
100
- end
119
+ return if (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
120
+ "Functions may not be defined within control directives or other mixins."
101
121
  end
102
122
 
103
123
  VALID_FUNCTION_CHILDREN = [
104
124
  Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::ReturnNode,
105
- Sass::Tree::VariableNode, Sass::Tree::WarnNode
125
+ Sass::Tree::VariableNode, Sass::Tree::WarnNode, Sass::Tree::ErrorNode
106
126
  ] + CONTROL_NODES
107
127
  def invalid_function_child?(parent, child)
108
- unless is_any_of?(child, VALID_FUNCTION_CHILDREN)
109
- "Functions can only contain variable declarations and control directives."
110
- end
128
+ return if is_any_of?(child, VALID_FUNCTION_CHILDREN)
129
+ "Functions can only contain variable declarations and control directives."
111
130
  end
112
131
 
113
- VALID_PROP_CHILDREN = CONTROL_NODES + [Sass::Tree::CommentNode,
114
- Sass::Tree::PropNode,
115
- Sass::Tree::MixinNode]
132
+ VALID_PROP_CHILDREN = CONTROL_NODES + [Sass::Tree::CommentNode,
133
+ Sass::Tree::PropNode,
134
+ Sass::Tree::MixinNode]
116
135
  def invalid_prop_child?(parent, child)
117
- unless is_any_of?(child, VALID_PROP_CHILDREN)
118
- "Illegal nesting: Only properties may be nested beneath properties."
119
- end
136
+ return if is_any_of?(child, VALID_PROP_CHILDREN)
137
+ "Illegal nesting: Only properties may be nested beneath properties."
120
138
  end
121
139
 
122
- VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
123
- Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode,
124
- Sass::Tree::MixinNode]
140
+ VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::KeyframeRuleNode, Sass::Tree::PropNode,
141
+ Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode, Sass::Tree::MixinNode]
125
142
  def invalid_prop_parent?(parent, child)
126
- unless is_any_of?(parent, VALID_PROP_PARENTS)
127
- "Properties are only allowed within rules, directives, mixin includes, or other properties." +
128
- child.pseudo_class_selector_message
129
- end
143
+ return if is_any_of?(parent, VALID_PROP_PARENTS)
144
+ "Properties are only allowed within rules, directives, mixin includes, or other properties." +
145
+ child.pseudo_class_selector_message
130
146
  end
131
147
 
132
148
  def invalid_return_parent?(parent, child)
@@ -135,6 +151,14 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
135
151
 
136
152
  private
137
153
 
154
+ # Whether `parent` should be assigned to `@parent`.
155
+ def transparent_parent?(parent, grandparent)
156
+ is_any_of?(parent, SCRIPT_NODES) ||
157
+ (parent.bubbles? &&
158
+ !grandparent.is_a?(Sass::Tree::RootNode) &&
159
+ !grandparent.is_a?(Sass::Tree::AtRootNode))
160
+ end
161
+
138
162
  def is_any_of?(val, classes)
139
163
  classes.each do |c|
140
164
  return true if val.is_a?(c)
@@ -18,15 +18,19 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
18
18
  @tabs = 0
19
19
  # 2 spaces by default
20
20
  @tab_chars = @options[:indent] || " "
21
+ @is_else = false
21
22
  end
22
23
 
23
24
  def visit_children(parent)
24
25
  @tabs += 1
25
26
  return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
27
+
28
+ res = visit_rule_level(parent.children)
29
+
26
30
  if @format == :sass
27
- "\n" + super.join.rstrip + "\n"
31
+ "\n" + res.rstrip + "\n"
28
32
  else
29
- " {\n" + super.join.rstrip + "\n#{ @tab_chars * (@tabs - 1)}}\n"
33
+ " {\n" + res.rstrip + "\n#{@tab_chars * (@tabs - 1)}}\n"
30
34
  end
31
35
  ensure
32
36
  @tabs -= 1
@@ -34,20 +38,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
34
38
 
35
39
  # Ensures proper spacing between top-level nodes.
36
40
  def visit_root(node)
37
- Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
38
- visit(child) +
39
- if nxt &&
40
- (child.is_a?(Sass::Tree::CommentNode) &&
41
- child.line + child.lines + 1 == nxt.line) ||
42
- (child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
43
- child.line + 1 == nxt.line) ||
44
- (child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
45
- child.line + 1 == nxt.line)
46
- ""
47
- else
48
- "\n"
49
- end
50
- end.join.rstrip + "\n"
41
+ visit_rule_level(node.children)
51
42
  end
52
43
 
53
44
  def visit_charset(node)
@@ -57,14 +48,14 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
57
48
  def visit_comment(node)
58
49
  value = interp_to_src(node.value)
59
50
  if @format == :sass
60
- content = value.gsub(/\*\/$/, '').rstrip
51
+ content = value.gsub(%r{\*/$}, '').rstrip
61
52
  if content =~ /\A[ \t]/
62
53
  # Re-indent SCSS comments like this:
63
54
  # /* foo
64
55
  # bar
65
56
  # baz */
66
57
  content.gsub!(/^/, ' ')
67
- content.sub!(/\A([ \t]*)\/\*/, '/*\1')
58
+ content.sub!(%r{\A([ \t]*)/\*}, '/*\1')
68
59
  end
69
60
 
70
61
  if content.include?("\n")
@@ -78,13 +69,13 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
78
69
  end
79
70
  end
80
71
 
81
- content.gsub!(/\A\/\*/, '//') if node.type == :silent
72
+ content.gsub!(%r{\A/\*}, '//') if node.type == :silent
82
73
  content.gsub!(/^/, tab_str)
83
74
  content = content.rstrip + "\n"
84
75
  else
85
76
  spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
86
77
  content = if node.type == :silent
87
- value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
78
+ value.gsub(%r{^[/ ]\*}, '//').gsub(%r{ *\*/$}, '')
88
79
  else
89
80
  value
90
81
  end.gsub(/^/, spaces) + "\n"
@@ -96,11 +87,15 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
96
87
  "#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
97
88
  end
98
89
 
90
+ def visit_error(node)
91
+ "#{tab_str}@error #{node.expr.to_sass(@options)}#{semi}\n"
92
+ end
93
+
99
94
  def visit_directive(node)
100
95
  res = "#{tab_str}#{interp_to_src(node.value)}"
101
96
  res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
102
97
  return res + "#{semi}\n" unless node.has_children
103
- res + yield + "\n"
98
+ res + yield
104
99
  end
105
100
 
106
101
  def visit_each(node)
@@ -109,13 +104,13 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
109
104
  end
110
105
 
111
106
  def visit_extend(node)
112
- "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}" +
113
- "#{" !optional" if node.optional?}\n"
107
+ "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}" +
108
+ "#{' !optional' if node.optional?}#{semi}\n"
114
109
  end
115
110
 
116
111
  def visit_for(node)
117
112
  "#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
118
- "#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
113
+ "#{node.exclusive ? 'to' : 'through'} #{node.to.to_sass(@options)}#{yield}"
119
114
  end
120
115
 
121
116
  def visit_function(node)
@@ -169,6 +164,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
169
164
  else
170
165
  str = "#{tab_str}@import #{node.uri}"
171
166
  end
167
+ str << " supports(#{node.supports_condition.to_src(@options)})" if node.supports_condition
172
168
  str << " #{interp_to_src(node.query)}" unless node.query.empty?
173
169
  "#{str}#{semi}\n"
174
170
  end
@@ -236,12 +232,13 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
236
232
  end
237
233
 
238
234
  def visit_rule(node)
235
+ rule = node.parsed_rules ? [node.parsed_rules.to_s] : node.rule
239
236
  if @format == :sass
240
- name = selector_to_sass(node.rule)
237
+ name = selector_to_sass(rule)
241
238
  name = "\\" + name if name[0] == ?:
242
239
  name.gsub(/^/, tab_str) + yield
243
240
  elsif @format == :scss
244
- name = selector_to_scss(node.rule)
241
+ name = selector_to_scss(rule)
245
242
  res = name + yield
246
243
  if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent
247
244
  res.slice!(-3..-1)
@@ -269,35 +266,53 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
269
266
  "#{tab_str}@at-root #{query_interp_to_src(node.query)}#{yield}"
270
267
  elsif node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode)
271
268
  rule = node.children.first
272
- "#{tab_str}@at-root #{selector_to_src(rule.rule)}#{visit_children(rule)}"
269
+ "#{tab_str}@at-root #{selector_to_src(rule.rule).lstrip}#{visit_children(rule)}"
273
270
  else
274
271
  "#{tab_str}@at-root#{yield}"
275
272
  end
276
273
  end
277
274
 
275
+ def visit_keyframerule(node)
276
+ "#{tab_str}#{node.resolved_value}#{yield}"
277
+ end
278
+
278
279
  private
279
280
 
281
+ # Visit rule-level nodes and return their conversion with appropriate
282
+ # whitespace added.
283
+ def visit_rule_level(nodes)
284
+ Sass::Util.enum_cons(nodes + [nil], 2).map do |child, nxt|
285
+ visit(child) +
286
+ if nxt &&
287
+ (child.is_a?(Sass::Tree::CommentNode) && child.line + child.lines + 1 == nxt.line) ||
288
+ (child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
289
+ child.line + 1 == nxt.line) ||
290
+ (child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
291
+ child.line + 1 == nxt.line) ||
292
+ (child.is_a?(Sass::Tree::PropNode) && nxt.is_a?(Sass::Tree::PropNode)) ||
293
+ (child.is_a?(Sass::Tree::MixinNode) && nxt.is_a?(Sass::Tree::MixinNode) &&
294
+ child.line + 1 == nxt.line)
295
+ ""
296
+ else
297
+ "\n"
298
+ end
299
+ end.join.rstrip + "\n"
300
+ end
301
+
280
302
  def interp_to_src(interp)
281
- interp.map do |r|
282
- next r if r.is_a?(String)
283
- "\#{#{r.to_sass(@options)}}"
284
- end.join
303
+ interp.map {|r| r.is_a?(String) ? r : r.to_sass(@options)}.join
285
304
  end
286
305
 
287
306
  # Like interp_to_src, but removes the unnecessary `#{}` around the keys and
288
307
  # values in query expressions.
289
308
  def query_interp_to_src(interp)
290
- Sass::Util.enum_with_index(interp).map do |r, i|
291
- next r if r.is_a?(String)
292
- before, after = interp[i - 1], interp[i + 1]
293
- if before.is_a?(String) && after.is_a?(String) &&
294
- ((before[-1] == ?( && after[0] == ?:) ||
295
- (before =~ /:\s*/ && after[0] == ?)))
296
- r.to_sass(@options)
297
- else
298
- "\#{#{r.to_sass(@options)}}"
299
- end
300
- end.join
309
+ interp = interp.map do |e|
310
+ next e unless e.is_a?(Sass::Script::Tree::Literal)
311
+ next e unless e.value.is_a?(Sass::Script::Value::String)
312
+ e.value.value
313
+ end
314
+
315
+ interp_to_src(interp)
301
316
  end
302
317
 
303
318
  def selector_to_src(sel)
@@ -309,7 +324,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
309
324
  if r.is_a?(String)
310
325
  r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
311
326
  else
312
- "\#{#{r.to_sass(@options)}}"
327
+ r.to_sass(@options)
313
328
  end
314
329
  end.join
315
330
  end
@@ -328,7 +343,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
328
343
 
329
344
  def dasherize(s)
330
345
  if @options[:dasherize]
331
- s.gsub('_', '-')
346
+ s.tr('_', '-')
332
347
  else
333
348
  s
334
349
  end