Class: Herb::Engine::Validators::NestingValidator

Inherits:
Herb::Engine::Validator show all
Defined in:
lib/herb/engine/validators/nesting_validator.rb,
sig/herb/engine/validators/nesting_validator.rbs

Instance Attribute Summary

Attributes inherited from Herb::Engine::Validator

#diagnostics, #enabled

Instance Method Summary collapse

Methods inherited from Herb::Engine::Validator

#add_diagnostic, #clear_diagnostics, #diagnostic_count, #enabled?, #error, #errors, #errors?, #hint, #info, #initialize, #validate, #warning, #warnings, #warnings?

Methods inherited from Visitor

#visit, #visit_all, #visit_cdata_node, #visit_child_nodes, #visit_document_node, #visit_erb_begin_node, #visit_erb_block_node, #visit_erb_case_match_node, #visit_erb_case_node, #visit_erb_content_node, #visit_erb_else_node, #visit_erb_end_node, #visit_erb_ensure_node, #visit_erb_for_node, #visit_erb_if_node, #visit_erb_in_node, #visit_erb_node, #visit_erb_open_tag_node, #visit_erb_render_node, #visit_erb_rescue_node, #visit_erb_strict_locals_node, #visit_erb_unless_node, #visit_erb_until_node, #visit_erb_when_node, #visit_erb_while_node, #visit_erb_yield_node, #visit_html_attribute_name_node, #visit_html_attribute_node, #visit_html_attribute_value_node, #visit_html_close_tag_node, #visit_html_comment_node, #visit_html_conditional_element_node, #visit_html_conditional_open_tag_node, #visit_html_doctype_node, #visit_html_omitted_close_tag_node, #visit_html_open_tag_node, #visit_html_text_node, #visit_html_virtual_close_tag_node, #visit_literal_node, #visit_node, #visit_ruby_html_attributes_splat_node, #visit_ruby_literal_node, #visit_ruby_parameter_node, #visit_ruby_render_keywords_node, #visit_ruby_render_local_node, #visit_whitespace_node, #visit_xml_declaration_node

Methods included from AST::Helpers

#erb_comment?, #erb_graphql?, #erb_output?, #erb_outputs?, #inline_ruby_comment?

Constructor Details

This class inherits a constructor from Herb::Engine::Validator

Instance Method Details

#add_validation_error(type, location, message) ⇒ Object

Parameters:

  • type (Object)
  • location (Object)
  • message (Object)

Returns:

  • (Object)


89
90
91
# File 'lib/herb/engine/validators/nesting_validator.rb', line 89

def add_validation_error(type, location, message)
  error(message, location, code: type, source: "NestingValidator")
end

#find_nested_elements(node, tag_name, &block) ⇒ void

This method returns an undefined value.

Parameters:

  • node (Object)
  • tag_name (Object)


77
78
79
80
81
82
83
84
85
86
87
# File 'lib/herb/engine/validators/nesting_validator.rb', line 77

def find_nested_elements(node, tag_name, &block)
  node.body.each do |child|
    if child.is_a?(Herb::AST::HTMLElementNode)
      if child.tag_name&.value&.downcase == tag_name
        yield child
      else
        find_nested_elements(child, tag_name, &block)
      end
    end
  end
end

#validate_html_nesting(node) ⇒ Object

Parameters:

  • node (Object)

Returns:

  • (Object)


19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/herb/engine/validators/nesting_validator.rb', line 19

def validate_html_nesting(node)
  tag_name = node.tag_name&.value&.downcase
  return unless tag_name

  case tag_name
  when "p"
    validate_no_block_elements_in_paragraph(node)
  when "a"
    validate_no_nested_anchors(node)
  when "button"
    validate_no_interactive_in_button(node)
  end
end

#validate_no_block_elements_in_paragraph(node) ⇒ Object

Parameters:

  • node (Object)

Returns:

  • (Object)


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/herb/engine/validators/nesting_validator.rb', line 33

def validate_no_block_elements_in_paragraph(node)
  block_elements = ["div", "section", "article", "header", "footer", "nav", "aside", "p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "dl", "table", "form"]

  node.body.each do |child|
    next unless child.is_a?(Herb::AST::HTMLElementNode)

    child_tag = child.tag_name&.value&.downcase
    next unless child_tag && block_elements.include?(child_tag)

    add_validation_error(
      "InvalidNestingError",
      child.location,
      "Block element <#{child_tag}> cannot be nested inside <p> at line #{child.location.start.line}"
    )
  end
end

#validate_no_interactive_in_button(node) ⇒ Object

Parameters:

  • node (Object)

Returns:

  • (Object)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/herb/engine/validators/nesting_validator.rb', line 60

def validate_no_interactive_in_button(node)
  interactive_elements = ["a", "button", "input", "select", "textarea"]

  node.body.each do |child|
    next unless child.is_a?(Herb::AST::HTMLElementNode)

    child_tag = child.tag_name&.value&.downcase
    next unless child_tag && interactive_elements.include?(child_tag)

    add_validation_error(
      "InvalidNestingError",
      child.location,
      "Interactive element <#{child_tag}> cannot be nested inside <button> at line #{child.location.start.line}"
    )
  end
end

#validate_no_nested_anchors(node) ⇒ Object

Parameters:

  • node (Object)

Returns:

  • (Object)


50
51
52
53
54
55
56
57
58
# File 'lib/herb/engine/validators/nesting_validator.rb', line 50

def validate_no_nested_anchors(node)
  find_nested_elements(node, "a") do |nested|
    add_validation_error(
      "NestedAnchorError",
      nested.location,
      "Anchor <a> cannot be nested inside another anchor at line #{nested.location.start.line}"
    )
  end
end

#visit_html_element_node(node) ⇒ Object

Parameters:

  • node (Object)

Returns:

  • (Object)


12
13
14
15
# File 'lib/herb/engine/validators/nesting_validator.rb', line 12

def visit_html_element_node(node)
  validate_html_nesting(node)
  super
end