a11y-lint 0.4.0 → 0.5.1

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: df2381933d808c2694937750b55b63897d22feb8b3726e8635504afa6bcb320b
4
- data.tar.gz: 5b04343e74f0ebb363cb2b2c065d67b9b20398d4b5bd12e86e89fe38a7bcfa8d
3
+ metadata.gz: c543a94f664746bb807384be228f6e83fcd7046f399f9c478e2c355cf26fa3c8
4
+ data.tar.gz: c37a04f979ad17e79e35b8b6fa9d615cbedebcb1fce9188eb24335e046fa4400
5
5
  SHA512:
6
- metadata.gz: 4a71a89a925316ae52107958bed1cdc927d653ccc0425b81ad2bca612f694cdc63b52d9c277f24c1957976e5972ed5c810b831f150c353393014908e7e187a6d
7
- data.tar.gz: d9982d126da0a7f412d8d43b68e90b27fcde739a3b08c2b6cc0e948241eb18b95e9e42542e5bb7ce75048f389fec11351901da0e900076369c3889d1e8c16b0a
6
+ metadata.gz: 3bb877b34c79e5e3dd5c969b21bb1e756acc8968ae245e7cd1274dc880529647ca83283a0d3ea7576d446df94d37a7565b45082f4a75008e826f1bcc9245f16d
7
+ data.tar.gz: bb23c43c6274d45fd5e9ea5cb9f73947357127fe9aa08c225438d436910357b32ca367fbe843827a3bddf4600159b4945c1bdd248805547a47a42657dd9e9cd2
@@ -34,3 +34,19 @@ globs: test/**/*.rb
34
34
  assert_equal("ImgMissingAlt", offense.rule)
35
35
  end
36
36
  ```
37
+
38
+ - Rules that inspect `ruby_code` (e.g. `link_to`, `image_tag` helpers) must include test cases for all three calling styles:
39
+ 1. Single-line with parentheses: `= link_to("", "/path", class: "icon")`
40
+ 2. Single-line without parentheses: `= link_to "", "/path", class: "icon"`
41
+ 3. Multiline with trailing comma:
42
+ ```slim
43
+ = link_to(\
44
+ "",
45
+ "/path",
46
+ class: "icon",
47
+ )
48
+ ```
49
+
50
+ Each style must be tested for both the offense case and the "passes with fix" case.
51
+
52
+ - Every rule must be tested against both the Slim and ERB pipelines.
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.1] - 2026-03-31
11
+
12
+ ### Fixed
13
+
14
+ - `LinkMissingAccessibleName` rule: detect multiline method calls with trailing commas
15
+
16
+ ## [0.5.0] - 2026-03-31
17
+
18
+ ### Added
19
+
20
+ - `LinkMissingAccessibleName` rule: detects `<a>` tags without accessible text content
21
+
10
22
  ## [0.4.0] - 2026-03-27
11
23
 
12
24
  ### Added
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ripper"
4
+
5
+ module A11y
6
+ module Lint
7
+ module Rules
8
+ # Checks that link_to / external_link_to calls with empty text
9
+ # include an aria-label (WCAG 4.1.2).
10
+ class LinkMissingAccessibleName < Rule
11
+ LINK_METHODS = %w[link_to external_link_to].freeze
12
+
13
+ def check(node)
14
+ return unless link_with_empty_text_and_no_accessible_name?(node)
15
+
16
+ "link with empty text content requires an aria-label (WCAG 4.1.2)"
17
+ end
18
+
19
+ private
20
+
21
+ def link_with_empty_text_and_no_accessible_name?(node)
22
+ code = node.ruby_code
23
+ return false unless code
24
+
25
+ sexp = Ripper.sexp(code)
26
+ return false unless sexp
27
+
28
+ call = extract_link_call(sexp)
29
+ return false unless call
30
+
31
+ first_arg_empty_string?(call) && !aria_label_within?(call)
32
+ end
33
+
34
+ def extract_link_call(sexp)
35
+ return unless sexp.is_a?(Array)
36
+ return sexp if link_call?(sexp)
37
+
38
+ sexp.each do |child|
39
+ result = extract_link_call(child)
40
+ return result if result
41
+ end
42
+
43
+ nil
44
+ end
45
+
46
+ def link_call?(sexp)
47
+ case sexp
48
+ in [:command, [:@ident, name, *], *] if LINK_METHODS.include?(name) then true
49
+ in [:method_add_arg, [:fcall, [:@ident, name, *]], *] if LINK_METHODS.include?(name) then true
50
+ else false
51
+ end
52
+ end
53
+
54
+ def first_arg_empty_string?(call)
55
+ args = extract_args(call)
56
+ return false unless args&.first
57
+
58
+ args.first in [:string_literal, [:string_content]]
59
+ end
60
+
61
+ def extract_args(call)
62
+ case call
63
+ in [:command, _, [:args_add_block, args, *]] then args
64
+ in [:method_add_arg, _, [:arg_paren, [:args_add_block, args, *]]] then args
65
+ in [:method_add_arg, _, [:arg_paren, Array => args]] then args
66
+ else nil
67
+ end
68
+ end
69
+
70
+ def aria_label_within?(sexp)
71
+ return true if aria_hash_with_label?(sexp)
72
+ return true if aria_label_string_key?(sexp)
73
+ return false unless sexp.is_a?(Array)
74
+
75
+ sexp.any? { |child| aria_label_within?(child) }
76
+ end
77
+
78
+ def aria_hash_with_label?(sexp)
79
+ return false unless sexp.is_a?(Array) && sexp[0] == :assoc_new
80
+
81
+ key = sexp[1]
82
+ value = sexp[2]
83
+
84
+ (key in [:@label, "aria:", *]) && label_key_within?(value)
85
+ end
86
+
87
+ def label_key_within?(sexp)
88
+ return true if label_key?(sexp)
89
+ return false unless sexp.is_a?(Array)
90
+
91
+ sexp.any? { |child| label_key_within?(child) }
92
+ end
93
+
94
+ def label_key?(sexp)
95
+ sexp.is_a?(Array) && sexp[0] == :assoc_new && (sexp[1] in [:@label, "label:", *])
96
+ end
97
+
98
+ def aria_label_string_key?(sexp)
99
+ return false unless sexp.is_a?(Array) && sexp[0] == :assoc_new
100
+
101
+ sexp[1] in [:string_literal, [:string_content, [:@tstring_content, "aria-label", *]]]
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module A11y
4
4
  module Lint
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.1"
6
6
  end
7
7
  end
data/lib/a11y/lint.rb CHANGED
@@ -9,6 +9,7 @@ require_relative "lint/erb_node"
9
9
  require_relative "lint/rule"
10
10
  require_relative "lint/rules/image_tag_missing_alt"
11
11
  require_relative "lint/rules/img_missing_alt"
12
+ require_relative "lint/rules/link_missing_accessible_name"
12
13
  require_relative "lint/slim_runner"
13
14
  require_relative "lint/erb_runner"
14
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: a11y-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdullah Hashim
@@ -63,6 +63,7 @@ files:
63
63
  - lib/a11y/lint/rule.rb
64
64
  - lib/a11y/lint/rules/image_tag_missing_alt.rb
65
65
  - lib/a11y/lint/rules/img_missing_alt.rb
66
+ - lib/a11y/lint/rules/link_missing_accessible_name.rb
66
67
  - lib/a11y/lint/slim_runner.rb
67
68
  - lib/a11y/lint/version.rb
68
69
  - sig/a11y/lint.rbs