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 +4 -4
- data/README.md +16 -4
- data/lib/erblint-github/linters/custom_helpers.rb +40 -3
- data/lib/erblint-github/linters/github/accessibility/avoid_both_disabled_and_aria_disabled.rb +31 -0
- data/lib/erblint-github/linters/github/accessibility/iframe_has_title.rb +38 -0
- data/lib/erblint-github/linters/github/accessibility/{no_aria_label_misuse.rb → no_aria_label_misuse_counter.rb} +16 -2
- data/lib/erblint-github/linters/github/accessibility/no_positive_tab_index.rb +29 -0
- data/lib/erblint-github/linters/github/accessibility/no_title_attribute_counter.rb +44 -0
- data/lib/tasks/docs.rake +17 -0
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48eba970607504df53f6b0f2ce5cff3b07051148eb1f4dca2fd78fa8287671a9
|
4
|
+
data.tar.gz: 820da77f5a3ad624c32f4de22817e1c3d80ed755ddbe717368ef97e17e87e262
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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::
|
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
|
-
|
42
|
+
## Rules
|
35
43
|
|
36
|
-
- [GitHub::Accessibility::
|
37
|
-
- [GitHub::Accessibility::
|
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 =
|
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
|
-
|
30
|
-
offense = ["#{
|
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
|
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
|
-
|
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
|
data/lib/tasks/docs.rake
ADDED
@@ -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.
|
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:
|
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/
|
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.
|
135
|
+
rubygems_version: 3.2.32
|
131
136
|
signing_key:
|
132
137
|
specification_version: 4
|
133
138
|
summary: erblint GitHub
|