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,9 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
  require File.dirname(__FILE__) + '/../test_helper'
3
+ require 'fileutils'
3
4
  require 'sass/util/test'
4
5
  require 'tmpdir'
5
6
 
6
- class ExecTest < Test::Unit::TestCase
7
+ class ExecTest < MiniTest::Test
7
8
  include Sass::Util::Test
8
9
 
9
10
  def setup
@@ -19,7 +20,7 @@ class ExecTest < Test::Unit::TestCase
19
20
  src = get_path("src.scss")
20
21
  dest = get_path("dest.css")
21
22
  write(src, ".ruleset { margin: 0 }")
22
- assert(exec(*%w[scss -t expanded --unix-newlines].push(src, dest)))
23
+ assert(exec(*%w[scss --sourcemap=none -t expanded --unix-newlines].push(src, dest)))
23
24
  assert_equal(".ruleset {\n margin: 0;\n}\n", read(dest))
24
25
  end
25
26
 
@@ -64,6 +65,14 @@ class ExecTest < Test::Unit::TestCase
64
65
  assert_equal(".ruleset\r\n margin: 0\r\n", read(src))
65
66
  end
66
67
 
68
+ def test_sass_convert_R
69
+ Dir.chdir(@dir) do
70
+ src = get_path("styles/src.css")
71
+ write(src, ".ruleset { margin: 0 }")
72
+ assert(exec(*%w[sass-convert -Rq --from css --to scss --trace styles]))
73
+ end
74
+ end
75
+
67
76
  private
68
77
 
69
78
  def get_path(name)
@@ -75,6 +84,7 @@ class ExecTest < Test::Unit::TestCase
75
84
  end
76
85
 
77
86
  def write(file, content)
87
+ FileUtils.mkdir_p(File.dirname(file))
78
88
  open(file, 'wb') {|f| f.write(content)}
79
89
  end
80
90
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  require File.dirname(__FILE__) + '/../test_helper'
3
3
 
4
- class ExtendTest < Test::Unit::TestCase
4
+ class ExtendTest < MiniTest::Test
5
5
  def test_basic
6
6
  assert_equal <<CSS, render(<<SCSS)
7
7
  .foo, .bar {
@@ -145,8 +145,8 @@ SCSS
145
145
  end
146
146
 
147
147
  def test_class_unification
148
- assert_unification '.foo.bar', '.baz {@extend .foo}', '.foo.bar, .bar.baz'
149
- assert_unification '.foo.baz', '.baz {@extend .foo}', '.baz'
148
+ assert_unification '.foo.bar', '.baz {@extend .foo}', '.foo.bar, .bar.baz'
149
+ assert_unification '.foo.baz', '.baz {@extend .foo}', '.baz'
150
150
  end
151
151
 
152
152
  def test_id_unification
@@ -167,42 +167,52 @@ SCSS
167
167
  end
168
168
 
169
169
  def test_universal_unification_with_namespaceless_universal_target
170
+ assert_extend_doesnt_match('ns|*', '.foo', :failed_to_unify, 2) do
171
+ render_unification '*.foo', 'ns|* {@extend .foo}'
172
+ end
173
+
170
174
  assert_unification '*.foo', '* {@extend .foo}', '*'
171
175
  assert_unification '*.foo', '*|* {@extend .foo}', '*'
172
176
  assert_unification '*|*.foo', '* {@extend .foo}', '*|*.foo, *'
173
177
  assert_unification '*|*.foo', '*|* {@extend .foo}', '*|*'
174
- assert_unification '*.foo', 'ns|* {@extend .foo}', '*.foo, ns|*'
175
178
  assert_unification '*|*.foo', 'ns|* {@extend .foo}', '*|*.foo, ns|*'
176
179
  end
177
180
 
178
181
  def test_universal_unification_with_namespaced_universal_target
179
- assert_unification 'ns|*.foo', '* {@extend .foo}', 'ns|*'
180
- assert_unification 'ns|*.foo', '*|* {@extend .foo}', 'ns|*'
182
+ assert_extend_doesnt_match('*', '.foo', :failed_to_unify, 2) do
183
+ render_unification 'ns|*.foo', '* {@extend .foo}'
184
+ end
181
185
 
182
186
  assert_extend_doesnt_match('ns2|*', '.foo', :failed_to_unify, 2) do
183
187
  render_unification 'ns1|*.foo', 'ns2|* {@extend .foo}'
184
188
  end
185
189
 
190
+ assert_unification 'ns|*.foo', '*|* {@extend .foo}', 'ns|*'
186
191
  assert_unification 'ns|*.foo', 'ns|* {@extend .foo}', 'ns|*'
187
192
  end
188
193
 
189
194
  def test_universal_unification_with_namespaceless_element_target
195
+ assert_extend_doesnt_match('ns|*', '.foo', :failed_to_unify, 2) do
196
+ render_unification 'a.foo', 'ns|* {@extend .foo}'
197
+ end
198
+
190
199
  assert_unification 'a.foo', '* {@extend .foo}', 'a'
191
200
  assert_unification 'a.foo', '*|* {@extend .foo}', 'a'
192
201
  assert_unification '*|a.foo', '* {@extend .foo}', '*|a.foo, a'
193
202
  assert_unification '*|a.foo', '*|* {@extend .foo}', '*|a'
194
- assert_unification 'a.foo', 'ns|* {@extend .foo}', 'a.foo, ns|a'
195
203
  assert_unification '*|a.foo', 'ns|* {@extend .foo}', '*|a.foo, ns|a'
196
204
  end
197
205
 
198
206
  def test_universal_unification_with_namespaced_element_target
199
- assert_unification 'ns|a.foo', '* {@extend .foo}', 'ns|a'
200
- assert_unification 'ns|a.foo', '*|* {@extend .foo}', 'ns|a'
207
+ assert_extend_doesnt_match('*', '.foo', :failed_to_unify, 2) do
208
+ render_unification 'ns|a.foo', '* {@extend .foo}'
209
+ end
201
210
 
202
211
  assert_extend_doesnt_match('ns2|*', '.foo', :failed_to_unify, 2) do
203
212
  render_unification 'ns1|a.foo', 'ns2|* {@extend .foo}'
204
213
  end
205
214
 
215
+ assert_unification 'ns|a.foo', '*|* {@extend .foo}', 'ns|a'
206
216
  assert_unification 'ns|a.foo', 'ns|* {@extend .foo}', 'ns|a'
207
217
  end
208
218
 
@@ -214,46 +224,56 @@ SCSS
214
224
  end
215
225
 
216
226
  def test_element_unification_with_namespaceless_universal_target
227
+ assert_extend_doesnt_match('ns|a', '.foo', :failed_to_unify, 2) do
228
+ render_unification '*.foo', 'ns|a {@extend .foo}'
229
+ end
230
+
217
231
  assert_unification '*.foo', 'a {@extend .foo}', '*.foo, a'
218
232
  assert_unification '*.foo', '*|a {@extend .foo}', '*.foo, a'
219
233
  assert_unification '*|*.foo', 'a {@extend .foo}', '*|*.foo, a'
220
234
  assert_unification '*|*.foo', '*|a {@extend .foo}', '*|*.foo, *|a'
221
- assert_unification '*.foo', 'ns|a {@extend .foo}', '*.foo, ns|a'
222
235
  assert_unification '*|*.foo', 'ns|a {@extend .foo}', '*|*.foo, ns|a'
223
236
  end
224
237
 
225
238
  def test_element_unification_with_namespaced_universal_target
226
- assert_unification 'ns|*.foo', 'a {@extend .foo}', 'ns|*.foo, ns|a'
227
- assert_unification 'ns|*.foo', '*|a {@extend .foo}', 'ns|*.foo, ns|a'
239
+ assert_extend_doesnt_match('a', '.foo', :failed_to_unify, 2) do
240
+ render_unification 'ns|*.foo', 'a {@extend .foo}'
241
+ end
228
242
 
229
243
  assert_extend_doesnt_match('ns2|a', '.foo', :failed_to_unify, 2) do
230
244
  render_unification 'ns1|*.foo', 'ns2|a {@extend .foo}'
231
245
  end
232
246
 
247
+ assert_unification 'ns|*.foo', '*|a {@extend .foo}', 'ns|*.foo, ns|a'
233
248
  assert_unification 'ns|*.foo', 'ns|a {@extend .foo}', 'ns|*.foo, ns|a'
234
249
  end
235
250
 
236
251
  def test_element_unification_with_namespaceless_element_target
252
+ assert_extend_doesnt_match('ns|a', '.foo', :failed_to_unify, 2) do
253
+ render_unification 'a.foo', 'ns|a {@extend .foo}'
254
+ end
255
+
256
+ assert_extend_doesnt_match('h1', '.foo', :failed_to_unify, 2) do
257
+ render_unification 'a.foo', 'h1 {@extend .foo}'
258
+ end
259
+
237
260
  assert_unification 'a.foo', 'a {@extend .foo}', 'a'
238
261
  assert_unification 'a.foo', '*|a {@extend .foo}', 'a'
239
262
  assert_unification '*|a.foo', 'a {@extend .foo}', '*|a.foo, a'
240
263
  assert_unification '*|a.foo', '*|a {@extend .foo}', '*|a'
241
- assert_unification 'a.foo', 'ns|a {@extend .foo}', 'a.foo, ns|a'
242
264
  assert_unification '*|a.foo', 'ns|a {@extend .foo}', '*|a.foo, ns|a'
243
-
244
- assert_extend_doesnt_match('h1', '.foo', :failed_to_unify, 2) do
245
- render_unification 'a.foo', 'h1 {@extend .foo}'
246
- end
247
265
  end
248
266
 
249
267
  def test_element_unification_with_namespaced_element_target
250
- assert_unification 'ns|a.foo', 'a {@extend .foo}', 'ns|a'
251
- assert_unification 'ns|a.foo', '*|a {@extend .foo}', 'ns|a'
268
+ assert_extend_doesnt_match('a', '.foo', :failed_to_unify, 2) do
269
+ render_unification 'ns|a.foo', 'a {@extend .foo}'
270
+ end
252
271
 
253
272
  assert_extend_doesnt_match('ns2|a', '.foo', :failed_to_unify, 2) do
254
273
  render_unification 'ns1|a.foo', 'ns2|a {@extend .foo}'
255
274
  end
256
275
 
276
+ assert_unification 'ns|a.foo', '*|a {@extend .foo}', 'ns|a'
257
277
  assert_unification 'ns|a.foo', 'ns|a {@extend .foo}', 'ns|a'
258
278
  end
259
279
 
@@ -310,9 +330,207 @@ SCSS
310
330
  end
311
331
 
312
332
  def test_negation_unification
313
- assert_unification ':not(.foo).baz', ':not(.bar) {@extend .baz}', ':not(.foo).baz, :not(.foo):not(.bar)'
314
- assert_unification ':not(.foo).baz', ':not(.foo) {@extend .baz}', ':not(.foo)'
315
- assert_unification ':not([a=b]).baz', ':not([a = b]) {@extend .baz}', ':not([a=b])'
333
+ assert_extends ':not(.foo).baz', ':not(.bar) {@extend .baz}', ':not(.foo).baz, :not(.foo):not(.bar)'
334
+ # Unifying to :not(.foo) here would reduce the specificity of the original selector.
335
+ assert_extends ':not(.foo).baz', ':not(.foo) {@extend .baz}', ':not(.foo).baz, :not(.foo)'
336
+ end
337
+
338
+ def test_prefixed_pseudoclass_unification
339
+ assert_unification(
340
+ ':nth-child(2n+1 of .foo).baz',
341
+ ':nth-child(2n of .foo) {@extend .baz}',
342
+ ':nth-child(2n+1 of .foo).baz, :nth-child(2n+1 of .foo):nth-child(2n of .foo)')
343
+
344
+ assert_unification(
345
+ ':nth-child(2n+1 of .foo).baz',
346
+ ':nth-child(2n+1 of .bar) {@extend .baz}',
347
+ ':nth-child(2n+1 of .foo).baz, :nth-child(2n+1 of .foo):nth-child(2n+1 of .bar)')
348
+
349
+ assert_unification(
350
+ ':nth-child(2n+1 of .foo).baz',
351
+ ':nth-child(2n+1 of .foo) {@extend .baz}',
352
+ ':nth-child(2n+1 of .foo)')
353
+ end
354
+
355
+ def test_extend_into_not
356
+ assert_extends(':not(.foo)', '.x {@extend .foo}', ':not(.foo):not(.x)')
357
+ assert_extends(':not(.foo.bar)', '.x {@extend .bar}', ':not(.foo.bar):not(.foo.x)')
358
+ assert_extends(
359
+ ':not(.foo.bar, .baz.bar)',
360
+ '.x {@extend .bar}',
361
+ ':not(.foo.bar, .foo.x, .baz.bar, .baz.x)')
362
+ end
363
+
364
+ def test_extend_into_mergeable_pseudoclasses
365
+ assert_extends(':matches(.foo)', '.x {@extend .foo}', ':matches(.foo, .x)')
366
+ assert_extends(':matches(.foo.bar)', '.x {@extend .bar}', ':matches(.foo.bar, .foo.x)')
367
+ assert_extends(
368
+ ':matches(.foo.bar, .baz.bar)',
369
+ '.x {@extend .bar}',
370
+ ':matches(.foo.bar, .foo.x, .baz.bar, .baz.x)')
371
+
372
+ assert_extends(':-moz-any(.foo)', '.x {@extend .foo}', ':-moz-any(.foo, .x)')
373
+ assert_extends(':current(.foo)', '.x {@extend .foo}', ':current(.foo, .x)')
374
+ assert_extends(':has(.foo)', '.x {@extend .foo}', ':has(.foo, .x)')
375
+ assert_extends(':host(.foo)', '.x {@extend .foo}', ':host(.foo, .x)')
376
+ assert_extends(':host-context(.foo)', '.x {@extend .foo}', ':host-context(.foo, .x)')
377
+ assert_extends(':nth-child(n of .foo)', '.x {@extend .foo}', ':nth-child(n of .foo, .x)')
378
+ assert_extends(
379
+ ':nth-last-child(n of .foo)',
380
+ '.x {@extend .foo}',
381
+ ':nth-last-child(n of .foo, .x)')
382
+ end
383
+
384
+ def test_complex_extend_into_pseudoclass
385
+ # Unlike other selectors, we don't allow complex selectors to be
386
+ # added to `:not` if they weren't there before. At time of
387
+ # writing, most browsers don't support that and will throw away
388
+ # the entire selector if it exists.
389
+ #assert_extends(':not(.bar)', '.x .y {@extend .bar}', ':not(.bar)')
390
+
391
+ # If the `:not()` already has a complex selector, we won't break
392
+ # anything by adding a new one.
393
+ assert_extends(':not(.baz .bar)', '.x .y {@extend .bar}',
394
+ ':not(.baz .bar):not(.baz .x .y):not(.x .baz .y)')
395
+
396
+ # If the `:not()` would only contain complex selectors, there's no
397
+ # harm in letting it continue to exist.
398
+ assert_extends(':not(%bar)', '.x .y {@extend %bar}', ':not(.x .y)')
399
+
400
+ assert_extends(':matches(.bar)', '.x .y {@extend .bar}', ':matches(.bar, .x .y)')
401
+ assert_extends(':current(.bar)', '.x .y {@extend .bar}', ':current(.bar, .x .y)')
402
+ assert_extends(':has(.bar)', '.x .y {@extend .bar}', ':has(.bar, .x .y)')
403
+ assert_extends(':host(.bar)', '.x .y {@extend .bar}', ':host(.bar, .x .y)')
404
+ assert_extends(':host-context(.bar)', '.x .y {@extend .bar}', ':host-context(.bar, .x .y)')
405
+ assert_extends(
406
+ ':-moz-any(.bar)',
407
+ '.x .y {@extend .bar}',
408
+ ':-moz-any(.bar, .x .y)')
409
+ assert_extends(
410
+ ':nth-child(n of .bar)',
411
+ '.x .y {@extend .bar}',
412
+ ':nth-child(n of .bar, .x .y)')
413
+ assert_extends(
414
+ ':nth-last-child(n of .bar)',
415
+ '.x .y {@extend .bar}',
416
+ ':nth-last-child(n of .bar, .x .y)')
417
+ end
418
+
419
+ def test_extend_over_selector_pseudoclass
420
+ assert_extends(':not(.foo)', '.x {@extend :not(.foo)}', ':not(.foo), .x')
421
+ assert_extends(
422
+ ':matches(.foo, .bar)',
423
+ '.x {@extend :matches(.foo, .bar)}',
424
+ ':matches(.foo, .bar), .x')
425
+ end
426
+
427
+ def test_matches_within_not
428
+ assert_extends(
429
+ ':not(.foo, .bar)',
430
+ ':matches(.x, .y) {@extend .foo}',
431
+ ':not(.foo, .x, .y, .bar)')
432
+ end
433
+
434
+ def test_pseudoclasses_merge
435
+ assert_extends(':matches(.foo)', ':matches(.bar) {@extend .foo}', ':matches(.foo, .bar)')
436
+ assert_extends(':-moz-any(.foo)', ':-moz-any(.bar) {@extend .foo}', ':-moz-any(.foo, .bar)')
437
+ assert_extends(':current(.foo)', ':current(.bar) {@extend .foo}', ':current(.foo, .bar)')
438
+ assert_extends(
439
+ ':nth-child(n of .foo)',
440
+ ':nth-child(n of .bar) {@extend .foo}',
441
+ ':nth-child(n of .foo, .bar)')
442
+ assert_extends(
443
+ ':nth-last-child(n of .foo)',
444
+ ':nth-last-child(n of .bar) {@extend .foo}',
445
+ ':nth-last-child(n of .foo, .bar)')
446
+ end
447
+
448
+ def test_nesting_pseudoclasses_merge
449
+ assert_extends(':has(.foo)', ':has(.bar) {@extend .foo}', ':has(.foo, :has(.bar))')
450
+ assert_extends(':host(.foo)', ':host(.bar) {@extend .foo}', ':host(.foo, :host(.bar))')
451
+ assert_extends(
452
+ ':host-context(.foo)',
453
+ ':host-context(.bar) {@extend .foo}',
454
+ ':host-context(.foo, :host-context(.bar))')
455
+ end
456
+
457
+ def test_not_unifies_with_unique_values
458
+ assert_unification('foo', ':not(bar) {@extend foo}', ':not(bar)')
459
+ assert_unification('#foo', ':not(#bar) {@extend #foo}', ':not(#bar)')
460
+ end
461
+
462
+ def test_not_adds_no_specificity
463
+ assert_specificity_equals(':not(.foo)', '.foo')
464
+ end
465
+
466
+ def test_matches_has_a_specificity_range
467
+ # `:matches(.foo, #bar)` has minimum specificity equal to that of `.foo`,
468
+ # which means `:matches(.foo, #bar) .a` can have less specificity than
469
+ # `#b.a`. Thus the selector generated by `#b.a` should be preserved.
470
+ assert_equal <<CSS, render(<<SCSS)
471
+ :matches(.foo, #bar) .a, :matches(.foo, #bar) #b.a {
472
+ a: b; }
473
+ CSS
474
+ :matches(.foo, #bar) %x {a: b}
475
+ .a {@extend %x}
476
+ #b.a {@extend %x}
477
+ SCSS
478
+
479
+ # `:matches(.foo, #bar)` has maximum specificity equal to that of `#bar`,
480
+ # which means `:matches(.foo, #bar).b` can have greater specificity than `.a
481
+ # .b`. Thus the selector generated by `:matches(.foo, #bar).b` should be
482
+ # preserved.
483
+ assert_equal <<CSS, render(<<SCSS)
484
+ .a .b, .a .b:matches(.foo, #bar) {
485
+ a: b; }
486
+ CSS
487
+ .a %x {a: b}
488
+ .b {@extend %x}
489
+ .b:matches(.foo, #bar) {@extend %x}
490
+ SCSS
491
+ end
492
+
493
+ def test_extend_into_not_and_normal_extend
494
+ assert_equal <<CSS, render(<<SCSS)
495
+ .x:not(.y):not(.bar), .foo:not(.y):not(.bar) {
496
+ a: b; }
497
+ CSS
498
+ .x:not(.y) {a: b}
499
+ .foo {@extend .x}
500
+ .bar {@extend .y}
501
+ SCSS
502
+ end
503
+
504
+ def test_extend_into_matches_and_normal_extend
505
+ assert_equal <<CSS, render(<<SCSS)
506
+ .x:matches(.y, .bar), .foo:matches(.y, .bar) {
507
+ a: b; }
508
+ CSS
509
+ .x:matches(.y) {a: b}
510
+ .foo {@extend .x}
511
+ .bar {@extend .y}
512
+ SCSS
513
+ end
514
+
515
+ def test_multilayer_pseudoclass_extend
516
+ assert_equal <<CSS, render(<<SCSS)
517
+ :matches(.x, .foo, .bar) {
518
+ a: b; }
519
+ CSS
520
+ :matches(.x) {a: b}
521
+ .foo {@extend .x}
522
+ .bar {@extend .foo}
523
+ SCSS
524
+ end
525
+
526
+ def test_root_only_allowed_at_root
527
+ assert_extends(':root .foo', '.bar .baz {@extend .foo}',
528
+ ':root .foo, :root .bar .baz')
529
+ assert_extends('.foo:root .bar', '.baz:root .bang {@extend .bar}',
530
+ '.foo:root .bar, .baz.foo:root .bang')
531
+ assert_extends('html:root .bar', 'xml:root .bang {@extend .bar}', 'html:root .bar')
532
+ assert_extends('.foo:root > .bar .x', '.baz:root .bang .y {@extend .x}',
533
+ '.foo:root > .bar .x, .baz.foo:root > .bar .bang .y')
316
534
  end
317
535
 
318
536
  def test_comma_extendee
@@ -340,24 +558,42 @@ CSS
340
558
  SCSS
341
559
  end
342
560
 
561
+ def test_nested_pseudo_selectors
562
+ assert_equal <<CSS, render(<<SCSS)
563
+ .foo .bar:not(.baz), .bang .bar:not(.baz) {
564
+ a: b; }
565
+ CSS
566
+ .foo {
567
+ .bar:not(.baz) {a: b}
568
+ }
569
+ .bang {@extend .foo}
570
+ SCSS
571
+ end
572
+
343
573
  ## Long Extendees
344
574
 
345
575
  def test_long_extendee
346
- assert_extends '.foo.bar', '.baz {@extend .foo.bar}', '.foo.bar, .baz'
576
+ assert_warning(<<WARNING) {assert_extends '.foo.bar', '.baz {@extend .foo.bar}', '.foo.bar, .baz'}
577
+ DEPRECATION WARNING on line 2 of test_long_extendee_inline.scss:
578
+ Extending a compound selector, .foo.bar, is deprecated and will not be supported in a future release.
579
+ See https://github.com/sass/sass/issues/1599 for details.
580
+ WARNING
347
581
  end
348
582
 
349
583
  def test_long_extendee_requires_all_selectors
350
- assert_extend_doesnt_match('.baz', '.foo.bar', :not_found, 2) do
351
- render_extends '.foo', '.baz {@extend .foo.bar}'
584
+ silence_warnings do
585
+ assert_extend_doesnt_match('.baz', '.foo.bar', :not_found, 2) do
586
+ render_extends '.foo', '.baz {@extend .foo.bar}'
587
+ end
352
588
  end
353
589
  end
354
590
 
355
591
  def test_long_extendee_matches_supersets
356
- assert_extends '.foo.bar.bap', '.baz {@extend .foo.bar}', '.foo.bar.bap, .bap.baz'
592
+ silence_warnings {assert_extends '.foo.bar.bap', '.baz {@extend .foo.bar}', '.foo.bar.bap, .bap.baz'}
357
593
  end
358
594
 
359
595
  def test_long_extendee_runs_unification
360
- assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo.bar}', 'ns|*.foo.bar, ns|a.baz'
596
+ silence_warnings {assert_extends 'ns|*.foo.bar', '*|a.baz {@extend .foo.bar}', 'ns|*.foo.bar, ns|a.baz'}
361
597
  end
362
598
 
363
599
  ## Long Extenders
@@ -367,7 +603,7 @@ SCSS
367
603
  end
368
604
 
369
605
  def test_long_extender_runs_unification
370
- assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo}', 'ns|*.foo.bar, ns|a.bar.baz'
606
+ assert_extends 'ns|*.foo.bar', '*|a.baz {@extend .foo}', 'ns|*.foo.bar, ns|a.bar.baz'
371
607
  end
372
608
 
373
609
  def test_long_extender_aborts_unification
@@ -453,9 +689,9 @@ SCSS
453
689
  end
454
690
 
455
691
  def test_nested_extender_doesnt_find_common_selectors_around_reference_selector
456
- assert_extends 'a /for/ b c .c1', 'a c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b a c .c2, a a /for/ b c .c2'
457
- assert_extends 'a /for/ b c .c1', 'a b .c2 {@extend .c1}', 'a /for/ b c .c1, a a /for/ b c .c2'
458
- assert_extends 'a /for/ b c .c1', 'b c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b c .c2'
692
+ silence_warnings {assert_extends 'a /for/ b c .c1', 'a c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b a c .c2, a a /for/ b c .c2'}
693
+ silence_warnings {assert_extends 'a /for/ b c .c1', 'a b .c2 {@extend .c1}', 'a /for/ b c .c1, a a /for/ b c .c2'}
694
+ silence_warnings {assert_extends 'a /for/ b c .c1', 'b c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b c .c2'}
459
695
  end
460
696
 
461
697
  def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
@@ -516,7 +752,7 @@ SCSS
516
752
  end
517
753
 
518
754
  def test_nested_extender_with_trailing_child_selector
519
- assert_raise(Sass::SyntaxError, "bar > can't extend: invalid selector") do
755
+ assert_raises(Sass::SyntaxError, "bar > can't extend: invalid selector") do
520
756
  render("bar > {@extend .baz}")
521
757
  end
522
758
  end
@@ -650,10 +886,10 @@ SCSS
650
886
 
651
887
  def test_basic_extend_loop
652
888
  assert_equal <<CSS, render(<<SCSS)
653
- .bar, .foo {
889
+ .foo, .bar {
654
890
  a: b; }
655
891
 
656
- .foo, .bar {
892
+ .bar, .foo {
657
893
  c: d; }
658
894
  CSS
659
895
  .foo {a: b; @extend .bar}
@@ -663,13 +899,13 @@ SCSS
663
899
 
664
900
  def test_three_level_extend_loop
665
901
  assert_equal <<CSS, render(<<SCSS)
666
- .baz, .bar, .foo {
902
+ .foo, .baz, .bar {
667
903
  a: b; }
668
904
 
669
- .foo, .baz, .bar {
905
+ .bar, .foo, .baz {
670
906
  c: d; }
671
907
 
672
- .bar, .foo, .baz {
908
+ .baz, .bar, .foo {
673
909
  e: f; }
674
910
  CSS
675
911
  .foo {a: b; @extend .bar}
@@ -692,6 +928,18 @@ CSS
692
928
  SCSS
693
929
  end
694
930
 
931
+ def test_cross_loop
932
+ # The first law of extend means the selector should stick around.
933
+ assert_equal <<CSS, render(<<SCSS)
934
+ .foo.bar, .foo, .bar {
935
+ a: b; }
936
+ CSS
937
+ .foo.bar {a: b}
938
+ .foo {@extend .bar}
939
+ .bar {@extend .foo}
940
+ SCSS
941
+ end
942
+
695
943
  def test_multiple_extender_merges_with_superset_selector
696
944
  assert_equal <<CSS, render(<<SCSS)
697
945
  a.bar.baz, a.foo {
@@ -830,6 +1078,17 @@ $foo: foo;
830
1078
  SCSS
831
1079
  end
832
1080
 
1081
+ def test_placeholder_in_selector_pseudoclass
1082
+ assert_equal <<CSS, render(<<SCSS)
1083
+ :matches(.bar, .baz) {
1084
+ color: blue; }
1085
+ CSS
1086
+ :matches(%foo) {color: blue}
1087
+ .bar {@extend %foo}
1088
+ .baz {@extend %foo}
1089
+ SCSS
1090
+ end
1091
+
833
1092
  def test_media_in_placeholder_selector
834
1093
  assert_equal <<CSS, render(<<SCSS)
835
1094
  .baz {
@@ -1005,7 +1264,7 @@ SCSS
1005
1264
  end
1006
1265
 
1007
1266
  def test_extend_with_subject_transfers_subject_to_extender
1008
- assert_equal(<<CSS, render(<<SCSS))
1267
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1009
1268
  foo bar! baz, foo .bip .bap! baz, .bip foo .bap! baz {
1010
1269
  a: b; }
1011
1270
  CSS
@@ -1013,7 +1272,7 @@ foo bar! baz {a: b}
1013
1272
  .bip .bap {@extend bar}
1014
1273
  SCSS
1015
1274
 
1016
- assert_equal(<<CSS, render(<<SCSS))
1275
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1017
1276
  foo.x bar.y! baz.z, foo.x .bip bar.bap! baz.z, .bip foo.x bar.bap! baz.z {
1018
1277
  a: b; }
1019
1278
  CSS
@@ -1023,7 +1282,7 @@ SCSS
1023
1282
  end
1024
1283
 
1025
1284
  def test_extend_with_subject_retains_subject_on_target
1026
- assert_equal(<<CSS, render(<<SCSS))
1285
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1027
1286
  .foo! .bar, .foo! .bip .bap, .bip .foo! .bap {
1028
1287
  a: b; }
1029
1288
  CSS
@@ -1033,7 +1292,7 @@ SCSS
1033
1292
  end
1034
1293
 
1035
1294
  def test_extend_with_subject_transfers_subject_to_target
1036
- assert_equal(<<CSS, render(<<SCSS))
1295
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1037
1296
  a.foo .bar, .bip a.bap! .bar {
1038
1297
  a: b; }
1039
1298
  CSS
@@ -1043,7 +1302,7 @@ SCSS
1043
1302
  end
1044
1303
 
1045
1304
  def test_extend_with_subject_retains_subject_on_extender
1046
- assert_equal(<<CSS, render(<<SCSS))
1305
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1047
1306
  .foo .bar, .foo .bip! .bap, .bip! .foo .bap {
1048
1307
  a: b; }
1049
1308
  CSS
@@ -1053,7 +1312,7 @@ SCSS
1053
1312
  end
1054
1313
 
1055
1314
  def test_extend_with_subject_fails_with_conflicting_subject
1056
- assert_equal(<<CSS, render(<<SCSS))
1315
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1057
1316
  x! .bar {
1058
1317
  a: b; }
1059
1318
  CSS
@@ -1115,6 +1374,16 @@ SCSS
1115
1374
 
1116
1375
  # Regression Tests
1117
1376
 
1377
+ def test_extend_with_middle_pseudo
1378
+ assert_equal(<<CSS, render(<<SCSS))
1379
+ .btn:active.focus, :active.focus:before {
1380
+ a: b; }
1381
+ CSS
1382
+ .btn:active.focus {a: b}
1383
+ :before {@extend .btn}
1384
+ SCSS
1385
+ end
1386
+
1118
1387
  def test_extend_parent_selector_suffix
1119
1388
  assert_equal <<CSS, render(<<SCSS)
1120
1389
  .a-b, .c {
@@ -1408,7 +1677,7 @@ Use "@extend #{target} !optional" if the extend should be able to fail.
1408
1677
  ERR
1409
1678
  end
1410
1679
 
1411
- def assert_unification(selector, extension, unified)
1680
+ def assert_unification(selector, extension, unified, nested = true)
1412
1681
  # Do some trickery so the first law of extend doesn't get in our way.
1413
1682
  assert_extends(
1414
1683
  "%-a #{selector}",
@@ -1416,6 +1685,22 @@ ERR
1416
1685
  unified.split(', ').map {|s| "-a #{s}"}.join(', '))
1417
1686
  end
1418
1687
 
1688
+ def assert_specificity_equals(sel1, sel2)
1689
+ assert_specificity_gte(sel1, sel2)
1690
+ assert_specificity_gte(sel2, sel1)
1691
+ end
1692
+
1693
+ def assert_specificity_gte(sel1, sel2)
1694
+ assert_equal <<CSS, render(<<SCSS)
1695
+ #{sel1} .a {
1696
+ a: b; }
1697
+ CSS
1698
+ #{sel1} %-a {a: b}
1699
+ .a {@extend %-a}
1700
+ #{sel2}.a {@extend %-a}
1701
+ SCSS
1702
+ end
1703
+
1419
1704
  def render_unification(selector, extension)
1420
1705
  render_extends(
1421
1706
  "%-a #{selector}",
@@ -1429,6 +1714,10 @@ ERR
1429
1714
  CSS
1430
1715
  end
1431
1716
 
1717
+ def assert_extends_to_nothing(selector, extension)
1718
+ assert_equal '', render_extends(selector, extension)
1719
+ end
1720
+
1432
1721
  def render_extends(selector, extension)
1433
1722
  render(<<SCSS)
1434
1723
  #{selector} {a: b}