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: Extend

Constant Summary collapse

MERGEABLE_DIRECTIVES =
[Sass::Tree::MediaNode]

Constants inherited from Base

Base::NODE_NAME_RE

Instance Attribute Summary collapse

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.



14
15
16
17
# File 'lib/sass/tree/visitors/cssize.rb', line 14

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

Instance Attribute Details

#parentTree::Node (readonly, protected)

Returns the immediate parent of the current node.

Returns:



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

def parent
  @parent
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

#visit(node) (protected)

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



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

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

#visit_children(parent) (protected)

Keeps track of the current parent node.



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

def visit_children(parent)
  with_parent parent do
    parent.children = super.flatten
    parent
  end
end

#visit_extend(node) (protected)

Registers an extension in the @extends subset map.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/sass/tree/visitors/cssize.rb', line 113

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 |seq|
      if !seq.members.last.is_a?(Sass::Selector::SimpleSequence)
        raise Sass::SyntaxError.new("#{seq} can't extend: invalid selector")
      end

      @extends[sel] = Extend.new(seq, sel, node, @parent_directives.dup, :not_found)
    end
  end

  []
end

#visit_import(node) (protected)

Modifies exception backtraces to include the imported file.



140
141
142
143
144
145
146
147
# File 'lib/sass/tree/visitors/cssize.rb', line 140

def visit_import(node)
  # Don't use #visit_children to avoid adding the import node to the list of parents.
  node.children.map {|c| visit(c)}.flatten
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.



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

def visit_media(node)
  yield unless bubble(node)
  media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
  node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
  media = media.select {|n| n.resolved_query = n.resolved_query.merge(node.resolved_query)}
  (node.children.empty? ? [] : [node]) + media
end

#visit_prop(node) (protected)

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



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/sass/tree/visitors/cssize.rb', line 177

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
# 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 = Sass::Util.extract!(node.children) do |c|
      c.is_a?(Sass::Tree::DirectiveNode) && !c.is_a?(Sass::Tree::MediaNode) &&
        c.resolved_value =~ /^@import /i
    end
    charset_and_index = Sass::Util.ruby1_8? &&
      node.children.each_with_index.find {|c, _| c.is_a?(Sass::Tree::CharsetNode)}
    if charset_and_index
      index = charset_and_index.last
      node.children = node.children[0..index] + imports + node.children[index+1..-1]
    else
      node.children = imports + node.children
    end
  end

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

#visit_rule(node) (protected)

Resolves parent references and nested selectors, and updates the indentation of the rule node based on the nesting level.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/sass/tree/visitors/cssize.rb', line 196

def visit_rule(node)
  parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
  # It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
  node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)

  yield

  rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles?}
  props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles? || c.invisible?}

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

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

  rules
end

#visit_supports(node) (protected)

Bubbles the @supports directive up through RuleNodes.



160
161
162
163
# File 'lib/sass/tree/visitors/cssize.rb', line 160

def visit_supports(node)
  yield unless bubble(node)
  node
end

#visit_trace(node) (protected)

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



166
167
168
169
170
171
172
173
# File 'lib/sass/tree/visitors/cssize.rb', line 166

def visit_trace(node)
  # Don't use #visit_children to avoid adding the trace node to the list of parents.
  node.children.map {|c| visit(c)}.flatten
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.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/sass/tree/visitors/cssize.rb', line 43

def with_parent(parent)
  if parent.is_a?(Sass::Tree::DirectiveNode)
    if MERGEABLE_DIRECTIVES.any? {|klass| parent.is_a?(klass)}
      old_parent_directive = @parent_directives.pop
    end
    @parent_directives.push parent
  end

  old_parent, @parent = @parent, parent
  yield
ensure
  @parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode)
  @parent_directives.push old_parent_directive if old_parent_directive
  @parent = old_parent
end