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)



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/sass/tree/visitors/cssize.rb', line 157

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.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/sass/tree/visitors/cssize.rb', line 212

def visit_directive(node)
  return node unless node.has_children
  if parent.is_a?(Sass::Tree::RuleNode)
    # @keyframes shouldn't include the rule nodes, so we manually create a
    # bubble that doesn't have the parent's contents for them.
    return node.normalized_name == '@keyframes' ? Bubble.new(node) : bubble(node)
  end

  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

  # We know empty @keyframes directives do nothing.
  if directive_exists || node.name == '@keyframes'
    []
  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.



114
115
116
117
118
# File 'lib/sass/tree/visitors/cssize.rb', line 114

def visit_extend(node)
  parent.resolved_rules.populate_extends(@extends, node.resolved_selector, node,
    @parents.select {|p| p.is_a?(Sass::Tree::DirectiveNode)})
  []
end

#visit_import(node) (protected)

Modifies exception backtraces to include the imported file.



121
122
123
124
125
126
127
# File 'lib/sass/tree/visitors/cssize.rb', line 121

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_keyframerule(node) (protected)



203
204
205
206
207
208
209
# File 'lib/sass/tree/visitors/cssize.rb', line 203

def visit_keyframerule(node)
  return node unless node.has_children

  yield

  debubble(node.children, node)
end

#visit_media(node) (protected)

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



244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/sass/tree/visitors/cssize.rb', line 244

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.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/sass/tree/visitors/cssize.rb', line 140

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)

Converts the entire document to CSS.

Returns:



63
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
# File 'lib/sass/tree/visitors/cssize.rb', line 63

def visit_root(node)
  yield

  if parent.nil?
    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.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/sass/tree/visitors/cssize.rb', line 184

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.



260
261
262
263
264
265
266
267
# File 'lib/sass/tree/visitors/cssize.rb', line 260

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.



130
131
132
133
134
135
136
# File 'lib/sass/tree/visitors/cssize.rb', line 130

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