Class: Sass::Tree::Visitors::Cssize

Inherits:
Base
  • Object
show all
Defined in:
lib/sass/tree/visitors/cssize.rb

Overview

A visitor for converting a static Sass tree into a static CSS tree.

Defined Under Namespace

Classes: Bubble, Extend

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

node_name, #visit_if

Constructor Details

#initializeCssize (protected)

Returns a new instance of Cssize.



16
17
18
19
# File 'lib/sass/tree/visitors/cssize.rb', line 16

def initialize
  @parents = []
  @extends = Sass::Util::SubsetMap.new
end

Class Method Details

.visit(root) ⇒ (Tree::Node, Sass::Util::SubsetMap)

Returns The resulting tree of static nodes and the extensions defined for this tree.

Parameters:

  • root (Tree::Node)

    The root node of the tree to visit.

Returns:



6
# File 'lib/sass/tree/visitors/cssize.rb', line 6

def self.visit(root); super; end

Instance Method Details

#parentTree::Node (protected)

Returns the immediate parent of the current node.

Returns:



12
13
14
# File 'lib/sass/tree/visitors/cssize.rb', line 12

def parent
  @parents.last
end

#visit(node) (protected)

If an exception is raised, this adds proper metadata to the backtrace.



22
23
24
25
26
27
# File 'lib/sass/tree/visitors/cssize.rb', line 22

def visit(node)
  super(node)
rescue Sass::SyntaxError => e
  e.modify_backtrace(:filename => node.filename, :line => node.line)
  raise e
end

#visit_atroot(node) (protected)



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/sass/tree/visitors/cssize.rb', line 189

def visit_atroot(node)
  # If there aren't any more directives or rules that this @at-root needs to
  # exclude, we can get rid of it and just evaluate the children.
  if @parents.none? {|n| node.exclude_node?(n)}
    results = visit_children_without_parent(node)
    results.each {|c| c.tabs += node.tabs if bubblable?(c)}
    if !results.empty? && bubblable?(results.last)
      results.last.group_end = node.group_end
    end
    return results
  end

  # If this @at-root excludes the immediate parent, return it as-is so that it
  # can be bubbled up by the parent node.
  return Bubble.new(node) if node.exclude_node?(parent)

  # Otherwise, duplicate the current parent and move it into the @at-root
  # node. As above, returning an @at-root node signals to the parent directive
  # that it should be bubbled upwards.
  bubble(node)
end

#visit_children(parent) (protected)

Keeps track of the current parent node.



30
31
32
33
34
35
# File 'lib/sass/tree/visitors/cssize.rb', line 30

def visit_children(parent)
  with_parent parent do
    parent.children = visit_children_without_parent(parent)
    parent
  end
end

#visit_children_without_parent(node) ⇒ Array<Sass::Tree::Node> (protected)

Like #visit_children, but doesn't set #parent.

Parameters:

Returns:

  • (Array<Sass::Tree::Node>)

    the flattened results of visiting all the children of node



42
43
44
# File 'lib/sass/tree/visitors/cssize.rb', line 42

def visit_children_without_parent(node)
  node.children.map {|c| visit(c)}.flatten
end

#visit_directive(node) (protected)

Bubbles a directive up through RuleNodes.



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/sass/tree/visitors/cssize.rb', line 236

def visit_directive(node)
  return node unless node.has_children
  return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)

  yield

  # Since we don't know if the mere presence of an unknown directive may be
  # important, we should keep an empty version around even if all the contents
  # are removed via @at-root. However, if the contents are just bubbled out,
  # we don't need to do so.
  directive_exists = node.children.any? do |child|
    next true unless child.is_a?(Bubble)
    next false unless child.node.is_a?(Sass::Tree::DirectiveNode)
    child.node.resolved_value == node.resolved_value
  end

  if directive_exists
    []
  else
    empty_node = node.dup
    empty_node.children = []
    [empty_node]
  end + debubble(node.children, node)
end

#visit_extend(node) (protected)

Registers an extension in the @extends subset map.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/sass/tree/visitors/cssize.rb', line 125

def visit_extend(node)
  node.resolved_selector.members.each do |seq|
    if seq.members.size > 1
      raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
    end

    sseq = seq.members.first
    if !sseq.is_a?(Sass::Selector::SimpleSequence)
      raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
    elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
      raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend parent selectors")
    end

    sel = sseq.members
    parent.resolved_rules.members.each do |member|
      unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
        raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
      end

      parent_directives = @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)}
      @extends[sel] = Extend.new(member, sel, node, parent_directives, :not_found)
    end
  end

  []
end

#visit_import(node) (protected)

Modifies exception backtraces to include the imported file.



153
154
155
156
157
158
159
# File 'lib/sass/tree/visitors/cssize.rb', line 153

def visit_import(node)
  visit_children_without_parent(node)
rescue Sass::SyntaxError => e
  e.modify_backtrace(:filename => node.children.first.filename)
  e.add_backtrace(:filename => node.filename, :line => node.line)
  raise e
end

#visit_media(node) (protected)

Bubbles the @media directive up through RuleNodes and merges it with other @media directives.



263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/sass/tree/visitors/cssize.rb', line 263

def visit_media(node)
  return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)
  return Bubble.new(node) if parent.is_a?(Sass::Tree::MediaNode)

  yield

  debubble(node.children, node) do |child|
    next child unless child.is_a?(Sass::Tree::MediaNode)
    # Copies of `node` can be bubbled, and we don't want to merge it with its
    # own query.
    next child if child.resolved_query == node.resolved_query
    next child if child.resolved_query = child.resolved_query.merge(node.resolved_query)
  end
end

#visit_prop(node) (protected)

Converts nested properties into flat properties and updates the indentation of the prop node based on the nesting level.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/sass/tree/visitors/cssize.rb', line 172

def visit_prop(node)
  if parent.is_a?(Sass::Tree::PropNode)
    node.resolved_name = "#{parent.resolved_name}-#{node.resolved_name}"
    node.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if node.style == :nested
  end

  yield

  result = node.children.dup
  if !node.resolved_value.empty? || node.children.empty?
    node.send(:check!)
    result.unshift(node)
  end

  result
end

#visit_root(node) ⇒ (Tree::Node, Sass::Util::SubsetMap) (protected)

In Ruby 1.8, ensures that there's only one @charset directive and that it's at the top of the document.

Returns:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/sass/tree/visitors/cssize.rb', line 64

def visit_root(node)
  yield

  if parent.nil?
    # In Ruby 1.9 we can make all @charset nodes invisible
    # and infer the final @charset from the encoding of the final string.
    if Sass::Util.ruby1_8?
      charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
      node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
      node.children.unshift charset if charset
    end

    imports_to_move = []
    import_limit = nil
    i = -1
    node.children.reject! do |n|
      i += 1
      if import_limit
        next false unless n.is_a?(Sass::Tree::CssImportNode)
        imports_to_move << n
        next true
      end

      if !n.is_a?(Sass::Tree::CommentNode) &&
          !n.is_a?(Sass::Tree::CharsetNode) &&
          !n.is_a?(Sass::Tree::CssImportNode)
        import_limit = i
      end

      false
    end

    if import_limit
      node.children = node.children[0...import_limit] + imports_to_move +
        node.children[import_limit..-1]
    end
  end

  return node, @extends
rescue Sass::SyntaxError => e
  e.sass_template ||= node.template
  raise e
end

#visit_rule(node) (protected)

Updates the indentation of the rule node based on the nesting level. The selectors were resolved in Perform.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/sass/tree/visitors/cssize.rb', line 216

def visit_rule(node)
  yield

  rules = node.children.select {|c| bubblable?(c)}
  props = node.children.reject {|c| bubblable?(c) || c.invisible?}

  unless props.empty?
    node.children = props
    rules.each {|r| r.tabs += 1} if node.style == :nested
    rules.unshift(node)
  end

  rules = debubble(rules)
  unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty? || !bubblable?(rules.last)
    rules.last.group_end = true
  end
  rules
end

#visit_supports(node) (protected)

Bubbles the @supports directive up through RuleNodes.



279
280
281
282
283
284
285
286
# File 'lib/sass/tree/visitors/cssize.rb', line 279

def visit_supports(node)
  return node unless node.has_children
  return bubble(node) if parent.is_a?(Sass::Tree::RuleNode)

  yield

  debubble(node.children, node)
end

#visit_trace(node) (protected)

Asserts that all the traced children are valid in their new location.



162
163
164
165
166
167
168
# File 'lib/sass/tree/visitors/cssize.rb', line 162

def visit_trace(node)
  visit_children_without_parent(node)
rescue Sass::SyntaxError => e
  e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
  e.add_backtrace(:filename => node.filename, :line => node.line)
  raise e
end

#with_parent(parent) { ... } ⇒ Object (protected)

Runs a block of code with the current parent node replaced with the given node.

Parameters:

  • parent (Tree::Node)

    The new parent for the duration of the block.

Yields:

  • A block in which the parent is set to parent.

Returns:

  • (Object)

    The return value of the block.



52
53
54
55
56
57
# File 'lib/sass/tree/visitors/cssize.rb', line 52

def with_parent(parent)
  @parents.push parent
  yield
ensure
  @parents.pop
end