@herb-tools/linter 0.8.10 → 0.9.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.
Files changed (587) hide show
  1. package/README.md +5 -5
  2. package/dist/{src/cli → cli}/argument-parser.js +15 -2
  3. package/dist/cli/argument-parser.js.map +1 -0
  4. package/dist/{src/cli → cli}/file-processor.js +155 -9
  5. package/dist/cli/file-processor.js.map +1 -0
  6. package/dist/cli/file-url.js +6 -0
  7. package/dist/cli/file-url.js.map +1 -0
  8. package/dist/cli/formatters/base-formatter.js.map +1 -0
  9. package/dist/{src/cli → cli}/formatters/detailed-formatter.js +16 -19
  10. package/dist/cli/formatters/detailed-formatter.js.map +1 -0
  11. package/dist/cli/formatters/github-actions-formatter.js.map +1 -0
  12. package/dist/cli/formatters/index.js.map +1 -0
  13. package/dist/cli/formatters/json-formatter.js.map +1 -0
  14. package/dist/cli/formatters/simple-formatter.js +54 -0
  15. package/dist/cli/formatters/simple-formatter.js.map +1 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/cli/lint-worker.js +143 -0
  18. package/dist/cli/lint-worker.js.map +1 -0
  19. package/dist/cli/output-manager.js.map +1 -0
  20. package/dist/{src/cli → cli}/summary-reporter.js +13 -16
  21. package/dist/cli/summary-reporter.js.map +1 -0
  22. package/dist/{src/cli.js → cli.js} +5 -3
  23. package/dist/cli.js.map +1 -0
  24. package/dist/{src/custom-rule-loader.js → custom-rule-loader.js} +20 -4
  25. package/dist/custom-rule-loader.js.map +1 -0
  26. package/dist/herb-disable-comment-utils.js.map +1 -0
  27. package/dist/herb-lint.js +61834 -17340
  28. package/dist/herb-lint.js.map +1 -1
  29. package/dist/index.cjs +3109 -956
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +2969 -897
  32. package/dist/index.js.map +1 -1
  33. package/dist/lint-worker.js +72889 -0
  34. package/dist/lint-worker.js.map +1 -0
  35. package/dist/linter-ignore.js.map +1 -0
  36. package/dist/{src/linter.js → linter.js} +89 -74
  37. package/dist/linter.js.map +1 -0
  38. package/dist/loader.cjs +32163 -7842
  39. package/dist/loader.cjs.map +1 -1
  40. package/dist/loader.js +32121 -7828
  41. package/dist/loader.js.map +1 -1
  42. package/dist/parse-cache.js +30 -0
  43. package/dist/parse-cache.js.map +1 -0
  44. package/dist/rules/actionview-no-silent-helper.js +45 -0
  45. package/dist/rules/actionview-no-silent-helper.js.map +1 -0
  46. package/dist/rules/actionview-no-silent-render.js +31 -0
  47. package/dist/rules/actionview-no-silent-render.js.map +1 -0
  48. package/dist/{src/rules → rules}/erb-comment-syntax.js +2 -2
  49. package/dist/rules/erb-comment-syntax.js.map +1 -0
  50. package/dist/{src/rules → rules}/erb-no-case-node-children.js +5 -3
  51. package/dist/rules/erb-no-case-node-children.js.map +1 -0
  52. package/dist/rules/erb-no-conditional-html-element.js +38 -0
  53. package/dist/rules/erb-no-conditional-html-element.js.map +1 -0
  54. package/dist/rules/erb-no-conditional-open-tag.js +24 -0
  55. package/dist/rules/erb-no-conditional-open-tag.js.map +1 -0
  56. package/dist/rules/erb-no-duplicate-branch-elements.js +329 -0
  57. package/dist/rules/erb-no-duplicate-branch-elements.js.map +1 -0
  58. package/dist/rules/erb-no-empty-control-flow.js +190 -0
  59. package/dist/rules/erb-no-empty-control-flow.js.map +1 -0
  60. package/dist/{src/rules → rules}/erb-no-empty-tags.js +2 -2
  61. package/dist/rules/erb-no-empty-tags.js.map +1 -0
  62. package/dist/{src/rules → rules}/erb-no-extra-newline.js +4 -21
  63. package/dist/rules/erb-no-extra-newline.js.map +1 -0
  64. package/dist/{src/rules → rules}/erb-no-extra-whitespace-inside-tags.js +39 -13
  65. package/dist/rules/erb-no-extra-whitespace-inside-tags.js.map +1 -0
  66. package/dist/rules/erb-no-inline-case-conditions.js +40 -0
  67. package/dist/rules/erb-no-inline-case-conditions.js.map +1 -0
  68. package/dist/rules/erb-no-instance-variables-in-partials.js +67 -0
  69. package/dist/rules/erb-no-instance-variables-in-partials.js.map +1 -0
  70. package/dist/rules/erb-no-interpolated-class-names.js +47 -0
  71. package/dist/rules/erb-no-interpolated-class-names.js.map +1 -0
  72. package/dist/rules/erb-no-javascript-tag-helper.js +34 -0
  73. package/dist/rules/erb-no-javascript-tag-helper.js.map +1 -0
  74. package/dist/{src/rules → rules}/erb-no-output-control-flow.js +9 -12
  75. package/dist/rules/erb-no-output-control-flow.js.map +1 -0
  76. package/dist/rules/erb-no-output-in-attribute-name.js +30 -0
  77. package/dist/rules/erb-no-output-in-attribute-name.js.map +1 -0
  78. package/dist/rules/erb-no-output-in-attribute-position.js +30 -0
  79. package/dist/rules/erb-no-output-in-attribute-position.js.map +1 -0
  80. package/dist/rules/erb-no-raw-output-in-attribute-value.js +35 -0
  81. package/dist/rules/erb-no-raw-output-in-attribute-value.js.map +1 -0
  82. package/dist/rules/erb-no-silent-statement.js +44 -0
  83. package/dist/rules/erb-no-silent-statement.js.map +1 -0
  84. package/dist/{src/rules → rules}/erb-no-silent-tag-in-attribute-name.js +2 -2
  85. package/dist/rules/erb-no-silent-tag-in-attribute-name.js.map +1 -0
  86. package/dist/rules/erb-no-statement-in-script.js +58 -0
  87. package/dist/rules/erb-no-statement-in-script.js.map +1 -0
  88. package/dist/rules/erb-no-then-in-control-flow.js +45 -0
  89. package/dist/rules/erb-no-then-in-control-flow.js.map +1 -0
  90. package/dist/rules/erb-no-trailing-whitespace.js +138 -0
  91. package/dist/rules/erb-no-trailing-whitespace.js.map +1 -0
  92. package/dist/rules/erb-no-unsafe-js-attribute.js +36 -0
  93. package/dist/rules/erb-no-unsafe-js-attribute.js.map +1 -0
  94. package/dist/rules/erb-no-unsafe-raw.js +63 -0
  95. package/dist/rules/erb-no-unsafe-raw.js.map +1 -0
  96. package/dist/rules/erb-no-unsafe-script-interpolation.js +88 -0
  97. package/dist/rules/erb-no-unsafe-script-interpolation.js.map +1 -0
  98. package/dist/{src/rules → rules}/erb-prefer-image-tag-helper.js +5 -4
  99. package/dist/rules/erb-prefer-image-tag-helper.js.map +1 -0
  100. package/dist/{src/rules → rules}/erb-require-trailing-newline.js +2 -2
  101. package/dist/rules/erb-require-trailing-newline.js.map +1 -0
  102. package/dist/{src/rules → rules}/erb-require-whitespace-inside-tags.js +39 -15
  103. package/dist/rules/erb-require-whitespace-inside-tags.js.map +1 -0
  104. package/dist/{src/rules → rules}/erb-right-trim.js +2 -2
  105. package/dist/rules/erb-right-trim.js.map +1 -0
  106. package/dist/{src/rules → rules}/erb-strict-locals-comment-syntax.js +4 -4
  107. package/dist/rules/erb-strict-locals-comment-syntax.js.map +1 -0
  108. package/dist/{src/rules → rules}/erb-strict-locals-required.js +2 -2
  109. package/dist/rules/erb-strict-locals-required.js.map +1 -0
  110. package/dist/rules/file-utils.js.map +1 -0
  111. package/dist/rules/herb-disable-comment-base.js.map +1 -0
  112. package/dist/{src/rules → rules}/herb-disable-comment-malformed.js +2 -2
  113. package/dist/rules/herb-disable-comment-malformed.js.map +1 -0
  114. package/dist/{src/rules → rules}/herb-disable-comment-missing-rules.js +2 -2
  115. package/dist/rules/herb-disable-comment-missing-rules.js.map +1 -0
  116. package/dist/{src/rules → rules}/herb-disable-comment-no-duplicate-rules.js +2 -2
  117. package/dist/rules/herb-disable-comment-no-duplicate-rules.js.map +1 -0
  118. package/dist/{src/rules → rules}/herb-disable-comment-no-redundant-all.js +2 -2
  119. package/dist/rules/herb-disable-comment-no-redundant-all.js.map +1 -0
  120. package/dist/{src/rules → rules}/herb-disable-comment-unnecessary.js +2 -2
  121. package/dist/rules/herb-disable-comment-unnecessary.js.map +1 -0
  122. package/dist/{src/rules → rules}/herb-disable-comment-valid-rule-name.js +2 -2
  123. package/dist/rules/herb-disable-comment-valid-rule-name.js.map +1 -0
  124. package/dist/rules/html-allowed-script-type.js +57 -0
  125. package/dist/rules/html-allowed-script-type.js.map +1 -0
  126. package/dist/rules/html-anchor-require-href.js +68 -0
  127. package/dist/rules/html-anchor-require-href.js.map +1 -0
  128. package/dist/{src/rules → rules}/html-aria-attribute-must-be-valid.js +3 -3
  129. package/dist/rules/html-aria-attribute-must-be-valid.js.map +1 -0
  130. package/dist/{src/rules → rules}/html-aria-label-is-well-formatted.js +3 -3
  131. package/dist/rules/html-aria-label-is-well-formatted.js.map +1 -0
  132. package/dist/{src/rules → rules}/html-aria-level-must-be-valid.js +3 -3
  133. package/dist/rules/html-aria-level-must-be-valid.js.map +1 -0
  134. package/dist/{src/rules → rules}/html-aria-role-heading-requires-level.js +5 -4
  135. package/dist/rules/html-aria-role-heading-requires-level.js.map +1 -0
  136. package/dist/{src/rules → rules}/html-aria-role-must-be-valid.js +3 -3
  137. package/dist/rules/html-aria-role-must-be-valid.js.map +1 -0
  138. package/dist/{src/rules → rules}/html-attribute-double-quotes.js +4 -4
  139. package/dist/rules/html-attribute-double-quotes.js.map +1 -0
  140. package/dist/{src/rules → rules}/html-attribute-equals-spacing.js +2 -2
  141. package/dist/rules/html-attribute-equals-spacing.js.map +1 -0
  142. package/dist/{src/rules → rules}/html-attribute-values-require-quotes.js +2 -2
  143. package/dist/rules/html-attribute-values-require-quotes.js.map +1 -0
  144. package/dist/{src/rules → rules}/html-avoid-both-disabled-and-aria-disabled.js +9 -9
  145. package/dist/rules/html-avoid-both-disabled-and-aria-disabled.js.map +1 -0
  146. package/dist/{src/rules → rules}/html-body-only-elements.js +5 -4
  147. package/dist/rules/html-body-only-elements.js.map +1 -0
  148. package/dist/{src/rules → rules}/html-boolean-attributes-no-value.js +4 -3
  149. package/dist/rules/html-boolean-attributes-no-value.js.map +1 -0
  150. package/dist/rules/html-details-has-summary.js +52 -0
  151. package/dist/rules/html-details-has-summary.js.map +1 -0
  152. package/dist/{src/rules → rules}/html-head-only-elements.js +6 -5
  153. package/dist/rules/html-head-only-elements.js.map +1 -0
  154. package/dist/{src/rules → rules}/html-iframe-has-title.js +8 -11
  155. package/dist/rules/html-iframe-has-title.js.map +1 -0
  156. package/dist/{src/rules → rules}/html-img-require-alt.js +11 -5
  157. package/dist/rules/html-img-require-alt.js.map +1 -0
  158. package/dist/{src/rules → rules}/html-input-require-autocomplete.js +7 -10
  159. package/dist/rules/html-input-require-autocomplete.js.map +1 -0
  160. package/dist/{src/rules → rules}/html-navigation-has-label.js +6 -5
  161. package/dist/rules/html-navigation-has-label.js.map +1 -0
  162. package/dist/rules/html-no-abstract-roles.js +29 -0
  163. package/dist/rules/html-no-abstract-roles.js.map +1 -0
  164. package/dist/rules/html-no-aria-hidden-on-body.js +42 -0
  165. package/dist/rules/html-no-aria-hidden-on-body.js.map +1 -0
  166. package/dist/{src/rules → rules}/html-no-aria-hidden-on-focusable.js +6 -5
  167. package/dist/rules/html-no-aria-hidden-on-focusable.js.map +1 -0
  168. package/dist/{src/rules → rules}/html-no-block-inside-inline.js +6 -9
  169. package/dist/rules/html-no-block-inside-inline.js.map +1 -0
  170. package/dist/{src/rules → rules}/html-no-duplicate-attributes.js +4 -3
  171. package/dist/rules/html-no-duplicate-attributes.js.map +1 -0
  172. package/dist/{src/rules → rules}/html-no-duplicate-ids.js +14 -11
  173. package/dist/rules/html-no-duplicate-ids.js.map +1 -0
  174. package/dist/{src/rules → rules}/html-no-duplicate-meta-names.js +22 -20
  175. package/dist/rules/html-no-duplicate-meta-names.js.map +1 -0
  176. package/dist/{src/rules → rules}/html-no-empty-attributes.js +2 -2
  177. package/dist/rules/html-no-empty-attributes.js.map +1 -0
  178. package/dist/rules/html-no-empty-headings.js +98 -0
  179. package/dist/rules/html-no-empty-headings.js.map +1 -0
  180. package/dist/{src/rules → rules}/html-no-nested-links.js +23 -15
  181. package/dist/rules/html-no-nested-links.js.map +1 -0
  182. package/dist/{src/rules → rules}/html-no-positive-tab-index.js +3 -3
  183. package/dist/rules/html-no-positive-tab-index.js.map +1 -0
  184. package/dist/{src/rules → rules}/html-no-self-closing.js +4 -4
  185. package/dist/rules/html-no-self-closing.js.map +1 -0
  186. package/dist/{src/rules → rules}/html-no-space-in-tag.js +4 -6
  187. package/dist/rules/html-no-space-in-tag.js.map +1 -0
  188. package/dist/{src/rules → rules}/html-no-title-attribute.js +6 -5
  189. package/dist/rules/html-no-title-attribute.js.map +1 -0
  190. package/dist/{src/rules → rules}/html-no-underscores-in-attribute-names.js +2 -2
  191. package/dist/rules/html-no-underscores-in-attribute-names.js.map +1 -0
  192. package/dist/rules/html-require-closing-tags.js +29 -0
  193. package/dist/rules/html-require-closing-tags.js.map +1 -0
  194. package/dist/{src/rules → rules}/html-tag-name-lowercase.js +13 -9
  195. package/dist/rules/html-tag-name-lowercase.js.map +1 -0
  196. package/dist/{src/rules → rules}/index.js +27 -4
  197. package/dist/rules/index.js.map +1 -0
  198. package/dist/{src/rules → rules}/parser-no-errors.js +3 -3
  199. package/dist/rules/parser-no-errors.js.map +1 -0
  200. package/dist/{src/rules → rules}/rule-utils.js +144 -219
  201. package/dist/rules/rule-utils.js.map +1 -0
  202. package/dist/rules/string-utils.js.map +1 -0
  203. package/dist/{src/rules → rules}/svg-tag-name-capitalization.js +7 -6
  204. package/dist/rules/svg-tag-name-capitalization.js.map +1 -0
  205. package/dist/rules/turbo-permanent-require-id.js +34 -0
  206. package/dist/rules/turbo-permanent-require-id.js.map +1 -0
  207. package/dist/{src/rules.js → rules.js} +62 -10
  208. package/dist/rules.js.map +1 -0
  209. package/dist/types/cli/argument-parser.d.ts +1 -0
  210. package/dist/types/cli/file-processor.d.ts +13 -0
  211. package/dist/types/cli/file-url.d.ts +1 -0
  212. package/dist/types/cli/index.d.ts +1 -0
  213. package/dist/types/cli/lint-worker.d.ts +34 -0
  214. package/dist/types/custom-rule-loader.d.ts +4 -0
  215. package/dist/types/index.d.ts +2 -0
  216. package/dist/types/linter.d.ts +13 -6
  217. package/dist/types/parse-cache.d.ts +9 -0
  218. package/dist/types/{src/rules/html-aria-level-must-be-valid.d.ts → rules/actionview-no-silent-helper.d.ts} +4 -3
  219. package/dist/types/rules/actionview-no-silent-render.d.ts +9 -0
  220. package/dist/types/rules/erb-comment-syntax.d.ts +1 -1
  221. package/dist/types/rules/erb-no-case-node-children.d.ts +1 -1
  222. package/dist/types/{src/rules/herb-disable-comment-malformed.d.ts → rules/erb-no-conditional-html-element.d.ts} +3 -3
  223. package/dist/types/{src/rules/erb-prefer-image-tag-helper.d.ts → rules/erb-no-conditional-open-tag.d.ts} +3 -3
  224. package/dist/types/rules/erb-no-duplicate-branch-elements.d.ts +18 -0
  225. package/dist/types/{src/rules/html-anchor-require-href.d.ts → rules/erb-no-empty-control-flow.d.ts} +2 -2
  226. package/dist/types/rules/erb-no-empty-tags.d.ts +1 -1
  227. package/dist/types/rules/erb-no-extra-newline.d.ts +1 -1
  228. package/dist/types/rules/erb-no-extra-whitespace-inside-tags.d.ts +1 -1
  229. package/dist/types/{src/rules/html-no-duplicate-attributes.d.ts → rules/erb-no-inline-case-conditions.d.ts} +4 -3
  230. package/dist/types/rules/erb-no-instance-variables-in-partials.d.ts +10 -0
  231. package/dist/types/{src/rules/html-no-aria-hidden-on-focusable.d.ts → rules/erb-no-interpolated-class-names.d.ts} +2 -2
  232. package/dist/types/{src/rules/html-aria-attribute-must-be-valid.d.ts → rules/erb-no-javascript-tag-helper.d.ts} +2 -2
  233. package/dist/types/rules/erb-no-output-control-flow.d.ts +1 -1
  234. package/dist/types/{src/rules/erb-no-silent-tag-in-attribute-name.d.ts → rules/erb-no-output-in-attribute-name.d.ts} +2 -2
  235. package/dist/types/{src/rules/herb-disable-comment-missing-rules.d.ts → rules/erb-no-output-in-attribute-position.d.ts} +2 -2
  236. package/dist/types/{src/rules/erb-no-empty-tags.d.ts → rules/erb-no-raw-output-in-attribute-value.d.ts} +2 -2
  237. package/dist/types/{src/rules/html-no-title-attribute.d.ts → rules/erb-no-silent-statement.d.ts} +4 -3
  238. package/dist/types/rules/erb-no-silent-tag-in-attribute-name.d.ts +1 -1
  239. package/dist/types/{src/rules/html-navigation-has-label.d.ts → rules/erb-no-statement-in-script.d.ts} +2 -2
  240. package/dist/types/rules/erb-no-then-in-control-flow.d.ts +9 -0
  241. package/dist/types/rules/erb-no-trailing-whitespace.d.ts +19 -0
  242. package/dist/types/{src/rules/html-no-positive-tab-index.d.ts → rules/erb-no-unsafe-js-attribute.d.ts} +2 -2
  243. package/dist/types/{src/rules/erb-no-case-node-children.d.ts → rules/erb-no-unsafe-raw.d.ts} +2 -2
  244. package/dist/types/rules/erb-no-unsafe-script-interpolation.d.ts +9 -0
  245. package/dist/types/rules/erb-prefer-image-tag-helper.d.ts +1 -1
  246. package/dist/types/rules/erb-require-trailing-newline.d.ts +1 -1
  247. package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +1 -1
  248. package/dist/types/rules/erb-right-trim.d.ts +1 -1
  249. package/dist/types/rules/erb-strict-locals-comment-syntax.d.ts +1 -1
  250. package/dist/types/rules/erb-strict-locals-required.d.ts +1 -1
  251. package/dist/types/rules/herb-disable-comment-malformed.d.ts +1 -1
  252. package/dist/types/rules/herb-disable-comment-missing-rules.d.ts +1 -1
  253. package/dist/types/rules/herb-disable-comment-no-duplicate-rules.d.ts +1 -1
  254. package/dist/types/rules/herb-disable-comment-no-redundant-all.d.ts +1 -1
  255. package/dist/types/rules/herb-disable-comment-unnecessary.d.ts +1 -1
  256. package/dist/types/rules/herb-disable-comment-valid-rule-name.d.ts +1 -1
  257. package/dist/types/{src/rules/html-no-empty-attributes.d.ts → rules/html-allowed-script-type.d.ts} +2 -2
  258. package/dist/types/rules/html-anchor-require-href.d.ts +3 -2
  259. package/dist/types/rules/html-aria-attribute-must-be-valid.d.ts +1 -1
  260. package/dist/types/rules/html-aria-label-is-well-formatted.d.ts +1 -1
  261. package/dist/types/rules/html-aria-level-must-be-valid.d.ts +1 -1
  262. package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +1 -1
  263. package/dist/types/rules/html-aria-role-must-be-valid.d.ts +1 -1
  264. package/dist/types/rules/html-attribute-double-quotes.d.ts +1 -1
  265. package/dist/types/rules/html-attribute-equals-spacing.d.ts +1 -1
  266. package/dist/types/rules/html-attribute-values-require-quotes.d.ts +1 -1
  267. package/dist/types/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +1 -1
  268. package/dist/types/rules/html-body-only-elements.d.ts +1 -1
  269. package/dist/types/rules/html-boolean-attributes-no-value.d.ts +1 -1
  270. package/dist/types/rules/html-details-has-summary.d.ts +9 -0
  271. package/dist/types/rules/html-head-only-elements.d.ts +1 -1
  272. package/dist/types/rules/html-iframe-has-title.d.ts +1 -1
  273. package/dist/types/rules/html-img-require-alt.d.ts +1 -1
  274. package/dist/types/rules/html-input-require-autocomplete.d.ts +1 -1
  275. package/dist/types/rules/html-navigation-has-label.d.ts +1 -1
  276. package/dist/types/{src/rules/html-no-empty-headings.d.ts → rules/html-no-abstract-roles.d.ts} +2 -2
  277. package/dist/types/{src/rules/erb-no-output-control-flow.d.ts → rules/html-no-aria-hidden-on-body.d.ts} +3 -3
  278. package/dist/types/rules/html-no-aria-hidden-on-focusable.d.ts +1 -1
  279. package/dist/types/rules/html-no-block-inside-inline.d.ts +1 -1
  280. package/dist/types/rules/html-no-duplicate-attributes.d.ts +1 -1
  281. package/dist/types/rules/html-no-duplicate-ids.d.ts +1 -1
  282. package/dist/types/rules/html-no-duplicate-meta-names.d.ts +1 -1
  283. package/dist/types/rules/html-no-empty-attributes.d.ts +1 -1
  284. package/dist/types/rules/html-no-empty-headings.d.ts +1 -1
  285. package/dist/types/rules/html-no-nested-links.d.ts +1 -1
  286. package/dist/types/rules/html-no-positive-tab-index.d.ts +1 -1
  287. package/dist/types/rules/html-no-self-closing.d.ts +1 -1
  288. package/dist/types/rules/html-no-space-in-tag.d.ts +1 -1
  289. package/dist/types/rules/html-no-title-attribute.d.ts +1 -1
  290. package/dist/types/rules/html-no-underscores-in-attribute-names.d.ts +1 -1
  291. package/dist/types/{src/rules/html-body-only-elements.d.ts → rules/html-require-closing-tags.d.ts} +4 -3
  292. package/dist/types/rules/html-tag-name-lowercase.d.ts +1 -1
  293. package/dist/types/rules/index.d.ts +27 -4
  294. package/dist/types/rules/parser-no-errors.d.ts +1 -1
  295. package/dist/types/rules/rule-utils.d.ts +36 -88
  296. package/dist/types/rules/svg-tag-name-capitalization.d.ts +1 -1
  297. package/dist/types/{src/rules/html-aria-role-must-be-valid.d.ts → rules/turbo-permanent-require-id.d.ts} +2 -2
  298. package/dist/types/types.d.ts +26 -7
  299. package/dist/types/urls.d.ts +1 -0
  300. package/dist/{src/types.js → types.js} +56 -0
  301. package/dist/types.js.map +1 -0
  302. package/dist/urls.js +5 -0
  303. package/dist/urls.js.map +1 -0
  304. package/docs/rules/README.md +26 -2
  305. package/docs/rules/actionview-no-silent-helper.md +57 -0
  306. package/docs/rules/actionview-no-silent-render.md +47 -0
  307. package/docs/rules/erb-no-conditional-html-element.md +90 -0
  308. package/docs/rules/erb-no-conditional-open-tag.md +130 -0
  309. package/docs/rules/erb-no-duplicate-branch-elements.md +98 -0
  310. package/docs/rules/erb-no-empty-control-flow.md +83 -0
  311. package/docs/rules/erb-no-inline-case-conditions.md +85 -0
  312. package/docs/rules/erb-no-instance-variables-in-partials.md +43 -0
  313. package/docs/rules/erb-no-interpolated-class-names.md +57 -0
  314. package/docs/rules/erb-no-javascript-tag-helper.md +33 -0
  315. package/docs/rules/erb-no-output-in-attribute-name.md +38 -0
  316. package/docs/rules/erb-no-output-in-attribute-position.md +60 -0
  317. package/docs/rules/erb-no-raw-output-in-attribute-value.md +37 -0
  318. package/docs/rules/erb-no-silent-statement.md +53 -0
  319. package/docs/rules/erb-no-statement-in-script.md +68 -0
  320. package/docs/rules/erb-no-then-in-control-flow.md +86 -0
  321. package/docs/rules/erb-no-trailing-whitespace.md +69 -0
  322. package/docs/rules/erb-no-unsafe-js-attribute.md +41 -0
  323. package/docs/rules/erb-no-unsafe-raw.md +47 -0
  324. package/docs/rules/erb-no-unsafe-script-interpolation.md +140 -0
  325. package/docs/rules/html-allowed-script-type.md +59 -0
  326. package/docs/rules/html-anchor-require-href.md +19 -6
  327. package/docs/rules/html-details-has-summary.md +46 -0
  328. package/docs/rules/html-img-require-alt.md +5 -3
  329. package/docs/rules/html-no-abstract-roles.md +74 -0
  330. package/docs/rules/html-no-aria-hidden-on-body.md +44 -0
  331. package/docs/rules/html-require-closing-tags.md +142 -0
  332. package/docs/rules/parser-no-errors.md +4 -17
  333. package/docs/rules/turbo-permanent-require-id.md +41 -0
  334. package/package.json +11 -10
  335. package/src/cli/argument-parser.ts +20 -2
  336. package/src/cli/file-processor.ts +189 -10
  337. package/src/cli/file-url.ts +6 -0
  338. package/src/cli/formatters/detailed-formatter.ts +19 -21
  339. package/src/cli/formatters/simple-formatter.ts +23 -13
  340. package/src/cli/index.ts +2 -0
  341. package/src/cli/lint-worker.ts +208 -0
  342. package/src/cli/summary-reporter.ts +14 -15
  343. package/src/cli.ts +5 -3
  344. package/src/custom-rule-loader.ts +20 -5
  345. package/src/herb-disable-comment-utils.ts +0 -3
  346. package/src/index.ts +22 -0
  347. package/src/linter.ts +98 -79
  348. package/src/parse-cache.ts +39 -0
  349. package/src/rules/actionview-no-silent-helper.ts +58 -0
  350. package/src/rules/actionview-no-silent-render.ts +44 -0
  351. package/src/rules/erb-comment-syntax.ts +2 -2
  352. package/src/rules/erb-no-case-node-children.ts +5 -3
  353. package/src/rules/erb-no-conditional-html-element.ts +53 -0
  354. package/src/rules/erb-no-conditional-open-tag.ts +37 -0
  355. package/src/rules/erb-no-duplicate-branch-elements.ts +436 -0
  356. package/src/rules/erb-no-empty-control-flow.ts +255 -0
  357. package/src/rules/erb-no-empty-tags.ts +2 -2
  358. package/src/rules/erb-no-extra-newline.ts +5 -25
  359. package/src/rules/erb-no-extra-whitespace-inside-tags.ts +45 -15
  360. package/src/rules/erb-no-inline-case-conditions.ts +54 -0
  361. package/src/rules/erb-no-instance-variables-in-partials.ts +101 -0
  362. package/src/rules/erb-no-interpolated-class-names.ts +65 -0
  363. package/src/rules/erb-no-javascript-tag-helper.ts +47 -0
  364. package/src/rules/erb-no-output-control-flow.ts +10 -10
  365. package/src/rules/erb-no-output-in-attribute-name.ts +39 -0
  366. package/src/rules/erb-no-output-in-attribute-position.ts +39 -0
  367. package/src/rules/erb-no-raw-output-in-attribute-value.ts +47 -0
  368. package/src/rules/erb-no-silent-statement.ts +58 -0
  369. package/src/rules/erb-no-silent-tag-in-attribute-name.ts +2 -2
  370. package/src/rules/erb-no-statement-in-script.ts +82 -0
  371. package/src/rules/erb-no-then-in-control-flow.ts +62 -0
  372. package/src/rules/erb-no-trailing-whitespace.ts +187 -0
  373. package/src/rules/erb-no-unsafe-js-attribute.ts +47 -0
  374. package/src/rules/erb-no-unsafe-raw.ts +83 -0
  375. package/src/rules/erb-no-unsafe-script-interpolation.ts +122 -0
  376. package/src/rules/erb-prefer-image-tag-helper.ts +5 -4
  377. package/src/rules/erb-require-trailing-newline.ts +2 -2
  378. package/src/rules/erb-require-whitespace-inside-tags.ts +42 -18
  379. package/src/rules/erb-right-trim.ts +2 -2
  380. package/src/rules/erb-strict-locals-comment-syntax.ts +4 -4
  381. package/src/rules/erb-strict-locals-required.ts +2 -2
  382. package/src/rules/herb-disable-comment-malformed.ts +2 -2
  383. package/src/rules/herb-disable-comment-missing-rules.ts +2 -2
  384. package/src/rules/herb-disable-comment-no-duplicate-rules.ts +2 -2
  385. package/src/rules/herb-disable-comment-no-redundant-all.ts +2 -2
  386. package/src/rules/herb-disable-comment-unnecessary.ts +2 -2
  387. package/src/rules/herb-disable-comment-valid-rule-name.ts +2 -2
  388. package/src/rules/html-allowed-script-type.ts +84 -0
  389. package/src/rules/html-anchor-require-href.ts +73 -11
  390. package/src/rules/html-aria-attribute-must-be-valid.ts +3 -3
  391. package/src/rules/html-aria-label-is-well-formatted.ts +3 -3
  392. package/src/rules/html-aria-level-must-be-valid.ts +3 -3
  393. package/src/rules/html-aria-role-heading-requires-level.ts +5 -4
  394. package/src/rules/html-aria-role-must-be-valid.ts +3 -3
  395. package/src/rules/html-attribute-double-quotes.ts +4 -4
  396. package/src/rules/html-attribute-equals-spacing.ts +2 -2
  397. package/src/rules/html-attribute-values-require-quotes.ts +2 -2
  398. package/src/rules/html-avoid-both-disabled-and-aria-disabled.ts +10 -11
  399. package/src/rules/html-body-only-elements.ts +5 -4
  400. package/src/rules/html-boolean-attributes-no-value.ts +4 -3
  401. package/src/rules/html-details-has-summary.ts +69 -0
  402. package/src/rules/html-head-only-elements.ts +6 -5
  403. package/src/rules/html-iframe-has-title.ts +8 -11
  404. package/src/rules/html-img-require-alt.ts +16 -5
  405. package/src/rules/html-input-require-autocomplete.ts +7 -10
  406. package/src/rules/html-navigation-has-label.ts +6 -5
  407. package/src/rules/html-no-abstract-roles.ts +40 -0
  408. package/src/rules/html-no-aria-hidden-on-body.ts +58 -0
  409. package/src/rules/html-no-aria-hidden-on-focusable.ts +6 -5
  410. package/src/rules/html-no-block-inside-inline.ts +7 -13
  411. package/src/rules/html-no-duplicate-attributes.ts +4 -3
  412. package/src/rules/html-no-duplicate-ids.ts +16 -13
  413. package/src/rules/html-no-duplicate-meta-names.ts +20 -19
  414. package/src/rules/html-no-empty-attributes.ts +2 -2
  415. package/src/rules/html-no-empty-headings.ts +44 -58
  416. package/src/rules/html-no-nested-links.ts +25 -16
  417. package/src/rules/html-no-positive-tab-index.ts +3 -3
  418. package/src/rules/html-no-self-closing.ts +5 -5
  419. package/src/rules/html-no-space-in-tag.ts +5 -8
  420. package/src/rules/html-no-title-attribute.ts +6 -5
  421. package/src/rules/html-no-underscores-in-attribute-names.ts +2 -2
  422. package/src/rules/html-require-closing-tags.ts +41 -0
  423. package/src/rules/html-tag-name-lowercase.ts +14 -9
  424. package/src/rules/index.ts +28 -4
  425. package/src/rules/parser-no-errors.ts +3 -3
  426. package/src/rules/rule-utils.ts +166 -279
  427. package/src/rules/svg-tag-name-capitalization.ts +10 -10
  428. package/src/rules/turbo-permanent-require-id.ts +49 -0
  429. package/src/rules.ts +66 -10
  430. package/src/types.ts +80 -7
  431. package/src/urls.ts +5 -0
  432. package/dist/package.json +0 -65
  433. package/dist/src/cli/argument-parser.js.map +0 -1
  434. package/dist/src/cli/file-processor.js.map +0 -1
  435. package/dist/src/cli/formatters/base-formatter.js.map +0 -1
  436. package/dist/src/cli/formatters/detailed-formatter.js.map +0 -1
  437. package/dist/src/cli/formatters/github-actions-formatter.js.map +0 -1
  438. package/dist/src/cli/formatters/index.js.map +0 -1
  439. package/dist/src/cli/formatters/json-formatter.js.map +0 -1
  440. package/dist/src/cli/formatters/simple-formatter.js +0 -44
  441. package/dist/src/cli/formatters/simple-formatter.js.map +0 -1
  442. package/dist/src/cli/index.js.map +0 -1
  443. package/dist/src/cli/output-manager.js.map +0 -1
  444. package/dist/src/cli/summary-reporter.js.map +0 -1
  445. package/dist/src/cli.js.map +0 -1
  446. package/dist/src/custom-rule-loader.js.map +0 -1
  447. package/dist/src/herb-disable-comment-utils.js.map +0 -1
  448. package/dist/src/herb-lint.js +0 -5
  449. package/dist/src/herb-lint.js.map +0 -1
  450. package/dist/src/index.js +0 -5
  451. package/dist/src/index.js.map +0 -1
  452. package/dist/src/linter-ignore.js.map +0 -1
  453. package/dist/src/linter.js.map +0 -1
  454. package/dist/src/loader.js +0 -17
  455. package/dist/src/loader.js.map +0 -1
  456. package/dist/src/rules/erb-comment-syntax.js.map +0 -1
  457. package/dist/src/rules/erb-no-case-node-children.js.map +0 -1
  458. package/dist/src/rules/erb-no-empty-tags.js.map +0 -1
  459. package/dist/src/rules/erb-no-extra-newline.js.map +0 -1
  460. package/dist/src/rules/erb-no-extra-whitespace-inside-tags.js.map +0 -1
  461. package/dist/src/rules/erb-no-output-control-flow.js.map +0 -1
  462. package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js.map +0 -1
  463. package/dist/src/rules/erb-prefer-image-tag-helper.js.map +0 -1
  464. package/dist/src/rules/erb-require-trailing-newline.js.map +0 -1
  465. package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +0 -1
  466. package/dist/src/rules/erb-right-trim.js.map +0 -1
  467. package/dist/src/rules/erb-strict-locals-comment-syntax.js.map +0 -1
  468. package/dist/src/rules/erb-strict-locals-required.js.map +0 -1
  469. package/dist/src/rules/file-utils.js.map +0 -1
  470. package/dist/src/rules/herb-disable-comment-base.js.map +0 -1
  471. package/dist/src/rules/herb-disable-comment-malformed.js.map +0 -1
  472. package/dist/src/rules/herb-disable-comment-missing-rules.js.map +0 -1
  473. package/dist/src/rules/herb-disable-comment-no-duplicate-rules.js.map +0 -1
  474. package/dist/src/rules/herb-disable-comment-no-redundant-all.js.map +0 -1
  475. package/dist/src/rules/herb-disable-comment-unnecessary.js.map +0 -1
  476. package/dist/src/rules/herb-disable-comment-valid-rule-name.js.map +0 -1
  477. package/dist/src/rules/html-anchor-require-href.js +0 -32
  478. package/dist/src/rules/html-anchor-require-href.js.map +0 -1
  479. package/dist/src/rules/html-aria-attribute-must-be-valid.js.map +0 -1
  480. package/dist/src/rules/html-aria-label-is-well-formatted.js.map +0 -1
  481. package/dist/src/rules/html-aria-level-must-be-valid.js.map +0 -1
  482. package/dist/src/rules/html-aria-role-heading-requires-level.js.map +0 -1
  483. package/dist/src/rules/html-aria-role-must-be-valid.js.map +0 -1
  484. package/dist/src/rules/html-attribute-double-quotes.js.map +0 -1
  485. package/dist/src/rules/html-attribute-equals-spacing.js.map +0 -1
  486. package/dist/src/rules/html-attribute-values-require-quotes.js.map +0 -1
  487. package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js.map +0 -1
  488. package/dist/src/rules/html-body-only-elements.js.map +0 -1
  489. package/dist/src/rules/html-boolean-attributes-no-value.js.map +0 -1
  490. package/dist/src/rules/html-head-only-elements.js.map +0 -1
  491. package/dist/src/rules/html-iframe-has-title.js.map +0 -1
  492. package/dist/src/rules/html-img-require-alt.js.map +0 -1
  493. package/dist/src/rules/html-input-require-autocomplete.js.map +0 -1
  494. package/dist/src/rules/html-navigation-has-label.js.map +0 -1
  495. package/dist/src/rules/html-no-aria-hidden-on-focusable.js.map +0 -1
  496. package/dist/src/rules/html-no-block-inside-inline.js.map +0 -1
  497. package/dist/src/rules/html-no-duplicate-attributes.js.map +0 -1
  498. package/dist/src/rules/html-no-duplicate-ids.js.map +0 -1
  499. package/dist/src/rules/html-no-duplicate-meta-names.js.map +0 -1
  500. package/dist/src/rules/html-no-empty-attributes.js.map +0 -1
  501. package/dist/src/rules/html-no-empty-headings.js +0 -115
  502. package/dist/src/rules/html-no-empty-headings.js.map +0 -1
  503. package/dist/src/rules/html-no-nested-links.js.map +0 -1
  504. package/dist/src/rules/html-no-positive-tab-index.js.map +0 -1
  505. package/dist/src/rules/html-no-self-closing.js.map +0 -1
  506. package/dist/src/rules/html-no-space-in-tag.js.map +0 -1
  507. package/dist/src/rules/html-no-title-attribute.js.map +0 -1
  508. package/dist/src/rules/html-no-underscores-in-attribute-names.js.map +0 -1
  509. package/dist/src/rules/html-tag-name-lowercase.js.map +0 -1
  510. package/dist/src/rules/index.js.map +0 -1
  511. package/dist/src/rules/parser-no-errors.js.map +0 -1
  512. package/dist/src/rules/rule-utils.js.map +0 -1
  513. package/dist/src/rules/string-utils.js.map +0 -1
  514. package/dist/src/rules/svg-tag-name-capitalization.js.map +0 -1
  515. package/dist/src/rules.js.map +0 -1
  516. package/dist/src/types.js.map +0 -1
  517. package/dist/tsconfig.tsbuildinfo +0 -1
  518. package/dist/types/src/cli/argument-parser.d.ts +0 -25
  519. package/dist/types/src/cli/file-processor.d.ts +0 -43
  520. package/dist/types/src/cli/formatters/base-formatter.d.ts +0 -6
  521. package/dist/types/src/cli/formatters/detailed-formatter.d.ts +0 -13
  522. package/dist/types/src/cli/formatters/github-actions-formatter.d.ts +0 -17
  523. package/dist/types/src/cli/formatters/index.d.ts +0 -5
  524. package/dist/types/src/cli/formatters/json-formatter.d.ts +0 -48
  525. package/dist/types/src/cli/formatters/simple-formatter.d.ts +0 -8
  526. package/dist/types/src/cli/index.d.ts +0 -5
  527. package/dist/types/src/cli/output-manager.d.ts +0 -32
  528. package/dist/types/src/cli/summary-reporter.d.ts +0 -28
  529. package/dist/types/src/cli.d.ts +0 -28
  530. package/dist/types/src/custom-rule-loader.d.ts +0 -62
  531. package/dist/types/src/herb-disable-comment-utils.d.ts +0 -69
  532. package/dist/types/src/herb-lint.d.ts +0 -2
  533. package/dist/types/src/index.d.ts +0 -4
  534. package/dist/types/src/linter-ignore.d.ts +0 -12
  535. package/dist/types/src/linter.d.ts +0 -133
  536. package/dist/types/src/loader.d.ts +0 -20
  537. package/dist/types/src/rules/erb-comment-syntax.d.ts +0 -14
  538. package/dist/types/src/rules/erb-no-extra-newline.d.ts +0 -14
  539. package/dist/types/src/rules/erb-no-extra-whitespace-inside-tags.d.ts +0 -18
  540. package/dist/types/src/rules/erb-require-trailing-newline.d.ts +0 -9
  541. package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +0 -18
  542. package/dist/types/src/rules/erb-right-trim.d.ts +0 -14
  543. package/dist/types/src/rules/erb-strict-locals-comment-syntax.d.ts +0 -9
  544. package/dist/types/src/rules/erb-strict-locals-required.d.ts +0 -9
  545. package/dist/types/src/rules/file-utils.d.ts +0 -13
  546. package/dist/types/src/rules/herb-disable-comment-base.d.ts +0 -37
  547. package/dist/types/src/rules/herb-disable-comment-no-duplicate-rules.d.ts +0 -8
  548. package/dist/types/src/rules/herb-disable-comment-no-redundant-all.d.ts +0 -8
  549. package/dist/types/src/rules/herb-disable-comment-unnecessary.d.ts +0 -8
  550. package/dist/types/src/rules/herb-disable-comment-valid-rule-name.d.ts +0 -8
  551. package/dist/types/src/rules/html-aria-label-is-well-formatted.d.ts +0 -8
  552. package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +0 -8
  553. package/dist/types/src/rules/html-attribute-double-quotes.d.ts +0 -15
  554. package/dist/types/src/rules/html-attribute-equals-spacing.d.ts +0 -14
  555. package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +0 -15
  556. package/dist/types/src/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +0 -8
  557. package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +0 -14
  558. package/dist/types/src/rules/html-head-only-elements.d.ts +0 -9
  559. package/dist/types/src/rules/html-iframe-has-title.d.ts +0 -8
  560. package/dist/types/src/rules/html-img-require-alt.d.ts +0 -8
  561. package/dist/types/src/rules/html-input-require-autocomplete.d.ts +0 -8
  562. package/dist/types/src/rules/html-no-block-inside-inline.d.ts +0 -8
  563. package/dist/types/src/rules/html-no-duplicate-ids.d.ts +0 -8
  564. package/dist/types/src/rules/html-no-duplicate-meta-names.d.ts +0 -9
  565. package/dist/types/src/rules/html-no-nested-links.d.ts +0 -8
  566. package/dist/types/src/rules/html-no-self-closing.d.ts +0 -16
  567. package/dist/types/src/rules/html-no-space-in-tag.d.ts +0 -16
  568. package/dist/types/src/rules/html-no-underscores-in-attribute-names.d.ts +0 -8
  569. package/dist/types/src/rules/html-tag-name-lowercase.d.ts +0 -18
  570. package/dist/types/src/rules/index.d.ts +0 -54
  571. package/dist/types/src/rules/parser-no-errors.d.ts +0 -9
  572. package/dist/types/src/rules/rule-utils.d.ts +0 -351
  573. package/dist/types/src/rules/string-utils.d.ts +0 -15
  574. package/dist/types/src/rules/svg-tag-name-capitalization.d.ts +0 -16
  575. package/dist/types/src/rules.d.ts +0 -2
  576. package/dist/types/src/types.d.ts +0 -190
  577. /package/dist/{src/cli → cli}/formatters/base-formatter.js +0 -0
  578. /package/dist/{src/cli → cli}/formatters/github-actions-formatter.js +0 -0
  579. /package/dist/{src/cli → cli}/formatters/index.js +0 -0
  580. /package/dist/{src/cli → cli}/formatters/json-formatter.js +0 -0
  581. /package/dist/{src/cli → cli}/index.js +0 -0
  582. /package/dist/{src/cli → cli}/output-manager.js +0 -0
  583. /package/dist/{src/herb-disable-comment-utils.js → herb-disable-comment-utils.js} +0 -0
  584. /package/dist/{src/linter-ignore.js → linter-ignore.js} +0 -0
  585. /package/dist/{src/rules → rules}/file-utils.js +0 -0
  586. /package/dist/{src/rules → rules}/herb-disable-comment-base.js +0 -0
  587. /package/dist/{src/rules → rules}/string-utils.js +0 -0
@@ -4,11 +4,11 @@
4
4
 
5
5
  ## Description
6
6
 
7
- Disallow the use of anchor tags without an `href` attribute in HTML templates. Use if you want to perform an action without having the user navigated to a new URL.
7
+ An `<a>` element that has an `href` attribute represents a hyperlink (a hypertext anchor) labeled by its contents. Links should go somewhere. If you want to perform an action without navigating the user to a new URL, use a `<button>` instead.
8
8
 
9
9
  ## Rationale
10
10
 
11
- Anchor tags without href are unfocusable if user is using keyboard navigation, or is unseen by screen readers.
11
+ Anchor tags without an `href` attribute are not focusable via keyboard navigation and are not visible to screen readers. This makes them inaccessible to users who rely on assistive technologies.
12
12
 
13
13
  ## Examples
14
14
 
@@ -20,13 +20,26 @@ Anchor tags without href are unfocusable if user is using keyboard navigation, o
20
20
 
21
21
  ### 🚫 Bad
22
22
 
23
+ ```erb
24
+ <a>Go to Page</a>
25
+ ```
26
+
27
+ ```erb
28
+ <a href="#">Go to Page</a>
29
+ ```
30
+
23
31
  ```erb
24
32
  <a data-action="click->doSomething">I'm a fake link</a>
25
33
  ```
26
34
 
27
35
  ## References
28
36
 
29
- * https://marcysutton.com/links-vs-buttons-in-modern-web-applications
30
- * https://a11y-101.com/design/button-vs-link
31
- * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role
32
- * https://www.scottohara.me/blog/2021/05/28/disabled-links.html#w3c/html-aria#305
37
+ * [MDN: The Anchor element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
38
+ * [HTML Spec: The `a` element](https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element)
39
+ * [WAI-ARIA 1.2: `link` role](https://www.w3.org/TR/wai-aria-1.2/#link)
40
+ * [Primer: Links](https://primer.style/design/accessibility/links)
41
+ * [Links vs. Buttons in Modern Web Applications](https://marcysutton.com/links-vs-buttons-in-modern-web-applications)
42
+ * [Button vs. Link](https://a11y-101.com/design/button-vs-link)
43
+ * [MDN: ARIA button role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role)
44
+ * [Disabled Links](https://www.scottohara.me/blog/2021/05/28/disabled-links.html)
45
+ * [`erblint-github`: `LinkHasHref`](https://github.com/github/erblint-github/blob/main/docs/rules/accessibility/link-has-href.md)
@@ -0,0 +1,46 @@
1
+ # Linter Rule: `<details>` elements must have a `<summary>` child
2
+
3
+ **Rule:** `html-details-has-summary`
4
+
5
+ ## Description
6
+
7
+ Ensure that all `<details>` elements have a direct `<summary>` child element that describes what the disclosure widget will expand.
8
+
9
+ ## Rationale
10
+
11
+ The `<summary>` element provides a visible label for the `<details>` disclosure widget, hinting to the user what they'll be expanding. Screen reader users rely on `<summary>` elements to understand the purpose of the expandable content. If a developer omits the `<summary>`, the user agent adds a default one with no meaningful context. The `<summary>` must be a direct child of `<details>` to function correctly — nesting it inside another element will not work as intended.
12
+
13
+ ## Examples
14
+
15
+ ### ✅ Good
16
+
17
+ ```erb
18
+ <details>
19
+ <summary>Expand me!</summary>
20
+ I do have a summary tag!
21
+ </details>
22
+
23
+ <details>
24
+ I do have a summary tag!
25
+ <summary>Expand me!</summary>
26
+ </details>
27
+ ```
28
+
29
+ ### 🚫 Bad
30
+
31
+ ```erb
32
+ <details>
33
+ I don't have a summary tag!
34
+ </details>
35
+
36
+ <details>
37
+ <div><summary>Expand me!</summary></div>
38
+ The summary tag needs to be a direct child of the details tag.
39
+ </details>
40
+ ```
41
+
42
+ ## References
43
+
44
+ - [HTML: `details` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
45
+ - [HTML: `summary` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
46
+ - [erblint-github: GitHub::Accessibility::DetailsHasSummary](https://github.com/github/erblint-github/pull/23)
@@ -31,12 +31,14 @@ Omitting the `alt` attribute entirely leads to poor accessibility and can negati
31
31
  ```erb
32
32
  <img src="/logo.png">
33
33
 
34
- <img src="/avatar.jpg" alt> <!-- TODO -->
34
+ <img src="/avatar.jpg" alt>
35
35
 
36
36
  <%= image_tag image_path("logo.png") %> <!-- TODO -->
37
37
  ```
38
38
 
39
39
  ## References
40
40
 
41
- * [W3C: Alternative Text](https://www.w3.org/WAI/tutorials/images/)
42
- * [WCAG 2.1: Non-text Content](https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1#non-text-content)
41
+ - [W3C WAI Images Tutorial](https://www.w3.org/WAI/tutorials/images/)
42
+ - [WCAG 2.1: Non-text Content](https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1#non-text-content)
43
+ - [Primer: Alternative text for images](https://primer.style/accessibility/design-guidance/alternative-text-for-images/)
44
+ - [erblint-github: `GitHub::Accessibility::ImageHasAlt`](https://github.com/github/erblint-github/blob/main/docs/rules/accessibility/image-has-alt.md)
@@ -0,0 +1,74 @@
1
+ # Linter Rule: No abstract ARIA roles
2
+
3
+ **Rule:** `html-no-abstract-roles`
4
+
5
+ ## Description
6
+
7
+ Prevent usage of WAI-ARIA abstract roles in the `role` attribute.
8
+
9
+ ## Rationale
10
+
11
+ The WAI-ARIA specification defines a set of abstract roles that are used to support the ARIA Roles Model for the purpose of defining general role concepts.
12
+
13
+ Abstract roles are used for the ontology only. They exist to help organize the hierarchy of roles and define shared characteristics, but they are not meant to be used by authors directly. Using abstract roles in content provides no semantic meaning to assistive technologies and can lead to accessibility issues.
14
+
15
+ Authors **MUST NOT** use abstract roles in content. Instead, use one of the concrete roles that inherit from these abstract roles. For example, use `button` instead of `command`, or `navigation` instead of `landmark`.
16
+
17
+ The following abstract roles must not be used:
18
+
19
+ - `command`
20
+ - `composite`
21
+ - `input`
22
+ - `landmark`
23
+ - `range`
24
+ - `roletype`
25
+ - `section`
26
+ - `sectionhead`
27
+ - `select`
28
+ - `structure`
29
+ - `widget`
30
+ - `window`
31
+
32
+ ## Examples
33
+
34
+ ### ✅ Good
35
+
36
+ ```erb
37
+ <div role="button">Push it</div>
38
+ ```
39
+
40
+ ```erb
41
+ <nav role="navigation">Menu</nav>
42
+ ```
43
+
44
+ ```erb
45
+ <div role="alert">Warning!</div>
46
+ ```
47
+
48
+ ```erb
49
+ <div role="slider" aria-valuenow="50">Volume</div>
50
+ ```
51
+
52
+ ### 🚫 Bad
53
+
54
+ ```erb
55
+ <div role="window">Hello, world!</div>
56
+ ```
57
+
58
+ ```erb
59
+ <div role="widget">Content</div>
60
+ ```
61
+
62
+ ```erb
63
+ <div role="command">Action</div>
64
+ ```
65
+
66
+ ```erb
67
+ <div role="landmark">Navigation</div>
68
+ ```
69
+
70
+ ## References
71
+
72
+ - [WAI-ARIA 1.0: Abstract Roles](https://www.w3.org/TR/wai-aria-1.0/roles#abstract_roles)
73
+ - [WAI-ARIA 1.2: Abstract Roles](https://www.w3.org/TR/wai-aria-1.2/#abstract_roles)
74
+ - [ember-template-lint: no-abstract-roles](https://github.com/ember-template-lint/ember-template-lint/blob/main/docs/rule/no-abstract-roles.md)
@@ -0,0 +1,44 @@
1
+ # Linter Rule: No `aria-hidden` on `<body>`
2
+
3
+ **Rule:** `html-no-aria-hidden-on-body`
4
+
5
+ ## Description
6
+
7
+ Prevent usage of `aria-hidden` on `<body>` tags.
8
+
9
+ ## Rationale
10
+
11
+ The `aria-hidden` attribute should never be present on the `<body>` element, as it hides the entire document from assistive technology users. This makes the entire page completely inaccessible to screen reader users, which is a critical accessibility violation.
12
+
13
+ ## Examples
14
+
15
+ ### ✅ Good
16
+
17
+ ```erb
18
+ <body>
19
+ <main>Content</main>
20
+ </body>
21
+
22
+ <body class="app" id="main">
23
+ <main>Content</main>
24
+ </body>
25
+ ```
26
+
27
+ ### 🚫 Bad
28
+
29
+ ```erb
30
+ <body aria-hidden>
31
+ <main>Content</main>
32
+ </body>
33
+
34
+ <body aria-hidden="true">
35
+ <main>Content</main>
36
+ </body>
37
+ ```
38
+
39
+ ## References
40
+
41
+ - [Using the aria-hidden attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute)
42
+ - [How Lighthouse identifies hidden body elements](https://web.dev/aria-hidden-body/)
43
+ - [WCAG 4.1.2 - Name, Role, Value (Level A)](https://www.w3.org/TR/WCAG21/#name-role-value)
44
+ - [ember-template-lint: no-aria-hidden-body](https://github.com/ember-template-lint/ember-template-lint/blob/main/docs/rule/no-aria-hidden-body.md)
@@ -0,0 +1,142 @@
1
+ # Linter Rule: Require explicit closing tags
2
+
3
+ **Rule:** `html-require-closing-tags`
4
+
5
+ ## Description
6
+
7
+ Disallow the omission of optional closing tags for HTML elements where the closing tag is technically optional according to the HTML specification. This rule flags elements that have an `HTMLOmittedCloseTagNode` as their close tag.
8
+
9
+ ## Rationale
10
+
11
+ While HTML allows certain closing tags to be omitted (implicitly closed by sibling elements or parent closing), explicit closing tags improve code readability and maintainability. They make the document structure clear at a glance and reduce ambiguity about where elements end.
12
+
13
+ Explicit closing tags also:
14
+
15
+ - Make templates easier to understand for developers unfamiliar with HTML's implicit closing rules
16
+ - Reduce potential for subtle bugs when refactoring or moving code
17
+ - Improve consistency across the codebase
18
+ - Make diffs cleaner when adding content to elements
19
+
20
+ ## Elements with Optional Closing Tags
21
+
22
+ This rule would flag elements that have omitted closing tags:
23
+
24
+ - `<li>` - list items
25
+ - `<dt>`, `<dd>` - definition list terms and descriptions
26
+ - `<p>` - paragraphs
27
+ - `<option>`, `<optgroup>` - select options and groups
28
+ - `<thead>`, `<tbody>`, `<tfoot>` - table sections
29
+ - `<tr>` - table rows
30
+ - `<td>`, `<th>` - table cells
31
+ - `<colgroup>` - table column groups
32
+ - `<rt>`, `<rp>` - ruby annotations
33
+
34
+ ## Examples
35
+
36
+ ### ✅ Good
37
+
38
+ Explicit closing tags:
39
+
40
+ ```erb
41
+ <ul>
42
+ <li>Item 1</li>
43
+ <li>Item 2</li>
44
+ <li>Item 3</li>
45
+ </ul>
46
+ ```
47
+
48
+ ```erb
49
+ <dl>
50
+ <dt>Term 1</dt>
51
+ <dd>Definition 1</dd>
52
+ <dt>Term 2</dt>
53
+ <dd>Definition 2</dd>
54
+ </dl>
55
+ ```
56
+
57
+ ```erb
58
+ <table>
59
+ <thead>
60
+ <tr>
61
+ <th>Header 1</th>
62
+ <th>Header 2</th>
63
+ </tr>
64
+ </thead>
65
+ <tbody>
66
+ <tr>
67
+ <td>Cell 1</td>
68
+ <td>Cell 2</td>
69
+ </tr>
70
+ </tbody>
71
+ </table>
72
+ ```
73
+
74
+ ```erb
75
+ <select>
76
+ <option>Option 1</option>
77
+ <option>Option 2</option>
78
+ <option>Option 3</option>
79
+ </select>
80
+ ```
81
+
82
+ ```erb
83
+ <div>
84
+ <p>Paragraph 1</p>
85
+ <p>Paragraph 2</p>
86
+ </div>
87
+ ```
88
+
89
+ ### 🚫 Bad
90
+
91
+ Omitted closing tags (implicitly closed):
92
+
93
+ ```erb
94
+ <ul>
95
+ <li>Item 1
96
+ <li>Item 2
97
+ <li>Item 3
98
+ </ul>
99
+ ```
100
+
101
+ ```erb
102
+ <dl>
103
+ <dt>Term 1
104
+ <dd>Definition 1
105
+ <dt>Term 2
106
+ <dd>Definition 2
107
+ </dl>
108
+ ```
109
+
110
+ ```erb
111
+ <table>
112
+ <thead>
113
+ <tr>
114
+ <th>Header 1
115
+ <th>Header 2
116
+ <tbody>
117
+ <tr>
118
+ <td>Cell 1
119
+ <td>Cell 2
120
+ </table>
121
+ ```
122
+
123
+ ```erb
124
+ <select>
125
+ <option>Option 1
126
+ <option>Option 2
127
+ <option>Option 3
128
+ </select>
129
+ ```
130
+
131
+ ```erb
132
+ <div>
133
+ <p>Paragraph 1
134
+ <p>Paragraph 2
135
+ </div>
136
+ ```
137
+
138
+ ## References
139
+
140
+ - [HTML Spec: Optional Tags](https://html.spec.whatwg.org/multipage/syntax.html#optional-tags)
141
+ - [HTML Spec: Tag Omission](https://html.spec.whatwg.org/multipage/dom.html#concept-element-tag-omission)
142
+ - [CSS-Tricks: Fighting the Space Between Inline Block Elements](https://css-tricks.com/fighting-the-space-between-inline-block-elements/)
@@ -39,15 +39,16 @@ By surfacing parser errors through the linter, developers can catch these critic
39
39
  ### 🚫 Bad
40
40
 
41
41
  ```html
42
- <!-- Mismatched closing tag -->
43
42
  <h2>Welcome to our site</h3>
43
+ ```
44
44
 
45
- <!-- Unclosed element -->
45
+ ```html
46
46
  <div>
47
47
  <p>This paragraph is never closed
48
48
  </div>
49
+ ```
49
50
 
50
- <!-- Missing opening tag -->
51
+ ```html
51
52
  Some content
52
53
  </div>
53
54
  ```
@@ -63,20 +64,6 @@ Some content
63
64
  <img src="image.jpg" alt="Description"></img>
64
65
  ```
65
66
 
66
- ## Error Types
67
-
68
- This rule reports various parser error types:
69
-
70
- - **`UNCLOSED_ELEMENT_ERROR`**: Elements that are opened but never closed
71
- - **`MISSING_CLOSING_TAG_ERROR`**: Opening tags without matching closing tags
72
- - **`MISSING_OPENING_TAG_ERROR`**: Closing tags without matching opening tags
73
- - **`TAG_NAMES_MISMATCH_ERROR`**: Opening and closing tags with different names
74
- - **`QUOTES_MISMATCH_ERROR`**: Mismatched quotation marks in attributes
75
- - **`VOID_ELEMENT_CLOSING_TAG_ERROR`**: Void elements (like `<img>`) with closing tags
76
- - **`RUBY_PARSE_ERROR`**: Invalid Ruby syntax within ERB tags
77
- - **`UNEXPECTED_TOKEN_ERROR`**: Unexpected tokens during parsing
78
- - **`UNEXPECTED_ERROR`**: Other unexpected parsing issues
79
-
80
67
  ## References
81
68
 
82
69
  * [HTML Living Standard - Parsing](https://html.spec.whatwg.org/multipage/parsing.html)
@@ -0,0 +1,41 @@
1
+ # Linter Rule: Require `id` attribute on elements with `data-turbo-permanent`
2
+
3
+ **Rule:** `turbo-permanent-require-id`
4
+
5
+ ## Description
6
+
7
+ Ensure that all HTML elements with the `data-turbo-permanent` attribute also have an `id` attribute. Without an `id`, Turbo can't track the element across page changes and the permanent behavior won't work as expected.
8
+
9
+ ## Rationale
10
+
11
+ Turbo's `data-turbo-permanent` attribute marks an element to be persisted across page navigations. Turbo uses the element's `id` to match it between the current page and the new page. If no `id` is present, Turbo has no way to identify and preserve the element, so the `data-turbo-permanent` attribute has no effect.
12
+
13
+ ## Examples
14
+
15
+ ### ✅ Good
16
+
17
+ ```erb
18
+ <div id="player" data-turbo-permanent>
19
+ <!-- This element will persist across page navigations -->
20
+ </div>
21
+
22
+ <audio id="background-music" data-turbo-permanent>
23
+ <source src="/music.mp3" type="audio/mpeg">
24
+ </audio>
25
+ ```
26
+
27
+ ### 🚫 Bad
28
+
29
+ ```erb
30
+ <div data-turbo-permanent>
31
+ <!-- Missing id: Turbo can't track this element -->
32
+ </div>
33
+
34
+ <audio data-turbo-permanent>
35
+ <source src="/music.mp3" type="audio/mpeg">
36
+ </audio>
37
+ ```
38
+
39
+ ## References
40
+
41
+ - [Turbo Handbook: Persisting Elements Across Page Loads](https://turbo.hotwired.dev/handbook/building#persisting-elements-across-page-loads)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herb-tools/linter",
3
- "version": "0.8.10",
3
+ "version": "0.9.1",
4
4
  "description": "HTML+ERB linter for validating HTML structure and enforcing best practices",
5
5
  "license": "MIT",
6
6
  "homepage": "https://herb-tools.dev",
@@ -33,9 +33,10 @@
33
33
  "default": "./dist/index.js"
34
34
  },
35
35
  "./cli": {
36
- "types": "./dist/types/src/cli.d.ts",
37
- "require": "./dist/src/cli.js",
38
- "default": "./dist/src/cli.js"
36
+ "types": "./dist/types/cli.d.ts",
37
+ "import": "./dist/cli.js",
38
+ "require": "./dist/cli.js",
39
+ "default": "./dist/cli.js"
39
40
  },
40
41
  "./loader": {
41
42
  "types": "./dist/types/loader.d.ts",
@@ -45,12 +46,12 @@
45
46
  }
46
47
  },
47
48
  "dependencies": {
48
- "@herb-tools/config": "0.8.10",
49
- "@herb-tools/core": "0.8.10",
50
- "@herb-tools/highlighter": "0.8.10",
51
- "@herb-tools/node-wasm": "0.8.10",
52
- "@herb-tools/printer": "0.8.10",
53
- "@herb-tools/rewriter": "0.8.10",
49
+ "@herb-tools/config": "0.9.1",
50
+ "@herb-tools/core": "0.9.1",
51
+ "@herb-tools/highlighter": "0.9.1",
52
+ "@herb-tools/node-wasm": "0.9.1",
53
+ "@herb-tools/printer": "0.9.1",
54
+ "@herb-tools/rewriter": "0.9.1",
54
55
  "picomatch": "^4.0.2",
55
56
  "tinyglobby": "^0.2.15"
56
57
  },
@@ -1,5 +1,6 @@
1
1
  import dedent from "dedent"
2
2
 
3
+ import { availableParallelism } from "node:os"
3
4
  import { parseArgs } from "util"
4
5
  import { Herb } from "@herb-tools/node-wasm"
5
6
 
@@ -27,6 +28,7 @@ export interface ParsedArguments {
27
28
  init: boolean
28
29
  loadCustomRules: boolean
29
30
  failLevel?: DiagnosticSeverity
31
+ jobs: number
30
32
  }
31
33
 
32
34
  export class ArgumentParser {
@@ -53,6 +55,8 @@ export class ArgumentParser {
53
55
  --github enable GitHub Actions annotations (combines with --format)
54
56
  --no-github disable GitHub Actions annotations (even in GitHub Actions environment)
55
57
  --no-custom-rules disable loading custom rules from project (custom rules are loaded by default from .herb/rules/**/*.{mjs,js})
58
+ -j, --jobs <n> number of parallel workers for linting files [default: auto]
59
+ use "auto" to detect based on available CPU cores
56
60
  --theme syntax highlighting theme (${THEME_NAMES.join("|")}) or path to custom theme file [default: ${DEFAULT_THEME}]
57
61
  --no-color disable colored output
58
62
  --no-timing hide timing information
@@ -83,7 +87,8 @@ export class ArgumentParser {
83
87
  "no-timing": { type: "boolean" },
84
88
  "no-wrap-lines": { type: "boolean" },
85
89
  "truncate-lines": { type: "boolean" },
86
- "no-custom-rules": { type: "boolean" }
90
+ "no-custom-rules": { type: "boolean" },
91
+ jobs: { type: "string", short: "j" }
87
92
  },
88
93
  allowPositionals: true
89
94
  })
@@ -163,7 +168,20 @@ export class ArgumentParser {
163
168
  }
164
169
  }
165
170
 
166
- return { patterns, configFile, formatOption, showTiming, theme, wrapLines, truncateLines, useGitHubActions, fix, fixUnsafe, ignoreDisableComments, force, init, loadCustomRules, failLevel }
171
+ let jobs = availableParallelism()
172
+
173
+ if (values.jobs && values.jobs !== "auto") {
174
+ const parsed = parseInt(values.jobs, 10)
175
+
176
+ if (isNaN(parsed) || parsed < 1) {
177
+ console.error(`Error: Invalid --jobs value "${values.jobs}". Must be a positive integer or "auto".`)
178
+ process.exit(1)
179
+ }
180
+
181
+ jobs = parsed
182
+ }
183
+
184
+ return { patterns, configFile, formatOption, showTiming, theme, wrapLines, truncateLines, useGitHubActions, fix, fixUnsafe, ignoreDisableComments, force, init, loadCustomRules, failLevel, jobs }
167
185
  }
168
186
 
169
187
  private getFilePatterns(positionals: string[]): string[] {