erblint-github 0.0.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e46c63a275b6e7d34b0aab81c0de051625bfbd7fc6c3fedb944a6d10a5ba3cff
4
- data.tar.gz: 33984e9857ae9a998a3ae56a9fd29c0bf89557d41eee3698cb13731327da7988
3
+ metadata.gz: 48eba970607504df53f6b0f2ce5cff3b07051148eb1f4dca2fd78fa8287671a9
4
+ data.tar.gz: 820da77f5a3ad624c32f4de22817e1c3d80ed755ddbe717368ef97e17e87e262
5
5
  SHA512:
6
- metadata.gz: 0404a048ebd068503aa1f7521a9c1f116165e3d9c507bee6e0a04c28ec3871b12fb53dc1141ff57981d013ac0af8d2bb41b21faea545c7741ff28f1d4e4c3b95
7
- data.tar.gz: c64a47d57b7bb87ed7f1437be1430f44f860f58e0d28a6acca5b530e442350e9d1e8f28d83fdf8230d731a0851e812f9ad82278bec964aefffdac16fc250a530
6
+ metadata.gz: 34b69cd67df4ea14698098a1331bd787cc9c653cb616f8020c0767478427a41144d177706397eaedc7f7f3767b60758231a000ab45d3fef2dcd8ee07249b49ad
7
+ data.tar.gz: d34ed13a0a85b146f4b2eff4e6fcbe00bbe7aef41d7168c6dadb2cc78a3fd49b42cc5dc92e6669cffb7b9bb81cf982495862fb46acb166a49dd63c9813d31d60
data/README.md CHANGED
@@ -23,19 +23,31 @@ require "erblint-github/linters"
23
23
  ```yaml
24
24
  ---
25
25
  linters:
26
+ GitHub::Accessibility::AvoidBothDisabledAndAriaDisabled:
27
+ enabled: true
28
+ GitHub::Accessibility::IframeHasTitle:
29
+ enabled: true
26
30
  GitHub::Accessibility::ImageHasAlt:
27
31
  enabled: true
28
- GitHub::Accessibility::NoAriaLabelMisuse:
32
+ GitHub::Accessibility::NoAriaLabelMisuseCounter:
33
+ enabled: true
34
+ GitHub::Accessibility::NoPositiveTabIndex:
29
35
  enabled: true
30
36
  GitHub::Accessibility::NoRedundantImageAlt:
31
37
  enabled: true
38
+ GitHub::Accessibility::NoTitleAttributeCounter:
39
+ enabled: true
32
40
  ```
33
41
 
34
- ### Rules
42
+ ## Rules
35
43
 
36
- - [GitHub::Accessibility::ImageHasAlt](./docs/rules/accessibility/no-aria-label-misuse.md)
37
- - [GitHub::Accessibility::NoAriaLabelMisuse](./docs/rules/accessibility/image-has-alt.md)
44
+ - [GitHub::Accessibility::AvoidBothDisabledAndAriaDisabled](./docs/rules/accessibility/avoid-both-disabled-and-aria-disabled.md)
45
+ - [GitHub::Accessibility::IframeHasTitle](./docs/rules/accessibility/iframe-has-title.md)
46
+ - [GitHub::Accessibility::ImageHasAlt](./docs/rules/accessibility/image-has-alt.md)
47
+ - [GitHub::Accessibility::NoAriaLabelMisuseCounter](./docs/rules/accessibility/no-aria-label-misuse-counter.md)
48
+ - [GitHub::Accessibility::NoPositiveTabIndex](./docs/rules/accessibility/no-positive-tab-index.md)
38
49
  - [GitHub::Accessibility::NoRedundantImageAlt](./docs/rules/accessibility/no-redundant-image-alt.md)
50
+ - [GitHub::Accessibility::NoTitleAttributeCounter](./docs/rules/accessibility/no-title-attribute-counter.md)
39
51
 
40
52
  ## Testing
41
53
 
@@ -11,7 +11,7 @@ module ERBLint
11
11
  indicator_node, _, code_node, = *node
12
12
  indicator = indicator_node&.loc&.source
13
13
  comment = code_node&.loc&.source&.strip
14
- rule_name = self.class.name.gsub("ERBLint::Linters::", "")
14
+ rule_name = simple_class_name
15
15
 
16
16
  if indicator == "#" && comment.start_with?("erblint:disable") && comment.match(rule_name)
17
17
  if @offenses.any?
@@ -24,10 +24,43 @@ module ERBLint
24
24
  end
25
25
  end
26
26
 
27
+ def counter_correct?(processed_source)
28
+ comment_node = nil
29
+ expected_count = 0
30
+ rule_name = simple_class_name
31
+ offenses_count = @offenses.length
32
+
33
+ processed_source.parser.ast.descendants(:erb).each do |node|
34
+ indicator_node, _, code_node, = *node
35
+ indicator = indicator_node&.loc&.source
36
+ comment = code_node&.loc&.source&.strip
37
+
38
+ if indicator == "#" && comment.start_with?("erblint:counter") && comment.match(rule_name)
39
+ comment_node = node
40
+ expected_count = comment.match(/\s(\d+)\s?$/)[1].to_i
41
+ end
42
+ end
43
+
44
+ if offenses_count.zero?
45
+ # have to adjust to get `\n` so we delete the whole line
46
+ add_offense(processed_source.to_source_range(comment_node.loc.adjust(end_pos: 1)), "Unused erblint:counter comment for #{rule_name}", "") if comment_node
47
+ return
48
+ end
49
+
50
+ first_offense = @offenses[0]
51
+
52
+ if comment_node.nil?
53
+ add_offense(processed_source.to_source_range(first_offense.source_range), "#{rule_name}: If you must, add <%# erblint:counter #{rule_name} #{offenses_count} %> to bypass this check.", "<%# erblint:counter #{rule_name} #{offenses_count} %>")
54
+ else
55
+ clear_offenses
56
+ add_offense(processed_source.to_source_range(comment_node.loc), "Incorrect erblint:counter number for #{rule_name}. Expected: #{expected_count}, actual: #{offenses_count}.", "<%# erblint:counter #{rule_name} #{offenses_count} %>") if expected_count != offenses_count
57
+ end
58
+ end
59
+
27
60
  def generate_offense(klass, processed_source, tag, message = nil, replacement = nil)
28
61
  message ||= klass::MESSAGE
29
- klass_name = klass.name.demodulize
30
- offense = ["#{klass_name}:#{message}", tag.node.loc.source].join("\n")
62
+ message += "\nLearn more at https://github.com/github/erblint-github#rules.\n"
63
+ offense = ["#{simple_class_name}:#{message}", tag.node.loc.source].join("\n")
31
64
  add_offense(processed_source.to_source_range(tag.loc), offense, replacement)
32
65
  end
33
66
 
@@ -45,6 +78,10 @@ module ERBLint
45
78
  def tags(processed_source)
46
79
  processed_source.parser.nodes_with_type(:tag).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
47
80
  end
81
+
82
+ def simple_class_name
83
+ self.class.name.gsub("ERBLint::Linters::", "")
84
+ end
48
85
  end
49
86
  end
50
87
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class AvoidBothDisabledAndAriaDisabled < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ ELEMENTS_WITH_NATIVE_DISABLED_ATTRIBUTE_SUPPORT = %w[button fieldset input optgroup option select textarea].freeze
14
+ MESSAGE = "[aria-disabled] may be used in place of native HTML [disabled] to allow tab-focus on an otherwise ignored element. Setting both attributes is contradictory."
15
+
16
+ def run(processed_source)
17
+ tags(processed_source).each do |tag|
18
+ next if tag.closing?
19
+ next unless ELEMENTS_WITH_NATIVE_DISABLED_ATTRIBUTE_SUPPORT.include?(tag.name)
20
+ next unless tag.attributes["disabled"] && tag.attributes["aria-disabled"]
21
+
22
+ generate_offense(self.class, processed_source, tag)
23
+ end
24
+
25
+ rule_disabled?(processed_source)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class IframeHasTitle < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ MESSAGE = "<iframe> with meaningful content should have a title attribute that identifies the content."\
14
+ " If <iframe> has no meaningful content, hide it from assistive technology with `aria-hidden='true'`."\
15
+
16
+ def run(processed_source)
17
+ tags(processed_source).each do |tag|
18
+ next if tag.name != "iframe"
19
+ next if tag.closing?
20
+
21
+ title = possible_attribute_values(tag, "title")
22
+
23
+ generate_offense(self.class, processed_source, tag) if title.empty? && !aria_hidden?(tag)
24
+ end
25
+
26
+ rule_disabled?(processed_source)
27
+ end
28
+
29
+ private
30
+
31
+ def aria_hidden?(tag)
32
+ tag.attributes["aria-hidden"]&.value&.present?
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -6,7 +6,7 @@ module ERBLint
6
6
  module Linters
7
7
  module GitHub
8
8
  module Accessibility
9
- class NoAriaLabelMisuse < Linter
9
+ class NoAriaLabelMisuseCounter < Linter
10
10
  include ERBLint::Linters::CustomHelpers
11
11
  include LinterRegistry
12
12
 
@@ -34,7 +34,21 @@ module ERBLint
34
34
  end
35
35
  end
36
36
  end
37
- rule_disabled?(processed_source)
37
+ counter_correct?(processed_source)
38
+ end
39
+
40
+ def autocorrect(processed_source, offense)
41
+ return unless offense.context
42
+
43
+ lambda do |corrector|
44
+ if processed_source.file_content.include?("erblint:counter #{simple_class_name}")
45
+ # update the counter if exists
46
+ corrector.replace(offense.source_range, offense.context)
47
+ else
48
+ # add comment with counter if none
49
+ corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
50
+ end
51
+ end
38
52
  end
39
53
  end
40
54
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class NoPositiveTabIndex < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ MESSAGE = "Do not use positive tabindex as it is error prone and can severely disrupt navigation experience for keyboard users"
14
+
15
+ def run(processed_source)
16
+ tags(processed_source).each do |tag|
17
+ next if tag.closing?
18
+ next unless tag.attributes["tabindex"]&.value.to_i.positive?
19
+
20
+ generate_offense(self.class, processed_source, tag)
21
+ end
22
+
23
+ rule_disabled?(processed_source)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class NoTitleAttributeCounter < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ MESSAGE = "The title attribute should never be used unless for an `<iframe>` as it is inaccessible for several groups of users."
14
+
15
+ def run(processed_source)
16
+ tags(processed_source).each do |tag|
17
+ next if tag.name == "iframe"
18
+ next if tag.closing?
19
+
20
+ title = possible_attribute_values(tag, "title")
21
+ generate_offense(self.class, processed_source, tag) if title.present?
22
+ end
23
+
24
+ counter_correct?(processed_source)
25
+ end
26
+
27
+ def autocorrect(processed_source, offense)
28
+ return unless offense.context
29
+
30
+ lambda do |corrector|
31
+ if processed_source.file_content.include?("erblint:counter #{simple_class_name}")
32
+ # update the counter if exists
33
+ corrector.replace(offense.source_range, offense.context)
34
+ else
35
+ # add comment with counter if none
36
+ corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :docs do
4
+ task :coverage do
5
+ require "erb_lint/all"
6
+ require "erblint-github/linters"
7
+
8
+ Dir[File.join(__dir__, "linters", "github/**/*.rb")].sort.each do |file|
9
+ rule_documentation_path = file
10
+ .gsub("#{__dir__}linters/github/", "docs/rules/")
11
+ .gsub(".rb", ".md")
12
+ .tr("_", "-")
13
+ raise "Missing rule documentation. Please document rule in #{rule_documentation_path}" unless File.file?(rule_documentation_path.to_s)
14
+ end
15
+ puts "All rules have been properly documented."
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erblint-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-01 00:00:00.000000000 Z
11
+ date: 2022-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erb_lint
@@ -105,9 +105,14 @@ files:
105
105
  - README.md
106
106
  - lib/erblint-github/linters.rb
107
107
  - lib/erblint-github/linters/custom_helpers.rb
108
+ - lib/erblint-github/linters/github/accessibility/avoid_both_disabled_and_aria_disabled.rb
109
+ - lib/erblint-github/linters/github/accessibility/iframe_has_title.rb
108
110
  - lib/erblint-github/linters/github/accessibility/image_has_alt.rb
109
- - lib/erblint-github/linters/github/accessibility/no_aria_label_misuse.rb
111
+ - lib/erblint-github/linters/github/accessibility/no_aria_label_misuse_counter.rb
112
+ - lib/erblint-github/linters/github/accessibility/no_positive_tab_index.rb
110
113
  - lib/erblint-github/linters/github/accessibility/no_redundant_image_alt.rb
114
+ - lib/erblint-github/linters/github/accessibility/no_title_attribute_counter.rb
115
+ - lib/tasks/docs.rake
111
116
  homepage: https://github.com/github/erblint-github
112
117
  licenses:
113
118
  - MIT
@@ -127,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
132
  - !ruby/object:Gem::Version
128
133
  version: '0'
129
134
  requirements: []
130
- rubygems_version: 3.0.3
135
+ rubygems_version: 3.2.32
131
136
  signing_key:
132
137
  specification_version: 4
133
138
  summary: erblint GitHub