# class Sass::Tree::Visitors::Cssize

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

### Constants

Extend

  A simple struct wrapping up information about a single `@extend` instance. A single {ExtendNode} can have multiple Extends if either the parent node or the extended selector is a comma sequence.

  @attr extender [Sass::Selector::Sequence]

      The selector of the CSS rule containing the `@extend`.

  @attr target [Array<Sass::Selector::Simple>] The selector being `@extend`ed. @attr node [Sass::Tree::ExtendNode] The node that produced this extend. @attr directives [Array<Sass::Tree::DirectiveNode>]

      The directives containing the `@extend`.

  @attr success [Boolean]

      Whether this extend successfully matched a selector.

### Public Class Methods

new() click to toggle source

    # File lib/sass/tree/visitors/cssize.rb, line 16
    def initialize
      @parents = []
      @extends = Sass::Util::SubsetMap.new
    end

visit(root) click to toggle source

@param root [Tree::Node] The root node of the tree to visit. @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes

    *and* the extensions defined for this tree

Calls superclass method Sass::Tree::Visitors::Base.visit

    # File lib/sass/tree/visitors/cssize.rb, line 6
    def self.visit(root); super; end

### Protected Instance Methods

parent() click to toggle source

Returns the immediate parent of the current node. @return [Tree::Node]

    # File lib/sass/tree/visitors/cssize.rb, line 12
    def parent
      @parents.last
    end

visit(node) click to toggle source

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

Calls superclass method Sass::Tree::Visitors::Base.visit

    # 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) click to toggle source

    # 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) click to toggle source

Keeps track of the current parent node.

    # 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) click to toggle source

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

@param node [Sass::Tree::Node] @return [Array<Sass::Tree::Node>] the flattened results of

    visiting all the children of `node`

    # 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) { || ... } click to toggle source

Bubbles a directive up through RuleNodes.

    # 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) click to toggle source

Registers an extension in the `@extends` subset map.

    # 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) click to toggle source

Modifies exception backtraces to include the imported file.

    # 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) { || ... } click to toggle source

    # 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) { || ... } click to toggle source

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

    # 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) { || ... } click to toggle source

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

    # 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) { || ... } click to toggle source

Converts the entire document to CSS.

@return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes

    *and* the extensions defined for this tree

    # 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) { || ... } click to toggle source

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

    # 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) { || ... } click to toggle source

Bubbles the `@supports` directive up through RuleNodes.

    # 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) click to toggle source

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

    # 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) { || ... } click to toggle source

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

@param parent [Tree::Node] The new parent for the duration of the block. @yield A block in which the parent is set to `parent`. @return [Object] The return value of the block.

    # File lib/sass/tree/visitors/cssize.rb, line 52
    def with_parent(parent)
      @parents.push parent
      yield
    ensure
      @parents.pop
    end

### Private Instance Methods

bubblable?(node) click to toggle source

Returns whether or not a node can be bubbled up through the syntax tree.

@param node [Sass::Tree::Node] @return [Boolean]

    # File lib/sass/tree/visitors/cssize.rb, line 332
    def bubblable?(node)
      node.is_a?(Sass::Tree::RuleNode) || node.bubbles?
    end

bubble(node) click to toggle source

“Bubbles” `node` one level by copying the parent and wrapping `node`'s children with it.

@param node [Sass::Tree::Node]. @return [Bubble]

    # File lib/sass/tree/visitors/cssize.rb, line 276
    def bubble(node)
      new_rule = parent.dup
      new_rule.children = node.children
      node.children = [new_rule]
      Bubble.new(node)
    end

debubble(children, parent = nil) { |node| ... } click to toggle source

Pops all bubbles in `children` and intersperses the results with the other values.

If `parent` is passed, it's copied and used as the parent node for the nested portions of `children`.

@param children [List<Sass::Tree::Node, Bubble>] @param parent [Sass::Tree::Node] @yield [node] An optional block for processing bubbled nodes. Each bubbled

    node will be passed to this block.

@yieldparam node [Sass::Tree::Node] A bubbled node. @yieldreturn [Sass::Tree::Node?] A node to use in place of the bubbled node.

    This can be the node itself, or `nil` to indicate that the node should be
    omitted.

@return [List<Sass::Tree::Node, Bubble>]

    # File lib/sass/tree/visitors/cssize.rb, line 298
    def debubble(children, parent = nil)
      # Keep track of the previous parent so that we don't divide `parent`
      # unnecessarily if the `@at-root` doesn't produce any new nodes (e.g.
      # `@at-root {@extend %foo}`).
      previous_parent = nil

      Sass::Util.slice_by(children) {|c| c.is_a?(Bubble)}.map do |(is_bubble, slice)|
        unless is_bubble
          next slice unless parent
          if previous_parent
            previous_parent.children.push(*slice)
            next []
          else
            previous_parent = new_parent = parent.dup
            new_parent.children = slice
            next new_parent
          end
        end

        slice.map do |bubble|
          next unless (node = block_given? ? yield(bubble.node) : bubble.node)
          node.tabs += bubble.tabs
          node.group_end = bubble.group_end
          results = [visit(node)].flatten
          previous_parent = nil unless results.empty?
          results
        end.compact
      end.flatten
    end