# A visitor for performing selector inheritance on a static CSS tree. # # Destructively modifies the tree. class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base # Performs the given extensions on the static CSS tree based in `root`, then # validates that all extends matched some selector. # # @param root [Tree::Node] The root node of the tree to visit. # @param extends [Sass::Util::SubsetMap{Selector::Simple => # Sass::Tree::Visitors::Cssize::Extend}] # The extensions to perform on this tree. # @return [Object] The return value of \{#visit} for the root node. def self.visit(root, extends) return if extends.empty? new(extends).send(:visit, root) check_extends_fired! extends end protected def initialize(extends) @parent_directives = [] @extends = extends end # If an exception is raised, this adds proper metadata to the backtrace. def visit(node) super(node) rescue Sass::SyntaxError => e e.modify_backtrace(:filename => node.filename, :line => node.line) raise e end # Keeps track of the current parent directives. def visit_children(parent) @parent_directives.push parent if parent.is_a?(Sass::Tree::DirectiveNode) super ensure @parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode) end # Applies the extend to a single rule's selector. def visit_rule(node) node.resolved_rules = node.resolved_rules.do_extend(@extends, @parent_directives) end class << self private def check_extends_fired!(extends) extends.each_value do |ex| next if ex.success || ex.node.optional? message = "\"#{ex.extender}\" failed to @extend \"#{ex.target.join}\"." # TODO(nweiz): this should use the Sass stack trace of the extend node. raise Sass::SyntaxError.new(< ex.node.filename, :line => ex.node.line) #{message} The selector "#{ex.target.join}" was not found. Use "@extend #{ex.target.join} !optional" if the extend should be able to fail. MESSAGE end end end end