Class: ERBLint::Linters::BaseLinter

Inherits:
Linter
  • Object
show all
Includes:
Helpers::RuleHelpers, TagTreeHelpers
Defined in:
lib/primer/view_components/linters/base_linter.rb

Overview

Provides the basic linter logic. When inherited, you should define:

  • ‘TAGS` - required - The HTML tags that the component supports. It will be used by the linter to match elements.

  • ‘MESSAGE` - required - The message shown when there’s an offense.

  • ‘CLASSES` - optional - The CSS classes that the component needs. The linter will only match elements with one of those classes.

  • ‘REQUIRED_ARGUMENTS` - optional - A list of HTML attributes that are required by the component.

Defined Under Namespace

Classes: ConfigSchema

Constant Summary collapse

DUMP_FILE =
".erblint-counter-ignore.json"
DISALLOWED_CLASSES =
[].freeze
CLASSES =
[].freeze
REQUIRED_ARGUMENTS =
[].freeze

Constants included from TagTreeHelpers

TagTreeHelpers::SELF_CLOSING_TAGS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::RuleHelpers

#erb_nodes, #extract_ruby_from_erb_node, #generate_node_offense, #generate_offense, #tags

Methods included from TagTreeHelpers

#build_tag_tree, #self_closing?

Class Method Details

.inherited(base) ⇒ Object

[View source]

32
33
34
35
36
# File 'lib/primer/view_components/linters/base_linter.rb', line 32

def self.inherited(base)
  super
  base.include(ERBLint::LinterRegistry)
  base.config_schema = ConfigSchema
end

Instance Method Details

#autocorrect(processed_source, offense) ⇒ Object

[View source]

85
86
87
88
89
90
91
92
93
94
95
# File 'lib/primer/view_components/linters/base_linter.rb', line 85

def autocorrect(processed_source, offense)
  return unless offense.context

  lambda do |corrector|
    if offense.context.include?(counter_disable)
      correct_counter(corrector, processed_source, offense)
    else
      corrector.replace(offense.source_range, offense.context)
    end
  end
end

#run(processed_source) ⇒ Object

[View source]

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/primer/view_components/linters/base_linter.rb', line 38

def run(processed_source)
  @total_offenses = 0
  @offenses_not_corrected = 0
  (tags, tag_tree) = build_tag_tree(processed_source)

  tags.each do |tag|
    next if tag.closing?
    next if self.class::TAGS.present? && self.class::TAGS&.none?(tag.name)

    classes = tag.attributes["class"]&.value&.split(" ") || []
    tag_tree[tag][:offense] = false

    next if (classes & self.class::DISALLOWED_CLASSES).any?
    next unless self.class::CLASSES.blank? || classes.any? do |klass|
      self.class::CLASSES.any? { |matcher| matcher === klass } # rubocop:disable Style/CaseEquality
    end

    args = map_arguments(tag, tag_tree[tag])
    correction = correction(args)

    attributes = tag.attributes.each.map(&:name).join(" ")
    matches_required_attributes = self.class::REQUIRED_ARGUMENTS.blank? || self.class::REQUIRED_ARGUMENTS.all? { |arg| attributes.match?(arg) }

    tag_tree[tag][:offense] = true
    tag_tree[tag][:correctable] = matches_required_attributes && !correction.nil?
    tag_tree[tag][:message] = message(args, processed_source)
    tag_tree[tag][:correction] = correction
  end

  tag_tree.each do |tag, h|
    next unless h[:offense]

    @total_offenses += 1
    # We always fix the offenses using blocks. The closing tag corresponds to `<% end %>`.
    if h[:correctable]
      add_correction(tag, h)
    else
      @offenses_not_corrected += 1
      generate_offense(self.class, processed_source, tag, h[:message])
    end
  end

  counter_correct?(processed_source)

  dump_data(processed_source) if ENV["DUMP_LINT_DATA"] == "1"
end