sass 3.3.0.alpha.382 → 3.3.0.alpha.388

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.
data/REVISION CHANGED
@@ -1 +1 @@
1
- bdc463148ed78f460943d6872b3e45109fe894ed
1
+ 4c629cf45c1d7ea2712bd73d16dbc5e96deda2a3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0.alpha.382
1
+ 3.3.0.alpha.388
data/VERSION_DATE CHANGED
@@ -1 +1 @@
1
- 11 October 2013 22:12:53 GMT
1
+ 11 October 2013 22:21:38 GMT
data/lib/sass/engine.rb CHANGED
@@ -878,9 +878,17 @@ WARNING
878
878
  end
879
879
 
880
880
  def parse_at_root_directive(parent, line, root, value, offset)
881
- at_root_node = Sass::Tree::AtRootNode.new
882
- return at_root_node unless value
881
+ return Sass::Tree::AtRootNode.new unless value
883
882
 
883
+ if value.start_with?('(')
884
+ parser = Sass::SCSS::Parser.new(value,
885
+ @options[:filename], @options[:importer],
886
+ @line, to_parser_offset(@offset))
887
+ offset = line.offset + line.text.index('at-root').to_i - 1
888
+ return Tree::AtRootNode.new(parser.parse_at_root_query)
889
+ end
890
+
891
+ at_root_node = Tree::AtRootNode.new
884
892
  parsed = parse_interp(value, offset)
885
893
  rule_node = Tree::RuleNode.new(parsed, full_line_range(line))
886
894
 
@@ -57,6 +57,7 @@ module Sass
57
57
 
58
58
  attr_writer :caller
59
59
  attr_writer :content
60
+ attr_writer :selector
60
61
 
61
62
  # variable
62
63
  # Script::Value
@@ -100,26 +101,7 @@ module Sass
100
101
  # @return [Selector::CommaSequence?] The current selector, with any
101
102
  # nesting fully resolved.
102
103
  def selector
103
- return if @no_selector
104
- return @selector if @selector
105
- return @caller.selector if @caller
106
- return @parent.selector if @parent
107
- nil
108
- end
109
-
110
- def selector=(value)
111
- @selector = value
112
- @no_selector = false
113
- end
114
-
115
- # Mark this environment as having no selector information. Unlike setting
116
- # {#selector} to nil, this indicates that this environment should not
117
- # inherit its {#parent}'s or {#caller}'s selector.
118
- #
119
- # This will be overwritten if {#selector} is set.
120
- def no_selector!
121
- @selector = nil
122
- @no_selector = true
104
+ @selector || (@caller && @caller.selector) || (@parent && @parent.selector)
123
105
  end
124
106
 
125
107
  # The top-level Environment object.
@@ -1704,7 +1704,7 @@ module Sass::Script
1704
1704
  index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1705
1705
  new_list = list.to_a.dup
1706
1706
  new_list[index] = value
1707
- Sass::Script::Value::List.new(new_list, list.separator)
1707
+ list(new_list, list.separator)
1708
1708
  end
1709
1709
  declare :set_nth, [:list, :n, :value]
1710
1710
 
@@ -1892,7 +1892,7 @@ module Sass::Script
1892
1892
  # @raise [ArgumentError] if `$map` is not a map
1893
1893
  def map_get(map, key)
1894
1894
  assert_type map, :Map
1895
- to_h(map)[key] || Sass::Script::Value::Null.new
1895
+ to_h(map)[key] || null
1896
1896
  end
1897
1897
  declare :map_get, [:map, :key]
1898
1898
 
@@ -1916,7 +1916,7 @@ module Sass::Script
1916
1916
  def map_merge(map1, map2)
1917
1917
  assert_type map1, :Map
1918
1918
  assert_type map2, :Map
1919
- Sass::Script::Value::Map.new(to_h(map1).merge(to_h(map2)))
1919
+ map(to_h(map1).merge(to_h(map2)))
1920
1920
  end
1921
1921
  declare :map_get, [:map1, :map2]
1922
1922
 
@@ -1930,7 +1930,7 @@ module Sass::Script
1930
1930
  # @raise [ArgumentError] if `$map` is not a map
1931
1931
  def map_keys(map)
1932
1932
  assert_type map, :Map
1933
- Sass::Script::Value::List.new(to_h(map).keys, :comma)
1933
+ list(to_h(map).keys, :comma)
1934
1934
  end
1935
1935
  declare :map_keys, [:map]
1936
1936
 
@@ -1946,7 +1946,7 @@ module Sass::Script
1946
1946
  # @raise [ArgumentError] if `$map` is not a map
1947
1947
  def map_values(map)
1948
1948
  assert_type map, :Map
1949
- Sass::Script::Value::List.new(to_h(map).values, :comma)
1949
+ list(to_h(map).values, :comma)
1950
1950
  end
1951
1951
  declare :map_values, [:map]
1952
1952
 
@@ -1962,7 +1962,7 @@ module Sass::Script
1962
1962
  # @raise [ArgumentError] if `$map` is not a map
1963
1963
  def map_has_key(map, key)
1964
1964
  assert_type map, :Map
1965
- Sass::Script::Value::Bool.new(to_h(map).has_key?(key))
1965
+ bool(to_h(map).has_key?(key))
1966
1966
  end
1967
1967
  declare :map_has_key, [:map, :key]
1968
1968
 
@@ -1982,8 +1982,7 @@ module Sass::Script
1982
1982
  # @raise [ArgumentError] if `$args` isn't a variable argument list
1983
1983
  def keywords(args)
1984
1984
  assert_type args, :ArgList
1985
- Sass::Script::Value::Map.new(
1986
- Sass::Util.map_keys(args.keywords) {|k| Sass::Script::String.new(k)})
1985
+ map(Sass::Util.map_keys(args.keywords) {|k| Sass::Script::String.new(k)})
1987
1986
  end
1988
1987
  declare :keywords, [:args]
1989
1988
 
@@ -2078,7 +2077,7 @@ module Sass::Script
2078
2077
  # @overload counters($args...)
2079
2078
  # @return [String]
2080
2079
  def counters(*args)
2081
- Sass::Script::String.new("counters(#{args.map {|a| a.to_s(options)}.join(',')})")
2080
+ identifier("counters(#{args.map {|a| a.to_s(options)}.join(',')})")
2082
2081
  end
2083
2082
  declare :counters, [], :var_args => true
2084
2083
 
@@ -2096,7 +2095,7 @@ module Sass::Script
2096
2095
  # the current scope.
2097
2096
  def variable_exists(name)
2098
2097
  assert_type name, :String
2099
- Sass::Script::Value::Bool.new(environment.caller.var(name.value))
2098
+ bool(environment.caller.var(name.value))
2100
2099
  end
2101
2100
  declare :variable_exists, [:name]
2102
2101
 
@@ -2117,7 +2116,7 @@ module Sass::Script
2117
2116
  # the global scope.
2118
2117
  def global_variable_exists(name)
2119
2118
  assert_type name, :String
2120
- Sass::Script::Value::Bool.new(environment.global_env.var(name.value))
2119
+ bool(environment.global_env.var(name.value))
2121
2120
  end
2122
2121
  declare :global_variable_exists, [:name]
2123
2122
 
@@ -2135,7 +2134,7 @@ module Sass::Script
2135
2134
  assert_type name, :String
2136
2135
  exists = Sass::Script::Functions.callable?(name.value.tr("-", "_"))
2137
2136
  exists ||= environment.function(name.value)
2138
- Sass::Script::Value::Bool.new(exists)
2137
+ bool(exists)
2139
2138
  end
2140
2139
  declare :function_exists, [:name]
2141
2140
 
@@ -2151,7 +2150,7 @@ module Sass::Script
2151
2150
  # @return [Sass::Script::Bool] Whether the mixin is defined.
2152
2151
  def mixin_exists(name)
2153
2152
  assert_type name, :String
2154
- Sass::Script::Value::Bool.new(environment.mixin(name.value))
2153
+ bool(environment.mixin(name.value))
2155
2154
  end
2156
2155
  declare :mixin_exists, [:name]
2157
2156
 
@@ -88,6 +88,15 @@ module Sass::Script::Value
88
88
  Sass::Script::Value::List.new(elements, separator)
89
89
  end
90
90
 
91
+ # Construct a Sass map.
92
+ #
93
+ # @param hash [Hash<Sass::Script::Value::Base,
94
+ # Sass::Script::Value::Base>] A Ruby map to convert to a Sass map.
95
+ # @return [Sass::Script::Value::Map] The map.
96
+ def map(hash)
97
+ Map.new(hash)
98
+ end
99
+
91
100
  # Create a sass null value.
92
101
  #
93
102
  # @return [Sass::Script::Value::Null]
@@ -67,6 +67,18 @@ module Sass
67
67
  ql
68
68
  end
69
69
 
70
+ # Parses an at-root query.
71
+ #
72
+ # @return [Array<String, Sass::Script;:Tree::Node>] The interpolated query.
73
+ # @raise [Sass::SyntaxError] if there's a syntax error in the query,
74
+ # or if it doesn't take up the entire input string.
75
+ def parse_at_root_query
76
+ init_scanner!
77
+ query = at_root_query
78
+ expected("@at-root query list") unless @scanner.eos?
79
+ query
80
+ end
81
+
70
82
  # Parses a supports query condition.
71
83
  #
72
84
  # @return [Sass::Supports::Condition] The parsed condition
@@ -435,7 +447,7 @@ module Sass
435
447
  query
436
448
  end
437
449
 
438
- def media_expr
450
+ def query_expr
439
451
  interp = interpolation
440
452
  return interp if interp
441
453
  return unless tok(/\(/)
@@ -453,6 +465,11 @@ module Sass
453
465
  res
454
466
  end
455
467
 
468
+ # Aliases allow us to use different descriptions if the same
469
+ # expression fails in different contexts.
470
+ alias_method :media_expr, :query_expr
471
+ alias_method :at_root_query, :query_expr
472
+
456
473
  def charset_directive(start_pos)
457
474
  tok! STRING
458
475
  name = @scanner[1] || @scanner[2]
@@ -490,6 +507,10 @@ module Sass
490
507
  end
491
508
 
492
509
  def at_root_directive(start_pos)
510
+ if tok?(/\(/) && (expr = at_root_query)
511
+ return block(node(Sass::Tree::AtRootNode.new(expr), start_pos), :directive)
512
+ end
513
+
493
514
  at_root_node = node(Sass::Tree::AtRootNode.new, start_pos)
494
515
  rule_node = ruleset
495
516
  return block(at_root_node, :stylesheet) unless rule_node
@@ -497,6 +518,17 @@ module Sass
497
518
  at_root_node
498
519
  end
499
520
 
521
+ def at_root_directive_list
522
+ return unless (first = tok(IDENT))
523
+ arr = [first]
524
+ ss
525
+ while (e = tok(IDENT))
526
+ arr << e
527
+ ss
528
+ end
529
+ arr
530
+ end
531
+
500
532
  # http://www.w3.org/TR/css3-conditional/
501
533
  def supports_directive(name, start_pos)
502
534
  condition = expr!(:supports_condition)
@@ -1146,6 +1178,8 @@ MESSAGE
1146
1178
  :media_query => "media query (e.g. print, screen, print and screen)",
1147
1179
  :media_query_list => "media query (e.g. print, screen, print and screen)",
1148
1180
  :media_expr => "media expression (e.g. (min-device-width: 800px))",
1181
+ :at_root_query => "@at-root query (e.g. (without: media))",
1182
+ :at_root_directive_list => '* or identifier',
1149
1183
  :pseudo_arg => "expression (e.g. fr, 2n+1)",
1150
1184
  :interp_ident => "identifier",
1151
1185
  :interp_name => "identifier",
@@ -1160,9 +1194,13 @@ MESSAGE
1160
1194
  :supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
1161
1195
  }
1162
1196
 
1163
- TOK_NAMES = Sass::Util.to_hash(
1164
- Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
1165
- merge(IDENT => "identifier", /[;}]/ => '";"')
1197
+ TOK_NAMES = Sass::Util.to_hash(Sass::SCSS::RX.constants.map do |c|
1198
+ [Sass::SCSS::RX.const_get(c), c.downcase]
1199
+ end).merge(
1200
+ IDENT => "identifier",
1201
+ /[;}]/ => '";"',
1202
+ /\b(without|with)\b/ => '"with" or "without"'
1203
+ )
1166
1204
 
1167
1205
  def tok?(rx)
1168
1206
  @scanner.match?(rx)
@@ -24,6 +24,23 @@ module Sass
24
24
  seq
25
25
  end
26
26
 
27
+ # Parses a static at-root query.
28
+ #
29
+ # @return [(Symbol, Array<String>)] The type of the query
30
+ # (`:with` or `:without`) and the values that are being filtered.
31
+ # @raise [Sass::SyntaxError] if there's a syntax error in the query,
32
+ # or if it doesn't take up the entire input string.
33
+ def parse_static_at_root_query
34
+ init_scanner!
35
+ tok!(/\(/); ss
36
+ type = tok!(/\b(without|with)\b/).to_sym; ss
37
+ tok!(/:/); ss
38
+ directives = expr!(:at_root_directive_list); ss
39
+ tok!(/\)/)
40
+ expected("@at-root query list") unless @scanner.eos?
41
+ return type, directives
42
+ end
43
+
27
44
  private
28
45
 
29
46
  def moz_document_function
@@ -7,6 +7,73 @@ module Sass
7
7
  #
8
8
  # @see Sass::Tree
9
9
  class AtRootNode < Node
10
+ # The query for this node (e.g. `(without: media)`),
11
+ # interspersed with {Sass::Script::Tree::Node}s representing
12
+ # `#{}`-interpolation. Any adjacent strings will be merged
13
+ # together.
14
+ #
15
+ # This will be nil if the directive didn't have a query. In this
16
+ # case, {#resolved\_type} will automatically be set to
17
+ # `:without` and {#resolved\_rule} will automatically be set to `["rule"]`.
18
+ #
19
+ # @return [Array<String, Sass::Script::Tree::Node>]
20
+ attr_accessor :query
21
+
22
+ # The resolved type of this directive. `:with` or `:without`.
23
+ #
24
+ # @return [Symbol]
25
+ attr_accessor :resolved_type
26
+
27
+ # The resolved value of this directive -- a list of directives
28
+ # to either include or exclude.
29
+ #
30
+ # @return [Array<String>]
31
+ attr_accessor :resolved_value
32
+
33
+ # The number of additional tabs that the contents of this node
34
+ # should be indented.
35
+ #
36
+ # @return [Number]
37
+ attr_accessor :tabs
38
+
39
+ # Whether the last child of this node should be considered the
40
+ # end of a group.
41
+ #
42
+ # @return [Boolean]
43
+ attr_accessor :group_end
44
+
45
+ def initialize(query = nil)
46
+ super()
47
+ @query = Sass::Util.strip_string_array(Sass::Util.merge_adjacent_strings(query)) if query
48
+ @tabs = 0
49
+ end
50
+
51
+ # Returns whether or not the given directive is excluded by this
52
+ # node. `directive` may be "rule", which indicates whether
53
+ # normal CSS rules should be excluded.
54
+ #
55
+ # @param directive [String]
56
+ # @return [Boolean]
57
+ def exclude?(directive)
58
+ if resolved_type == :with
59
+ return false if resolved_value.include?('all')
60
+ !resolved_value.include?(directive)
61
+ else # resolved_type == :without
62
+ return true if resolved_value.include?('all')
63
+ resolved_value.include?(directive)
64
+ end
65
+ end
66
+
67
+ # Returns whether the given node is excluded by this node.
68
+ #
69
+ # @param node [Sass::Tree::Node]
70
+ # @return [Boolean]
71
+ def exclude_node?(node)
72
+ return exclude?(node.name.gsub(/^@/, '')) if node.is_a?(Sass::Tree::DirectiveNode)
73
+ exclude?(node.class.to_s.gsub(/.*::(.*)Node$/, '\1').downcase)
74
+ end
75
+
76
+ # @see Node#bubbles?
10
77
  def bubbles?
11
78
  true
12
79
  end
@@ -20,9 +20,16 @@ module Sass::Tree
20
20
  # @return [String]
21
21
  attr_accessor :resolved_value
22
22
 
23
+ # @see RuleNode#tabs
24
+ attr_accessor :tabs
25
+
26
+ # @see RuleNode#group_end
27
+ attr_accessor :group_end
28
+
23
29
  # @param value [Array<String, Sass::Script::Tree::Node>] See \{#value}
24
30
  def initialize(value)
25
31
  @value = value
32
+ @tabs = 0
26
33
  super()
27
34
  end
28
35
 
@@ -38,5 +45,9 @@ module Sass::Tree
38
45
  def name
39
46
  value.first.gsub(/ .*$/, '')
40
47
  end
48
+
49
+ def bubbles?
50
+ has_children
51
+ end
41
52
  end
42
53
  end
@@ -21,16 +21,9 @@ module Sass::Tree
21
21
  # @return [Sass::Media::QueryList]
22
22
  attr_accessor :resolved_query
23
23
 
24
- # @see RuleNode#tabs
25
- attr_accessor :tabs
26
-
27
- # @see RuleNode#group_end
28
- attr_accessor :group_end
29
-
30
24
  # @param query [Array<String, Sass::Script::Tree::Node>] See \{#query}
31
25
  def initialize(query)
32
26
  @query = query
33
- @tabs = 0
34
27
  super('')
35
28
  end
36
29
 
@@ -51,8 +44,5 @@ module Sass::Tree
51
44
  def invisible?
52
45
  children.all? {|c| c.invisible?}
53
46
  end
54
-
55
- # @see Node#bubbles?
56
- def bubbles?; true; end
57
47
  end
58
48
  end
@@ -1,8 +1,5 @@
1
1
  module Sass::Tree
2
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
3
  #
7
4
  # @see Sass::Tree
8
5
  class SupportsNode < DirectiveNode
@@ -16,17 +13,10 @@ module Sass::Tree
16
13
  # @return [Sass::Supports::Condition]
17
14
  attr_accessor :condition
18
15
 
19
- # @see RuleNode#tabs
20
- attr_accessor :tabs
21
-
22
- # @see RuleNode#group_end
23
- attr_accessor :group_end
24
-
25
16
  # @param condition [Sass::Supports::Condition] See \{#condition}
26
17
  def initialize(name, condition)
27
18
  @name = name
28
19
  @condition = condition
29
- @tabs = 0
30
20
  super('')
31
21
  end
32
22
 
@@ -44,8 +34,5 @@ module Sass::Tree
44
34
  def invisible?
45
35
  children.all? {|c| c.invisible?}
46
36
  end
47
-
48
- # @see Node#bubbles?
49
- def bubbles?; true; end
50
37
  end
51
38
  end
@@ -156,7 +156,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
156
156
  end
157
157
 
158
158
  def visit_media(node)
159
- "#{tab_str}@media #{media_interp_to_src(node.query)}#{yield}"
159
+ "#{tab_str}@media #{query_interp_to_src(node.query)}#{yield}"
160
160
  end
161
161
 
162
162
  def visit_supports(node)
@@ -265,7 +265,9 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
265
265
  end
266
266
 
267
267
  def visit_atroot(node)
268
- if node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode)
268
+ if node.query
269
+ "#{tab_str}@at-root #{query_interp_to_src(node.query)}#{yield}"
270
+ elsif node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode)
269
271
  rule = node.children.first
270
272
  "#{tab_str}@at-root #{selector_to_src(rule.rule)}#{visit_children(rule)}"
271
273
  else
@@ -283,8 +285,8 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
283
285
  end
284
286
 
285
287
  # Like interp_to_src, but removes the unnecessary `#{}` around the keys and
286
- # values in media expressions.
287
- def media_interp_to_src(interp)
288
+ # values in query expressions.
289
+ def query_interp_to_src(interp)
288
290
  Sass::Util.enum_with_index(interp).map do |r, i|
289
291
  next r if r.is_a?(String)
290
292
  before, after = interp[i - 1], interp[i + 1]
@@ -27,11 +27,20 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
27
27
  # Keeps track of the current parent node.
28
28
  def visit_children(parent)
29
29
  with_parent parent do
30
- parent.children = super.flatten
30
+ parent.children = visit_children_without_parent(parent)
31
31
  parent
32
32
  end
33
33
  end
34
34
 
35
+ # Like {#visit\_children}, but doesn't set {#parent}.
36
+ #
37
+ # @param node [Sass::Tree::Node]
38
+ # @return [Array<Sass::Tree::Node>] the flattened results of
39
+ # visiting all the children of `node`
40
+ def visit_children_without_parent(node)
41
+ node.children.map {|c| visit(c)}.flatten
42
+ end
43
+
35
44
  MERGEABLE_DIRECTIVES = [Sass::Tree::MediaNode]
36
45
 
37
46
  # Runs a block of code with the current parent node
@@ -138,8 +147,7 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
138
147
 
139
148
  # Modifies exception backtraces to include the imported file.
140
149
  def visit_import(node)
141
- # Don't use #visit_children to avoid adding the import node to the list of parents.
142
- node.children.map {|c| visit(c)}.flatten
150
+ visit_children_without_parent(node)
143
151
  rescue Sass::SyntaxError => e
144
152
  e.modify_backtrace(:filename => node.children.first.filename)
145
153
  e.add_backtrace(:filename => node.filename, :line => node.line)
@@ -150,28 +158,46 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
150
158
  # and merges it with other `@media` directives.
151
159
  def visit_media(node)
152
160
  yield unless bubble(node)
153
- media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
154
- node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
155
- media = media.select {|n| n.resolved_query = n.resolved_query.merge(node.resolved_query)}
156
- (node.children.empty? ? [] : [node]) + media
161
+
162
+ bubbled = node.children.select do |n|
163
+ n.is_a?(Sass::Tree::AtRootNode) || n.is_a?(Sass::Tree::MediaNode)
164
+ end
165
+ node.children -= bubbled
166
+
167
+ bubbled = bubbled.map do |n|
168
+ next visit(n) if n.is_a?(Sass::Tree::AtRootNode)
169
+ # Otherwise, n should be a MediaNode.
170
+ next [] unless n.resolved_query = n.resolved_query.merge(node.resolved_query)
171
+ n
172
+ end.flatten
173
+
174
+ (node.children.empty? ? [] : [node]) + bubbled
157
175
  end
158
176
 
159
177
  # Bubbles the `@supports` directive up through RuleNodes.
160
178
  def visit_supports(node)
161
- yield unless bubble(node)
162
- node
179
+ visit_directive(node) {yield}
163
180
  end
164
181
 
165
182
  # Asserts that all the traced children are valid in their new location.
166
183
  def visit_trace(node)
167
- # Don't use #visit_children to avoid adding the trace node to the list of parents.
168
- node.children.map {|c| visit(c)}.flatten
184
+ visit_children_without_parent(node)
169
185
  rescue Sass::SyntaxError => e
170
186
  e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
171
187
  e.add_backtrace(:filename => node.filename, :line => node.line)
172
188
  raise e
173
189
  end
174
190
 
191
+ # Bubbles a directive up through RuleNodes.
192
+ def visit_directive(node)
193
+ return yield unless node.has_children
194
+ yield unless (bubbled = bubble(node))
195
+ at_roots = node.children.select {|n| n.is_a?(Sass::Tree::AtRootNode)}
196
+ node.children -= at_roots
197
+ at_roots.map! {|n| visit(n)}.flatten
198
+ (bubbled && node.children.empty? ? [] : [node]) + at_roots
199
+ end
200
+
175
201
  # Converts nested properties into flat properties
176
202
  # and updates the indentation of the prop node based on the nesting level.
177
203
  def visit_prop(node)
@@ -199,6 +225,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
199
225
  rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles?}
200
226
  props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles? || c.invisible?}
201
227
 
228
+ rules.map {|c| c.is_a?(Sass::Tree::AtRootNode) ? visit(c) : c}.flatten
229
+
202
230
  unless props.empty?
203
231
  node.children = props
204
232
  rules.each {|r| r.tabs += 1} if node.style == :nested
@@ -211,7 +239,19 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
211
239
  end
212
240
 
213
241
  def visit_atroot(node)
214
- yield.children
242
+ if @parent_directives.any? {|n| node.exclude_node?(n)}
243
+ return node if node.exclude_node?(parent)
244
+
245
+ new_rule = parent.dup
246
+ new_rule.children = node.children
247
+ node.children = [new_rule]
248
+ return node
249
+ end
250
+
251
+ results = visit_children_without_parent(node)
252
+ results.each {|n| n.tabs += node.tabs}
253
+ results.last.group_end = node.group_end unless results.empty?
254
+ results
215
255
  end
216
256
 
217
257
  private
@@ -380,11 +380,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
380
380
  # Runs SassScript interpolation in the selector,
381
381
  # and then parses the result into a {Sass::Selector::CommaSequence}.
382
382
  def visit_rule(node)
383
- old_at_root, @at_root = @at_root, false
383
+ old_at_root_without_rule, @at_root_without_rule = @at_root_without_rule, false
384
384
  parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
385
385
  node.filename, node.options[:importer], node.line)
386
386
  node.parsed_rules ||= parser.parse_selector
387
- node.resolved_rules = node.parsed_rules.resolve_parent_refs(@environment.selector, !old_at_root)
387
+ node.resolved_rules = node.parsed_rules.resolve_parent_refs(
388
+ @environment.selector, !old_at_root_without_rule)
388
389
  node.stack_trace = @environment.stack.to_s if node.options[:trace_selectors]
389
390
  with_environment Sass::Environment.new(@environment, node.options) do
390
391
  @environment.selector = node.resolved_rules
@@ -392,16 +393,25 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
392
393
  end
393
394
  node
394
395
  ensure
395
- @at_root = old_at_root
396
+ @at_root_without_rule = old_at_root_without_rule
396
397
  end
397
398
 
398
399
  # Sets a variable that indicates that the first level of rule nodes
399
400
  # shouldn't include the parent selector by default.
400
401
  def visit_atroot(node)
401
- old_at_root, @at_root = @at_root, true
402
- yield.children
402
+ if node.query
403
+ parser = Sass::SCSS::StaticParser.new(run_interp(node.query),
404
+ node.filename, node.options[:importer], node.line)
405
+ node.resolved_type, node.resolved_value = parser.parse_static_at_root_query
406
+ else
407
+ node.resolved_type, node.resolved_value = :without, ['rule']
408
+ end
409
+
410
+ old_at_root_without_rule = @at_root_without_rule
411
+ @at_root_without_rule = true if node.exclude?('rule')
412
+ yield
403
413
  ensure
404
- @at_root = old_at_root
414
+ @at_root_without_rule = old_at_root_without_rule
405
415
  end
406
416
 
407
417
  # Loads the new variable value into the environment.
@@ -440,7 +450,6 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
440
450
  def visit_directive(node)
441
451
  node.resolved_value = run_interp(node.value)
442
452
  with_environment Sass::Environment.new(@environment) do
443
- @environment.no_selector!
444
453
  node.children = node.children.map {|c| visit(c)}.flatten
445
454
  node
446
455
  end
@@ -1726,6 +1726,34 @@ SASS
1726
1726
  SCSS
1727
1727
  end
1728
1728
 
1729
+ def test_at_root_without
1730
+ assert_scss_to_sass <<SASS, <<SCSS
1731
+ .foo
1732
+ @at-root (without: media rule)
1733
+ a: b
1734
+ SASS
1735
+ .foo {
1736
+ @at-root (without: media rule) {
1737
+ a: b;
1738
+ }
1739
+ }
1740
+ SCSS
1741
+ end
1742
+
1743
+ def test_at_root_with
1744
+ assert_scss_to_sass <<SASS, <<SCSS
1745
+ .foo
1746
+ @at-root (with: media rule)
1747
+ a: b
1748
+ SASS
1749
+ .foo {
1750
+ @at-root (with: media rule) {
1751
+ a: b;
1752
+ }
1753
+ }
1754
+ SCSS
1755
+ end
1756
+
1729
1757
  def test_function_var_kwargs_with_list
1730
1758
  assert_scss_to_sass <<SASS, <<SCSS
1731
1759
  @function foo($a: b, $c: d)
@@ -2476,6 +2476,19 @@ CSS
2476
2476
  SASS
2477
2477
  end
2478
2478
 
2479
+ def test_at_root_with_query
2480
+ assert_equal <<CSS, render(<<SASS)
2481
+ .foo .bar {
2482
+ a: b; }
2483
+ CSS
2484
+ .foo
2485
+ @media screen
2486
+ @at-root (without: media)
2487
+ .bar
2488
+ a: b
2489
+ SASS
2490
+ end
2491
+
2479
2492
  # Regression tests
2480
2493
 
2481
2494
  def test_list_separator_with_arg_list
@@ -939,7 +939,6 @@ SCSS
939
939
  @flooblehoof {
940
940
  .foo, .bar {
941
941
  a: b; } }
942
-
943
942
  @flooblehoof {}
944
943
  CSS
945
944
  @flooblehoof {.foo {a: b}}
@@ -566,7 +566,6 @@ SCSS
566
566
  assert_parses <<SCSS
567
567
  @foo bar {
568
568
  a: b; }
569
-
570
569
  @bar baz {
571
570
  c: d; }
572
571
  SCSS
@@ -586,7 +585,6 @@ SCSS
586
585
  assert_equal <<CSS, render(<<SCSS)
587
586
  @foo {
588
587
  a: b; }
589
-
590
588
  @bar {
591
589
  a: b; }
592
590
  CSS
@@ -563,6 +563,20 @@ foo bar {
563
563
  SCSS
564
564
  end
565
565
 
566
+ def test_unknown_directive_bubbling
567
+ assert_equal(<<CSS, render(<<SCSS, :style => :nested))
568
+ @fblthp {
569
+ .foo .bar {
570
+ a: b; } }
571
+ CSS
572
+ .foo {
573
+ @fblthp {
574
+ .bar {a: b}
575
+ }
576
+ }
577
+ SCSS
578
+ end
579
+
566
580
  ## Namespace Properties
567
581
 
568
582
  def test_namespace_properties
@@ -2153,21 +2167,6 @@ CSS
2153
2167
  SCSS
2154
2168
  end
2155
2169
 
2156
- def test_at_root_in_nested_unknown_directive
2157
- assert_equal <<CSS, render(<<SCSS)
2158
- .foo {
2159
- @fblthp {
2160
- .bar {
2161
- a: b; } } }
2162
- CSS
2163
- .foo {
2164
- @fblthp {
2165
- @at-root .bar {a: b}
2166
- }
2167
- }
2168
- SCSS
2169
- end
2170
-
2171
2170
  def test_at_root_with_parent_ref
2172
2171
  assert_equal <<CSS, render(<<SCSS)
2173
2172
  .foo {
@@ -2213,6 +2212,308 @@ CSS
2213
2212
  SCSS
2214
2213
  end
2215
2214
 
2215
+ ## @at-root (...)
2216
+
2217
+ def test_at_root_without_media
2218
+ assert_equal <<CSS, render(<<SCSS)
2219
+ .foo .bar {
2220
+ a: b; }
2221
+ CSS
2222
+ .foo {
2223
+ @media screen {
2224
+ @at-root (without: media) {
2225
+ .bar {
2226
+ a: b;
2227
+ }
2228
+ }
2229
+ }
2230
+ }
2231
+ SCSS
2232
+ end
2233
+
2234
+ def test_at_root_without_supports
2235
+ assert_equal <<CSS, render(<<SCSS)
2236
+ .foo .bar {
2237
+ a: b; }
2238
+ CSS
2239
+ .foo {
2240
+ @supports (foo: bar) {
2241
+ @at-root (without: supports) {
2242
+ .bar {
2243
+ a: b;
2244
+ }
2245
+ }
2246
+ }
2247
+ }
2248
+ SCSS
2249
+ end
2250
+
2251
+ def test_at_root_without_rule
2252
+ assert_equal <<CSS, render(<<SCSS)
2253
+ @media screen {
2254
+ .bar {
2255
+ a: b; } }
2256
+ CSS
2257
+ .foo {
2258
+ @media screen {
2259
+ @at-root (without: rule) {
2260
+ .bar {
2261
+ a: b;
2262
+ }
2263
+ }
2264
+ }
2265
+ }
2266
+ SCSS
2267
+ end
2268
+
2269
+ def test_at_root_without_unknown_directive
2270
+ assert_equal <<CSS, render(<<SCSS)
2271
+ .foo .bar {
2272
+ a: b; }
2273
+ CSS
2274
+ .foo {
2275
+ @fblthp {
2276
+ @at-root (without: fblthp) {
2277
+ .bar {
2278
+ a: b;
2279
+ }
2280
+ }
2281
+ }
2282
+ }
2283
+ SCSS
2284
+ end
2285
+
2286
+ def test_at_root_without_multiple
2287
+ assert_equal <<CSS, render(<<SCSS)
2288
+ @supports (foo: bar) {
2289
+ .bar {
2290
+ a: b; } }
2291
+ CSS
2292
+ .foo {
2293
+ @media screen {
2294
+ @supports (foo: bar) {
2295
+ @at-root (without: media rule) {
2296
+ .bar {
2297
+ a: b;
2298
+ }
2299
+ }
2300
+ }
2301
+ }
2302
+ }
2303
+ SCSS
2304
+ end
2305
+
2306
+ def test_at_root_without_all
2307
+ assert_equal <<CSS, render(<<SCSS)
2308
+ .bar {
2309
+ a: b; }
2310
+ CSS
2311
+ .foo {
2312
+ @supports (foo: bar) {
2313
+ @fblthp {
2314
+ @at-root (without: all) {
2315
+ .bar {
2316
+ a: b;
2317
+ }
2318
+ }
2319
+ }
2320
+ }
2321
+ }
2322
+ SCSS
2323
+ end
2324
+
2325
+ def test_at_root_with_media
2326
+ assert_equal <<CSS, render(<<SCSS)
2327
+ @media screen {
2328
+ .bar {
2329
+ a: b; } }
2330
+ CSS
2331
+ .foo {
2332
+ @media screen {
2333
+ @fblthp {
2334
+ @supports (foo: bar) {
2335
+ @at-root (with: media) {
2336
+ .bar {
2337
+ a: b;
2338
+ }
2339
+ }
2340
+ }
2341
+ }
2342
+ }
2343
+ }
2344
+ SCSS
2345
+ end
2346
+
2347
+ def test_at_root_with_rule
2348
+ assert_equal <<CSS, render(<<SCSS)
2349
+ .foo .bar {
2350
+ a: b; }
2351
+ CSS
2352
+ .foo {
2353
+ @media screen {
2354
+ @fblthp {
2355
+ @supports (foo: bar) {
2356
+ @at-root (with: rule) {
2357
+ .bar {
2358
+ a: b;
2359
+ }
2360
+ }
2361
+ }
2362
+ }
2363
+ }
2364
+ }
2365
+ SCSS
2366
+ end
2367
+
2368
+ def test_at_root_with_supports
2369
+ assert_equal <<CSS, render(<<SCSS)
2370
+ @supports (foo: bar) {
2371
+ .bar {
2372
+ a: b; } }
2373
+ CSS
2374
+ .foo {
2375
+ @media screen {
2376
+ @fblthp {
2377
+ @supports (foo: bar) {
2378
+ @at-root (with: supports) {
2379
+ .bar {
2380
+ a: b;
2381
+ }
2382
+ }
2383
+ }
2384
+ }
2385
+ }
2386
+ }
2387
+ SCSS
2388
+ end
2389
+
2390
+ def test_at_root_with_unknown_directive
2391
+ assert_equal <<CSS, render(<<SCSS)
2392
+ @fblthp {
2393
+ .bar {
2394
+ a: b; } }
2395
+ CSS
2396
+ .foo {
2397
+ @media screen {
2398
+ @fblthp {
2399
+ @supports (foo: bar) {
2400
+ @at-root (with: fblthp) {
2401
+ .bar {
2402
+ a: b;
2403
+ }
2404
+ }
2405
+ }
2406
+ }
2407
+ }
2408
+ }
2409
+ SCSS
2410
+ end
2411
+
2412
+ def test_at_root_with_multiple
2413
+ assert_equal <<CSS, render(<<SCSS)
2414
+ @media screen {
2415
+ .foo .bar {
2416
+ a: b; } }
2417
+ CSS
2418
+ .foo {
2419
+ @media screen {
2420
+ @fblthp {
2421
+ @supports (foo: bar) {
2422
+ @at-root (with: media rule) {
2423
+ .bar {
2424
+ a: b;
2425
+ }
2426
+ }
2427
+ }
2428
+ }
2429
+ }
2430
+ }
2431
+ SCSS
2432
+ end
2433
+
2434
+ def test_at_root_with_all
2435
+ assert_equal <<CSS, render(<<SCSS)
2436
+ @media screen {
2437
+ @fblthp {
2438
+ @supports (foo: bar) {
2439
+ .foo .bar {
2440
+ a: b; } } } }
2441
+ CSS
2442
+ .foo {
2443
+ @media screen {
2444
+ @fblthp {
2445
+ @supports (foo: bar) {
2446
+ @at-root (with: all) {
2447
+ .bar {
2448
+ a: b;
2449
+ }
2450
+ }
2451
+ }
2452
+ }
2453
+ }
2454
+ }
2455
+ SCSS
2456
+ end
2457
+
2458
+ def test_at_root_dynamic_values
2459
+ assert_equal <<CSS, render(<<SCSS)
2460
+ @media screen {
2461
+ .bar {
2462
+ a: b; } }
2463
+ CSS
2464
+ $key: with;
2465
+ $value: media;
2466
+ .foo {
2467
+ @media screen {
2468
+ @at-root ($key: $value) {
2469
+ .bar {
2470
+ a: b;
2471
+ }
2472
+ }
2473
+ }
2474
+ }
2475
+ SCSS
2476
+ end
2477
+
2478
+ def test_at_root_interpolated_query
2479
+ assert_equal <<CSS, render(<<SCSS)
2480
+ @media screen {
2481
+ .bar {
2482
+ a: b; } }
2483
+ CSS
2484
+ .foo {
2485
+ @media screen {
2486
+ @at-root (\#{"with: media"}) {
2487
+ .bar {
2488
+ a: b;
2489
+ }
2490
+ }
2491
+ }
2492
+ }
2493
+ SCSS
2494
+ end
2495
+
2496
+ def test_at_root_plus_extend
2497
+ assert_equal <<CSS, render(<<SCSS)
2498
+ .foo .bar {
2499
+ a: b; }
2500
+ CSS
2501
+ %base {
2502
+ a: b;
2503
+ }
2504
+
2505
+ .foo {
2506
+ @media screen {
2507
+ @at-root (without: media) {
2508
+ .bar {
2509
+ @extend %base;
2510
+ }
2511
+ }
2512
+ }
2513
+ }
2514
+ SCSS
2515
+ end
2516
+
2216
2517
  ## Selector Script
2217
2518
 
2218
2519
  def test_selector_script
@@ -2602,57 +2903,6 @@ SCSS
2602
2903
 
2603
2904
  # Regression
2604
2905
 
2605
- def test_nested_unknown_directive
2606
- assert_equal(<<CSS, render(<<SCSS, :style => :nested))
2607
- .foo {
2608
- @fblthp {
2609
- .bar {
2610
- a: b; } } }
2611
- CSS
2612
- .foo {
2613
- @fblthp {
2614
- .bar {a: b}
2615
- }
2616
- }
2617
- SCSS
2618
-
2619
- assert_equal(<<CSS, render(<<SCSS, :style => :compressed))
2620
- .foo{@fblthp{.bar{a:b}}}
2621
- CSS
2622
- .foo {
2623
- @fblthp {
2624
- .bar {a: b}
2625
- }
2626
- }
2627
- SCSS
2628
-
2629
- assert_equal(<<CSS, render(<<SCSS, :style => :compact))
2630
- .foo { @fblthp { .bar { a: b; } } }
2631
- CSS
2632
- .foo {
2633
- @fblthp {
2634
- .bar {a: b}
2635
- }
2636
- }
2637
- SCSS
2638
-
2639
- assert_equal(<<CSS, render(<<SCSS, :style => :expanded))
2640
- .foo {
2641
- @fblthp {
2642
- .bar {
2643
- a: b;
2644
- }
2645
- }
2646
- }
2647
- CSS
2648
- .foo {
2649
- @fblthp {
2650
- .bar {a: b}
2651
- }
2652
- }
2653
- SCSS
2654
- end
2655
-
2656
2906
  def test_loud_comment_in_compressed_mode
2657
2907
  assert_equal(<<CSS, render(<<SCSS))
2658
2908
  /*! foo */
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- hash: 592302577
4
+ hash: 592302085
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 3
9
9
  - 0
10
10
  - alpha
11
- - 382
12
- version: 3.3.0.alpha.382
11
+ - 388
12
+ version: 3.3.0.alpha.388
13
13
  platform: ruby
14
14
  authors:
15
15
  - Nathan Weizenbaum