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

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