sass 3.2.0.alpha.96 → 3.2.0.alpha.99

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.
@@ -38,5 +38,8 @@ module Sass::Tree
38
38
  def invisible?
39
39
  children.all? {|c| c.invisible?}
40
40
  end
41
+
42
+ # @see Node#bubbles?
43
+ def bubbles?; true; end
41
44
  end
42
45
  end
@@ -188,6 +188,13 @@ module Sass
188
188
  Sass::Tree::Visitors::DeepCopy.visit(self)
189
189
  end
190
190
 
191
+ # Whether or not this node bubbles up through RuleNodes.
192
+ #
193
+ # @return [Boolean]
194
+ def bubbles?
195
+ false
196
+ end
197
+
191
198
  protected
192
199
 
193
200
  # @see Sass::Shared.balance
@@ -0,0 +1,51 @@
1
+ module Sass::Tree
2
+ # A static node representing a `@supports` rule.
3
+ # `@supports` rules behave differently from other directives
4
+ # in that when they're nested within rules,
5
+ # they bubble up to top-level.
6
+ #
7
+ # @see Sass::Tree
8
+ class SupportsNode < DirectiveNode
9
+ # The name, which may include a browser prefix.
10
+ #
11
+ # @return [String]
12
+ attr_accessor :name
13
+
14
+ # The supports condition.
15
+ #
16
+ # @return [Sass::Supports::Condition]
17
+ attr_accessor :condition
18
+
19
+ # @see RuleNode#tabs
20
+ attr_accessor :tabs
21
+
22
+ # @see RuleNode#group_end
23
+ attr_accessor :group_end
24
+
25
+ # @param condition [Sass::Supports::Condition] See \{#condition}
26
+ def initialize(name, condition)
27
+ @name = name
28
+ @condition = condition
29
+ @tabs = 0
30
+ super('')
31
+ end
32
+
33
+ # @see DirectiveNode#value
34
+ def value; raise NotImplementedError; end
35
+
36
+ # @see DirectiveNode#resolved_value
37
+ def resolved_value
38
+ @resolved_value ||= "@#{name} #{condition.to_css}"
39
+ end
40
+
41
+ # True when the directive has no visible children.
42
+ #
43
+ # @return [Boolean]
44
+ def invisible?
45
+ children.all? {|c| c.invisible?}
46
+ end
47
+
48
+ # @see Node#bubbles?
49
+ def bubbles?; true; end
50
+ end
51
+ end
@@ -150,6 +150,10 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
150
150
  "#{tab_str}@media #{node.query.to_src(@options)}#{yield}"
151
151
  end
152
152
 
153
+ def visit_supports(node)
154
+ "#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}"
155
+ end
156
+
153
157
  def visit_cssimport(node)
154
158
  if node.uri.is_a?(Sass::Script::Node)
155
159
  str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
@@ -121,23 +121,19 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
121
121
  # Bubbles the `@media` directive up through RuleNodes
122
122
  # and merges it with other `@media` directives.
123
123
  def visit_media(node)
124
- if parent.is_a?(Sass::Tree::RuleNode)
125
- new_rule = parent.dup
126
- new_rule.children = node.children
127
- node.children = with_parent(node) {Array(visit(new_rule))}
128
- # If the last child is actually the end of the group,
129
- # the parent's cssize will set it properly
130
- node.children.last.group_end = false unless node.children.empty?
131
- else
132
- yield
133
- end
134
-
124
+ yield unless bubble(node)
135
125
  media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
136
126
  node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
137
127
  media = media.select {|n| n.query = n.query.merge(node.query)}
138
128
  (node.children.empty? ? [] : [node]) + media
139
129
  end
140
130
 
131
+ # Bubbles the `@supports` directive up through RuleNodes.
132
+ def visit_supports(node)
133
+ yield unless bubble(node)
134
+ node
135
+ end
136
+
141
137
  # Asserts that all the traced children are valid in their new location.
142
138
  def visit_trace(node)
143
139
  # Don't use #visit_children to avoid adding the trace node to the list of parents.
@@ -176,8 +172,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
176
172
 
177
173
  yield
178
174
 
179
- rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode)}
180
- props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode) || c.invisible?}
175
+ rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles?}
176
+ props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles? || c.invisible?}
181
177
 
182
178
  unless props.empty?
183
179
  node.children = props
@@ -189,4 +185,17 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
189
185
 
190
186
  rules
191
187
  end
188
+
189
+ private
190
+
191
+ def bubble(node)
192
+ return unless parent.is_a?(Sass::Tree::RuleNode)
193
+ new_rule = parent.dup
194
+ new_rule.children = node.children
195
+ node.children = with_parent(node) {Array(visit(new_rule))}
196
+ # If the last child is actually the end of the group,
197
+ # the parent's cssize will set it properly
198
+ node.children.last.group_end = false unless node.children.empty?
199
+ true
200
+ end
192
201
  end
@@ -94,4 +94,9 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
94
94
  node.query = node.query.deep_copy
95
95
  yield
96
96
  end
97
+
98
+ def visit_supports(node)
99
+ node.condition = node.condition.deep_copy
100
+ yield
101
+ end
97
102
  end
@@ -302,6 +302,12 @@ END
302
302
  yield
303
303
  end
304
304
 
305
+ def visit_supports(node)
306
+ node.condition = node.condition.deep_copy
307
+ node.condition.perform(@environment)
308
+ yield
309
+ end
310
+
305
311
  def visit_cssimport(node)
306
312
  node.resolved_uri = run_interp([node.uri])
307
313
  if node.query
@@ -104,4 +104,9 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
104
104
  node.query.options = @options
105
105
  yield
106
106
  end
107
+
108
+ def visit_supports(node)
109
+ node.condition.options = @options
110
+ yield
111
+ end
107
112
  end
@@ -113,6 +113,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
113
113
  str
114
114
  end
115
115
 
116
+ def visit_supports(node)
117
+ visit_media(node)
118
+ end
119
+
116
120
  def visit_cssimport(node)
117
121
  visit_directive(node)
118
122
  end
@@ -1184,6 +1184,28 @@ $val: 20;
1184
1184
  SCSS
1185
1185
  end
1186
1186
 
1187
+ def test_supports_with_expressions
1188
+ assert_renders <<SASS, <<SCSS
1189
+ $query: "(feature1: val)"
1190
+ $feature: feature2
1191
+ $val: val
1192
+
1193
+ @supports \#{$query} and ($feature: $val) or (not ($feature + 3: $val + 4))
1194
+ foo
1195
+ a: b
1196
+ SASS
1197
+ $query: "(feature1: val)";
1198
+ $feature: feature2;
1199
+ $val: val;
1200
+
1201
+ @supports \#{$query} and ($feature: $val) or (not ($feature + 3: $val + 4)) {
1202
+ foo {
1203
+ a: b;
1204
+ }
1205
+ }
1206
+ SCSS
1207
+ end
1208
+
1187
1209
  # Hacks
1188
1210
 
1189
1211
  def test_declaration_hacks
@@ -181,591 +181,115 @@ SCSS
181
181
  end
182
182
 
183
183
  def test_class_unification
184
- assert_equal <<CSS, render(<<SCSS)
185
- .foo.bar, .bar.baz {
186
- a: b; }
187
- CSS
188
- .foo.bar {a: b}
189
- .baz {@extend .foo}
190
- SCSS
191
-
192
- assert_equal <<CSS, render(<<SCSS)
193
- .baz {
194
- a: b; }
195
- CSS
196
- .foo.baz {a: b}
197
- .baz {@extend .foo}
198
- SCSS
184
+ assert_unification '.foo.bar', '.baz {@extend .foo}', '.foo.bar, .bar.baz'
185
+ assert_unification '.foo.baz', '.baz {@extend .foo}', '.baz'
199
186
  end
200
187
 
201
188
  def test_id_unification
202
- assert_equal <<CSS, render(<<SCSS)
203
- .foo.bar, .bar#baz {
204
- a: b; }
205
- CSS
206
- .foo.bar {a: b}
207
- #baz {@extend .foo}
208
- SCSS
209
-
210
- assert_equal <<CSS, render(<<SCSS)
211
- #baz {
212
- a: b; }
213
- CSS
214
- .foo#baz {a: b}
215
- #baz {@extend .foo}
216
- SCSS
217
-
218
- assert_equal <<CSS, render(<<SCSS)
219
- .foo#baz {
220
- a: b; }
221
- CSS
222
- .foo#baz {a: b}
223
- #bar {@extend .foo}
224
- SCSS
189
+ assert_unification '.foo.bar', '#baz {@extend .foo}', '.foo.bar, .bar#baz'
190
+ assert_unification '.foo#baz', '#baz {@extend .foo}', '#baz'
191
+ assert_unification '.foo#baz', '#bar {@extend .foo}', '.foo#baz'
225
192
  end
226
193
 
227
194
  def test_universal_unification_with_simple_target
228
- assert_equal <<CSS, render(<<SCSS)
229
- .foo, * {
230
- a: b; }
231
- CSS
232
- .foo {a: b}
233
- * {@extend .foo}
234
- SCSS
235
-
236
- assert_equal <<CSS, render(<<SCSS)
237
- .foo, *|* {
238
- a: b; }
239
- CSS
240
- .foo {a: b}
241
- *|* {@extend .foo}
242
- SCSS
243
-
244
- assert_equal <<CSS, render(<<SCSS)
245
- .bar {
246
- a: b; }
247
- CSS
248
- .foo.bar {a: b}
249
- * {@extend .foo}
250
- SCSS
251
-
252
- assert_equal <<CSS, render(<<SCSS)
253
- .bar {
254
- a: b; }
255
- CSS
256
- .foo.bar {a: b}
257
- *|* {@extend .foo}
258
- SCSS
259
-
260
- assert_equal <<CSS, render(<<SCSS)
261
- .foo.bar, ns|*.bar {
262
- a: b; }
263
- CSS
264
- .foo.bar {a: b}
265
- ns|* {@extend .foo}
266
- SCSS
195
+ assert_unification '.foo', '* {@extend .foo}', '.foo, *'
196
+ assert_unification '.foo', '*|* {@extend .foo}', '.foo, *|*'
197
+ assert_unification '.foo.bar', '* {@extend .foo}', '.bar'
198
+ assert_unification '.foo.bar', '*|* {@extend .foo}', '.bar'
199
+ assert_unification '.foo.bar', 'ns|* {@extend .foo}', '.foo.bar, ns|*.bar'
267
200
  end
268
201
 
269
202
  def test_universal_unification_with_namespaceless_universal_target
270
- assert_equal <<CSS, render(<<SCSS)
271
- * {
272
- a: b; }
273
- CSS
274
- *.foo {a: b}
275
- * {@extend .foo}
276
- SCSS
277
-
278
- assert_equal <<CSS, render(<<SCSS)
279
- * {
280
- a: b; }
281
- CSS
282
- *.foo {a: b}
283
- *|* {@extend .foo}
284
- SCSS
285
-
286
- assert_equal <<CSS, render(<<SCSS)
287
- *|*.foo, * {
288
- a: b; }
289
- CSS
290
- *|*.foo {a: b}
291
- * {@extend .foo}
292
- SCSS
293
-
294
- assert_equal <<CSS, render(<<SCSS)
295
- *|* {
296
- a: b; }
297
- CSS
298
- *|*.foo {a: b}
299
- *|* {@extend .foo}
300
- SCSS
301
-
302
- assert_equal <<CSS, render(<<SCSS)
303
- *.foo, ns|* {
304
- a: b; }
305
- CSS
306
- *.foo {a: b}
307
- ns|* {@extend .foo}
308
- SCSS
309
-
310
- assert_equal <<CSS, render(<<SCSS)
311
- *|*.foo, ns|* {
312
- a: b; }
313
- CSS
314
- *|*.foo {a: b}
315
- ns|* {@extend .foo}
316
- SCSS
203
+ assert_unification '*.foo', '* {@extend .foo}', '*'
204
+ assert_unification '*.foo', '*|* {@extend .foo}', '*'
205
+ assert_unification '*|*.foo', '* {@extend .foo}', '*|*.foo, *'
206
+ assert_unification '*|*.foo', '*|* {@extend .foo}', '*|*'
207
+ assert_unification '*.foo', 'ns|* {@extend .foo}', '*.foo, ns|*'
208
+ assert_unification '*|*.foo', 'ns|* {@extend .foo}', '*|*.foo, ns|*'
317
209
  end
318
210
 
319
211
  def test_universal_unification_with_namespaced_universal_target
320
- assert_equal <<CSS, render(<<SCSS)
321
- ns|* {
322
- a: b; }
323
- CSS
324
- ns|*.foo {a: b}
325
- * {@extend .foo}
326
- SCSS
327
-
328
- assert_equal <<CSS, render(<<SCSS)
329
- ns|* {
330
- a: b; }
331
- CSS
332
- ns|*.foo {a: b}
333
- *|* {@extend .foo}
334
- SCSS
335
-
336
- assert_equal <<CSS, render(<<SCSS)
337
- ns1|*.foo {
338
- a: b; }
339
- CSS
340
- ns1|*.foo {a: b}
341
- ns2|* {@extend .foo}
342
- SCSS
343
-
344
- assert_equal <<CSS, render(<<SCSS)
345
- ns|* {
346
- a: b; }
347
- CSS
348
- ns|*.foo {a: b}
349
- ns|* {@extend .foo}
350
- SCSS
212
+ assert_unification 'ns|*.foo', '* {@extend .foo}', 'ns|*'
213
+ assert_unification 'ns|*.foo', '*|* {@extend .foo}', 'ns|*'
214
+ assert_unification 'ns1|*.foo', 'ns2|* {@extend .foo}', 'ns1|*.foo'
215
+ assert_unification 'ns|*.foo', 'ns|* {@extend .foo}', 'ns|*'
351
216
  end
352
217
 
353
218
  def test_universal_unification_with_namespaceless_element_target
354
- assert_equal <<CSS, render(<<SCSS)
355
- a {
356
- a: b; }
357
- CSS
358
- a.foo {a: b}
359
- * {@extend .foo}
360
- SCSS
361
-
362
- assert_equal <<CSS, render(<<SCSS)
363
- a {
364
- a: b; }
365
- CSS
366
- a.foo {a: b}
367
- *|* {@extend .foo}
368
- SCSS
369
-
370
- assert_equal <<CSS, render(<<SCSS)
371
- *|a.foo, a {
372
- a: b; }
373
- CSS
374
- *|a.foo {a: b}
375
- * {@extend .foo}
376
- SCSS
377
-
378
- assert_equal <<CSS, render(<<SCSS)
379
- *|a {
380
- a: b; }
381
- CSS
382
- *|a.foo {a: b}
383
- *|* {@extend .foo}
384
- SCSS
385
-
386
- assert_equal <<CSS, render(<<SCSS)
387
- a.foo, ns|a {
388
- a: b; }
389
- CSS
390
- a.foo {a: b}
391
- ns|* {@extend .foo}
392
- SCSS
393
-
394
- assert_equal <<CSS, render(<<SCSS)
395
- *|a.foo, ns|a {
396
- a: b; }
397
- CSS
398
- *|a.foo {a: b}
399
- ns|* {@extend .foo}
400
- SCSS
219
+ assert_unification 'a.foo', '* {@extend .foo}', 'a'
220
+ assert_unification 'a.foo', '*|* {@extend .foo}', 'a'
221
+ assert_unification '*|a.foo', '* {@extend .foo}', '*|a.foo, a'
222
+ assert_unification '*|a.foo', '*|* {@extend .foo}', '*|a'
223
+ assert_unification 'a.foo', 'ns|* {@extend .foo}', 'a.foo, ns|a'
224
+ assert_unification '*|a.foo', 'ns|* {@extend .foo}', '*|a.foo, ns|a'
401
225
  end
402
226
 
403
227
  def test_universal_unification_with_namespaced_element_target
404
- assert_equal <<CSS, render(<<SCSS)
405
- ns|a {
406
- a: b; }
407
- CSS
408
- ns|a.foo {a: b}
409
- * {@extend .foo}
410
- SCSS
411
-
412
- assert_equal <<CSS, render(<<SCSS)
413
- ns|a {
414
- a: b; }
415
- CSS
416
- ns|a.foo {a: b}
417
- *|* {@extend .foo}
418
- SCSS
419
-
420
- assert_equal <<CSS, render(<<SCSS)
421
- ns1|a.foo {
422
- a: b; }
423
- CSS
424
- ns1|a.foo {a: b}
425
- ns2|* {@extend .foo}
426
- SCSS
427
-
428
- assert_equal <<CSS, render(<<SCSS)
429
- ns|a {
430
- a: b; }
431
- CSS
432
- ns|a.foo {a: b}
433
- ns|* {@extend .foo}
434
- SCSS
228
+ assert_unification 'ns|a.foo', '* {@extend .foo}', 'ns|a'
229
+ assert_unification 'ns|a.foo', '*|* {@extend .foo}', 'ns|a'
230
+ assert_unification 'ns1|a.foo', 'ns2|* {@extend .foo}', 'ns1|a.foo'
231
+ assert_unification 'ns|a.foo', 'ns|* {@extend .foo}', 'ns|a'
435
232
  end
436
233
 
437
234
  def test_element_unification_with_simple_target
438
- assert_equal <<CSS, render(<<SCSS)
439
- .foo, a {
440
- a: b; }
441
- CSS
442
- .foo {a: b}
443
- a {@extend .foo}
444
- SCSS
445
-
446
- assert_equal <<CSS, render(<<SCSS)
447
- .foo.bar, a.bar {
448
- a: b; }
449
- CSS
450
- .foo.bar {a: b}
451
- a {@extend .foo}
452
- SCSS
453
-
454
- assert_equal <<CSS, render(<<SCSS)
455
- .foo.bar, *|a.bar {
456
- a: b; }
457
- CSS
458
- .foo.bar {a: b}
459
- *|a {@extend .foo}
460
- SCSS
461
-
462
- assert_equal <<CSS, render(<<SCSS)
463
- .foo.bar, ns|a.bar {
464
- a: b; }
465
- CSS
466
- .foo.bar {a: b}
467
- ns|a {@extend .foo}
468
- SCSS
235
+ assert_unification '.foo', 'a {@extend .foo}', '.foo, a'
236
+ assert_unification '.foo.bar', 'a {@extend .foo}', '.foo.bar, a.bar'
237
+ assert_unification '.foo.bar', '*|a {@extend .foo}', '.foo.bar, *|a.bar'
238
+ assert_unification '.foo.bar', 'ns|a {@extend .foo}', '.foo.bar, ns|a.bar'
469
239
  end
470
240
 
471
241
  def test_element_unification_with_namespaceless_universal_target
472
- assert_equal <<CSS, render(<<SCSS)
473
- *.foo, a {
474
- a: b; }
475
- CSS
476
- *.foo {a: b}
477
- a {@extend .foo}
478
- SCSS
479
-
480
- assert_equal <<CSS, render(<<SCSS)
481
- *.foo, a {
482
- a: b; }
483
- CSS
484
- *.foo {a: b}
485
- *|a {@extend .foo}
486
- SCSS
487
-
488
- assert_equal <<CSS, render(<<SCSS)
489
- *|*.foo, a {
490
- a: b; }
491
- CSS
492
- *|*.foo {a: b}
493
- a {@extend .foo}
494
- SCSS
495
-
496
- assert_equal <<CSS, render(<<SCSS)
497
- *|*.foo, *|a {
498
- a: b; }
499
- CSS
500
- *|*.foo {a: b}
501
- *|a {@extend .foo}
502
- SCSS
503
-
504
- assert_equal <<CSS, render(<<SCSS)
505
- *.foo, ns|a {
506
- a: b; }
507
- CSS
508
- *.foo {a: b}
509
- ns|a {@extend .foo}
510
- SCSS
511
-
512
- assert_equal <<CSS, render(<<SCSS)
513
- *|*.foo, ns|a {
514
- a: b; }
515
- CSS
516
- *|*.foo {a: b}
517
- ns|a {@extend .foo}
518
- SCSS
242
+ assert_unification '*.foo', 'a {@extend .foo}', '*.foo, a'
243
+ assert_unification '*.foo', '*|a {@extend .foo}', '*.foo, a'
244
+ assert_unification '*|*.foo', 'a {@extend .foo}', '*|*.foo, a'
245
+ assert_unification '*|*.foo', '*|a {@extend .foo}', '*|*.foo, *|a'
246
+ assert_unification '*.foo', 'ns|a {@extend .foo}', '*.foo, ns|a'
247
+ assert_unification '*|*.foo', 'ns|a {@extend .foo}', '*|*.foo, ns|a'
519
248
  end
520
249
 
521
250
  def test_element_unification_with_namespaced_universal_target
522
- assert_equal <<CSS, render(<<SCSS)
523
- ns|*.foo, ns|a {
524
- a: b; }
525
- CSS
526
- ns|*.foo {a: b}
527
- a {@extend .foo}
528
- SCSS
529
-
530
- assert_equal <<CSS, render(<<SCSS)
531
- ns|*.foo, ns|a {
532
- a: b; }
533
- CSS
534
- ns|*.foo {a: b}
535
- *|a {@extend .foo}
536
- SCSS
537
-
538
- assert_equal <<CSS, render(<<SCSS)
539
- ns1|*.foo {
540
- a: b; }
541
- CSS
542
- ns1|*.foo {a: b}
543
- ns2|a {@extend .foo}
544
- SCSS
545
-
546
- assert_equal <<CSS, render(<<SCSS)
547
- ns|*.foo, ns|a {
548
- a: b; }
549
- CSS
550
- ns|*.foo {a: b}
551
- ns|a {@extend .foo}
552
- SCSS
251
+ assert_unification 'ns|*.foo', 'a {@extend .foo}', 'ns|*.foo, ns|a'
252
+ assert_unification 'ns|*.foo', '*|a {@extend .foo}', 'ns|*.foo, ns|a'
253
+ assert_unification 'ns1|*.foo', 'ns2|a {@extend .foo}', 'ns1|*.foo'
254
+ assert_unification 'ns|*.foo', 'ns|a {@extend .foo}', 'ns|*.foo, ns|a'
553
255
  end
554
256
 
555
257
  def test_element_unification_with_namespaceless_element_target
556
- assert_equal <<CSS, render(<<SCSS)
557
- a {
558
- a: b; }
559
- CSS
560
- a.foo {a: b}
561
- a {@extend .foo}
562
- SCSS
563
-
564
- assert_equal <<CSS, render(<<SCSS)
565
- a {
566
- a: b; }
567
- CSS
568
- a.foo {a: b}
569
- *|a {@extend .foo}
570
- SCSS
571
-
572
- assert_equal <<CSS, render(<<SCSS)
573
- *|a.foo, a {
574
- a: b; }
575
- CSS
576
- *|a.foo {a: b}
577
- a {@extend .foo}
578
- SCSS
579
-
580
- assert_equal <<CSS, render(<<SCSS)
581
- *|a {
582
- a: b; }
583
- CSS
584
- *|a.foo {a: b}
585
- *|a {@extend .foo}
586
- SCSS
587
-
588
- assert_equal <<CSS, render(<<SCSS)
589
- a.foo, ns|a {
590
- a: b; }
591
- CSS
592
- a.foo {a: b}
593
- ns|a {@extend .foo}
594
- SCSS
595
-
596
- assert_equal <<CSS, render(<<SCSS)
597
- *|a.foo, ns|a {
598
- a: b; }
599
- CSS
600
- *|a.foo {a: b}
601
- ns|a {@extend .foo}
602
- SCSS
603
-
604
- assert_equal <<CSS, render(<<SCSS)
605
- a.foo {
606
- a: b; }
607
- CSS
608
- a.foo {a: b}
609
- h1 {@extend .foo}
610
- SCSS
258
+ assert_unification 'a.foo', 'a {@extend .foo}', 'a'
259
+ assert_unification 'a.foo', '*|a {@extend .foo}', 'a'
260
+ assert_unification '*|a.foo', 'a {@extend .foo}', '*|a.foo, a'
261
+ assert_unification '*|a.foo', '*|a {@extend .foo}', '*|a'
262
+ assert_unification 'a.foo', 'ns|a {@extend .foo}', 'a.foo, ns|a'
263
+ assert_unification '*|a.foo', 'ns|a {@extend .foo}', '*|a.foo, ns|a'
264
+ assert_unification 'a.foo', 'h1 {@extend .foo}', 'a.foo'
611
265
  end
612
266
 
613
267
  def test_element_unification_with_namespaced_element_target
614
- assert_equal <<CSS, render(<<SCSS)
615
- ns|a {
616
- a: b; }
617
- CSS
618
- ns|a.foo {a: b}
619
- a {@extend .foo}
620
- SCSS
621
-
622
- assert_equal <<CSS, render(<<SCSS)
623
- ns|a {
624
- a: b; }
625
- CSS
626
- ns|a.foo {a: b}
627
- *|a {@extend .foo}
628
- SCSS
629
-
630
- assert_equal <<CSS, render(<<SCSS)
631
- ns1|a.foo {
632
- a: b; }
633
- CSS
634
- ns1|a.foo {a: b}
635
- ns2|a {@extend .foo}
636
- SCSS
637
-
638
- assert_equal <<CSS, render(<<SCSS)
639
- ns|a {
640
- a: b; }
641
- CSS
642
- ns|a.foo {a: b}
643
- ns|a {@extend .foo}
644
- SCSS
268
+ assert_unification 'ns|a.foo', 'a {@extend .foo}', 'ns|a'
269
+ assert_unification 'ns|a.foo', '*|a {@extend .foo}', 'ns|a'
270
+ assert_unification 'ns1|a.foo', 'ns2|a {@extend .foo}', 'ns1|a.foo'
271
+ assert_unification 'ns|a.foo', 'ns|a {@extend .foo}', 'ns|a'
645
272
  end
646
273
 
647
274
  def test_attribute_unification
648
- assert_equal <<CSS, render(<<SCSS)
649
- [foo=bar].baz, [foo=bar][foo=baz] {
650
- a: b; }
651
- CSS
652
- [foo=bar].baz {a: b}
653
- [foo=baz] {@extend .baz}
654
- SCSS
655
-
656
- assert_equal <<CSS, render(<<SCSS)
657
- [foo=bar].baz, [foo=bar][foo^=bar] {
658
- a: b; }
659
- CSS
660
- [foo=bar].baz {a: b}
661
- [foo^=bar] {@extend .baz}
662
- SCSS
663
-
664
- assert_equal <<CSS, render(<<SCSS)
665
- [foo=bar].baz, [foo=bar][foot=bar] {
666
- a: b; }
667
- CSS
668
- [foo=bar].baz {a: b}
669
- [foot=bar] {@extend .baz}
670
- SCSS
671
-
672
- assert_equal <<CSS, render(<<SCSS)
673
- [foo=bar].baz, [foo=bar][ns|foo=bar] {
674
- a: b; }
675
- CSS
676
- [foo=bar].baz {a: b}
677
- [ns|foo=bar] {@extend .baz}
678
- SCSS
679
-
680
- assert_equal <<CSS, render(<<SCSS)
681
- [foo=bar] {
682
- a: b; }
683
- CSS
684
- [foo=bar].baz {a: b}
685
- [foo=bar] {@extend .baz}
686
- SCSS
275
+ assert_unification '[foo=bar].baz', '[foo=baz] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo=baz]'
276
+ assert_unification '[foo=bar].baz', '[foo^=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo^=bar]'
277
+ assert_unification '[foo=bar].baz', '[foot=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foot=bar]'
278
+ assert_unification '[foo=bar].baz', '[ns|foo=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][ns|foo=bar]'
279
+ assert_unification '%-a [foo=bar].bar', '[foo=bar] {@extend .bar}', '-a [foo=bar]'
687
280
  end
688
281
 
689
282
  def test_pseudo_unification
690
- assert_equal <<CSS, render(<<SCSS)
691
- :foo.baz, :foo:foo(2n+1) {
692
- a: b; }
693
- CSS
694
- :foo.baz {a: b}
695
- :foo(2n+1) {@extend .baz}
696
- SCSS
697
-
698
- assert_equal <<CSS, render(<<SCSS)
699
- :foo.baz, :foo::foo {
700
- a: b; }
701
- CSS
702
- :foo.baz {a: b}
703
- ::foo {@extend .baz}
704
- SCSS
705
-
706
- assert_equal <<CSS, render(<<SCSS)
707
- ::foo.baz {
708
- a: b; }
709
- CSS
710
- ::foo.baz {a: b}
711
- ::bar {@extend .baz}
712
- SCSS
713
-
714
- assert_equal <<CSS, render(<<SCSS)
715
- ::foo.baz {
716
- a: b; }
717
- CSS
718
- ::foo.baz {a: b}
719
- ::foo(2n+1) {@extend .baz}
720
- SCSS
721
-
722
- assert_equal <<CSS, render(<<SCSS)
723
- ::foo {
724
- a: b; }
725
- CSS
726
- ::foo.baz {a: b}
727
- ::foo {@extend .baz}
728
- SCSS
729
-
730
- assert_equal <<CSS, render(<<SCSS)
731
- ::foo(2n+1) {
732
- a: b; }
733
- CSS
734
- ::foo(2n+1).baz {a: b}
735
- ::foo(2n+1) {@extend .baz}
736
- SCSS
737
-
738
- assert_equal <<CSS, render(<<SCSS)
739
- :foo.baz, :foo:bar {
740
- a: b; }
741
- CSS
742
- :foo.baz {a: b}
743
- :bar {@extend .baz}
744
- SCSS
745
-
746
- assert_equal <<CSS, render(<<SCSS)
747
- .baz:foo, :foo:after {
748
- a: b; }
749
- CSS
750
- .baz:foo {a: b}
751
- :after {@extend .baz}
752
- SCSS
753
-
754
- assert_equal <<CSS, render(<<SCSS)
755
- .baz:after, :foo:after {
756
- a: b; }
757
- CSS
758
- .baz:after {a: b}
759
- :foo {@extend .baz}
760
- SCSS
761
-
762
- assert_equal <<CSS, render(<<SCSS)
763
- :foo {
764
- a: b; }
765
- CSS
766
- :foo.baz {a: b}
767
- :foo {@extend .baz}
768
- SCSS
283
+ assert_unification ':foo.baz', ':foo(2n+1) {@extend .baz}', ':foo.baz, :foo:foo(2n+1)'
284
+ assert_unification ':foo.baz', '::foo {@extend .baz}', ':foo.baz, :foo::foo'
285
+ assert_unification '::foo.baz', '::bar {@extend .baz}', '::foo.baz'
286
+ assert_unification '::foo.baz', '::foo(2n+1) {@extend .baz}', '::foo.baz'
287
+ assert_unification '::foo.baz', '::foo {@extend .baz}', '::foo'
288
+ assert_unification '::foo(2n+1).baz', '::foo(2n+1) {@extend .baz}', '::foo(2n+1)'
289
+ assert_unification ':foo.baz', ':bar {@extend .baz}', ':foo.baz, :foo:bar'
290
+ assert_unification '.baz:foo', ':after {@extend .baz}', '.baz:foo, :foo:after'
291
+ assert_unification '.baz:after', ':foo {@extend .baz}', '.baz:after, :foo:after'
292
+ assert_unification ':foo.baz', ':foo {@extend .baz}', ':foo'
769
293
  end
770
294
 
771
295
  def test_pseudoelement_remains_at_end_of_selector
@@ -851,29 +375,9 @@ SCSS
851
375
  end
852
376
 
853
377
  def test_negation_unification
854
- assert_equal <<CSS, render(<<SCSS)
855
- :not(.foo).baz, :not(.foo):not(.bar) {
856
- a: b; }
857
- CSS
858
- :not(.foo).baz {a: b}
859
- :not(.bar) {@extend .baz}
860
- SCSS
861
-
862
- assert_equal <<CSS, render(<<SCSS)
863
- :not(.foo) {
864
- a: b; }
865
- CSS
866
- :not(.foo).baz {a: b}
867
- :not(.foo) {@extend .baz}
868
- SCSS
869
-
870
- assert_equal <<CSS, render(<<SCSS)
871
- :not([a=b]) {
872
- a: b; }
873
- CSS
874
- :not([a=b]).baz {a: b}
875
- :not([a = b]) {@extend .baz}
876
- SCSS
378
+ assert_unification ':not(.foo).baz', ':not(.bar) {@extend .baz}', ':not(.foo).baz, :not(.foo):not(.bar)'
379
+ assert_unification ':not(.foo).baz', ':not(.foo) {@extend .baz}', ':not(.foo)'
380
+ assert_unification ':not([a=b]).baz', ':not([a = b]) {@extend .baz}', ':not([a=b])'
877
381
  end
878
382
 
879
383
  def test_comma_extendee
@@ -2023,6 +1527,36 @@ d {@extend a}
2023
1527
  SCSS
2024
1528
  end
2025
1529
 
1530
+ def test_extend_redundancy_elimination_when_it_would_reduce_specificity
1531
+ assert_equal <<CSS, render(<<SCSS)
1532
+ a, a.foo {
1533
+ x: y; }
1534
+ CSS
1535
+ a {x: y}
1536
+ a.foo {@extend a}
1537
+ SCSS
1538
+ end
1539
+
1540
+ def test_extend_redundancy_elimination_when_it_would_preserve_specificity
1541
+ assert_equal <<CSS, render(<<SCSS)
1542
+ .bar a {
1543
+ x: y; }
1544
+ CSS
1545
+ .bar a {x: y}
1546
+ a.foo {@extend a}
1547
+ SCSS
1548
+ end
1549
+
1550
+ def test_extend_redundancy_elimination_never_eliminates_base_selector
1551
+ assert_equal <<CSS, render(<<SCSS)
1552
+ a.foo, .foo {
1553
+ x: y; }
1554
+ CSS
1555
+ a.foo {x: y}
1556
+ .foo {@extend a}
1557
+ SCSS
1558
+ end
1559
+
2026
1560
  def test_extend_cross_branch_redundancy_elimination
2027
1561
  assert_equal <<CSS, render(<<SCSS)
2028
1562
  a c d, b c a d {
@@ -2046,6 +1580,17 @@ SCSS
2046
1580
 
2047
1581
  private
2048
1582
 
1583
+ def assert_unification(selector, extension, unified)
1584
+ assert_equal <<CSS, render(<<SCSS)
1585
+ #{unified.split(', ').map {|s| "-a #{s}"}.join(', ')} {
1586
+ a: b; }
1587
+ CSS
1588
+ %-a #{selector} {a: b}
1589
+ #{extension}
1590
+ -a {@extend %-a}
1591
+ SCSS
1592
+ end
1593
+
2049
1594
  def render(sass, options = {})
2050
1595
  munge_filename options
2051
1596
  Sass::Engine.new(sass, {:syntax => :scss}.merge(options)).render