module SCSSLint # Checks the order of nested items within a rule set. class Linter::DeclarationOrder < Linter include LinterRegistry def check_order(node) check_node(node) yield # Continue linting children end alias_method :visit_rule, :check_order alias_method :visit_mixin, :check_order private MESSAGE = 'Rule sets should be ordered as follows: '\ '@extends, @includes without @content, ' \ 'properties, @includes with @content, ' \ 'nested rule sets' MIXIN_WITH_CONTENT = 'mixin_with_content' DECLARATION_ORDER = [ Sass::Tree::ExtendNode, Sass::Tree::MixinNode, Sass::Tree::PropNode, MIXIN_WITH_CONTENT, Sass::Tree::RuleNode, ] def important_node?(node) DECLARATION_ORDER.include?(node.class) end def check_node(node) children = node.children.select { |n| important_node?(n) } .map { |n| node_declaration_type(n) } sorted_children = children.sort do |a, b| DECLARATION_ORDER.index(a) <=> DECLARATION_ORDER.index(b) end return unless children != sorted_children add_lint(node.children.first, MESSAGE) end def node_declaration_type(node) # If the node has no children, return the class. return node.class unless node.has_children # If the node is a mixin with children, indicate that; # otherwise, just return the class. return node.class unless node.is_a?(Sass::Tree::MixinNode) MIXIN_WITH_CONTENT end end end