@herb-tools/linter 0.8.9 → 0.9.0

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 (573) 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 +60648 -17513
  28. package/dist/herb-lint.js.map +1 -1
  29. package/dist/index.cjs +2621 -934
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +2554 -873
  32. package/dist/index.js.map +1 -1
  33. package/dist/lint-worker.js +71462 -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 +31206 -7834
  39. package/dist/loader.cjs.map +1 -1
  40. package/dist/loader.js +31168 -7802
  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/{src/rules → rules}/erb-comment-syntax.js +2 -2
  47. package/dist/rules/erb-comment-syntax.js.map +1 -0
  48. package/dist/{src/rules → rules}/erb-no-case-node-children.js +2 -2
  49. package/dist/rules/erb-no-case-node-children.js.map +1 -0
  50. package/dist/rules/erb-no-conditional-html-element.js +38 -0
  51. package/dist/rules/erb-no-conditional-html-element.js.map +1 -0
  52. package/dist/rules/erb-no-conditional-open-tag.js +24 -0
  53. package/dist/rules/erb-no-conditional-open-tag.js.map +1 -0
  54. package/dist/rules/erb-no-duplicate-branch-elements.js +245 -0
  55. package/dist/rules/erb-no-duplicate-branch-elements.js.map +1 -0
  56. package/dist/{src/rules → rules}/erb-no-empty-tags.js +2 -2
  57. package/dist/rules/erb-no-empty-tags.js.map +1 -0
  58. package/dist/{src/rules → rules}/erb-no-extra-newline.js +4 -21
  59. package/dist/rules/erb-no-extra-newline.js.map +1 -0
  60. package/dist/{src/rules → rules}/erb-no-extra-whitespace-inside-tags.js +39 -13
  61. package/dist/rules/erb-no-extra-whitespace-inside-tags.js.map +1 -0
  62. package/dist/rules/erb-no-inline-case-conditions.js +40 -0
  63. package/dist/rules/erb-no-inline-case-conditions.js.map +1 -0
  64. package/dist/rules/erb-no-instance-variables-in-partials.js +67 -0
  65. package/dist/rules/erb-no-instance-variables-in-partials.js.map +1 -0
  66. package/dist/rules/erb-no-interpolated-class-names.js +47 -0
  67. package/dist/rules/erb-no-interpolated-class-names.js.map +1 -0
  68. package/dist/rules/erb-no-javascript-tag-helper.js +34 -0
  69. package/dist/rules/erb-no-javascript-tag-helper.js.map +1 -0
  70. package/dist/{src/rules → rules}/erb-no-output-control-flow.js +9 -12
  71. package/dist/rules/erb-no-output-control-flow.js.map +1 -0
  72. package/dist/rules/erb-no-output-in-attribute-name.js +30 -0
  73. package/dist/rules/erb-no-output-in-attribute-name.js.map +1 -0
  74. package/dist/rules/erb-no-output-in-attribute-position.js +30 -0
  75. package/dist/rules/erb-no-output-in-attribute-position.js.map +1 -0
  76. package/dist/rules/erb-no-raw-output-in-attribute-value.js +35 -0
  77. package/dist/rules/erb-no-raw-output-in-attribute-value.js.map +1 -0
  78. package/dist/{src/rules → rules}/erb-no-silent-tag-in-attribute-name.js +2 -2
  79. package/dist/rules/erb-no-silent-tag-in-attribute-name.js.map +1 -0
  80. package/dist/rules/erb-no-statement-in-script.js +58 -0
  81. package/dist/rules/erb-no-statement-in-script.js.map +1 -0
  82. package/dist/rules/erb-no-then-in-control-flow.js +45 -0
  83. package/dist/rules/erb-no-then-in-control-flow.js.map +1 -0
  84. package/dist/rules/erb-no-trailing-whitespace.js +138 -0
  85. package/dist/rules/erb-no-trailing-whitespace.js.map +1 -0
  86. package/dist/rules/erb-no-unsafe-js-attribute.js +36 -0
  87. package/dist/rules/erb-no-unsafe-js-attribute.js.map +1 -0
  88. package/dist/rules/erb-no-unsafe-raw.js +63 -0
  89. package/dist/rules/erb-no-unsafe-raw.js.map +1 -0
  90. package/dist/rules/erb-no-unsafe-script-interpolation.js +54 -0
  91. package/dist/rules/erb-no-unsafe-script-interpolation.js.map +1 -0
  92. package/dist/{src/rules → rules}/erb-prefer-image-tag-helper.js +5 -4
  93. package/dist/rules/erb-prefer-image-tag-helper.js.map +1 -0
  94. package/dist/{src/rules → rules}/erb-require-trailing-newline.js +2 -2
  95. package/dist/rules/erb-require-trailing-newline.js.map +1 -0
  96. package/dist/{src/rules → rules}/erb-require-whitespace-inside-tags.js +39 -15
  97. package/dist/rules/erb-require-whitespace-inside-tags.js.map +1 -0
  98. package/dist/{src/rules → rules}/erb-right-trim.js +2 -2
  99. package/dist/rules/erb-right-trim.js.map +1 -0
  100. package/dist/{src/rules → rules}/erb-strict-locals-comment-syntax.js +5 -5
  101. package/dist/rules/erb-strict-locals-comment-syntax.js.map +1 -0
  102. package/dist/{src/rules → rules}/erb-strict-locals-required.js +2 -2
  103. package/dist/rules/erb-strict-locals-required.js.map +1 -0
  104. package/dist/rules/file-utils.js.map +1 -0
  105. package/dist/rules/herb-disable-comment-base.js.map +1 -0
  106. package/dist/{src/rules → rules}/herb-disable-comment-malformed.js +2 -2
  107. package/dist/rules/herb-disable-comment-malformed.js.map +1 -0
  108. package/dist/{src/rules → rules}/herb-disable-comment-missing-rules.js +2 -2
  109. package/dist/rules/herb-disable-comment-missing-rules.js.map +1 -0
  110. package/dist/{src/rules → rules}/herb-disable-comment-no-duplicate-rules.js +2 -2
  111. package/dist/rules/herb-disable-comment-no-duplicate-rules.js.map +1 -0
  112. package/dist/{src/rules → rules}/herb-disable-comment-no-redundant-all.js +2 -2
  113. package/dist/rules/herb-disable-comment-no-redundant-all.js.map +1 -0
  114. package/dist/{src/rules → rules}/herb-disable-comment-unnecessary.js +2 -2
  115. package/dist/rules/herb-disable-comment-unnecessary.js.map +1 -0
  116. package/dist/{src/rules → rules}/herb-disable-comment-valid-rule-name.js +2 -2
  117. package/dist/rules/herb-disable-comment-valid-rule-name.js.map +1 -0
  118. package/dist/rules/html-allowed-script-type.js +57 -0
  119. package/dist/rules/html-allowed-script-type.js.map +1 -0
  120. package/dist/rules/html-anchor-require-href.js +68 -0
  121. package/dist/rules/html-anchor-require-href.js.map +1 -0
  122. package/dist/{src/rules → rules}/html-aria-attribute-must-be-valid.js +3 -3
  123. package/dist/rules/html-aria-attribute-must-be-valid.js.map +1 -0
  124. package/dist/{src/rules → rules}/html-aria-label-is-well-formatted.js +3 -3
  125. package/dist/rules/html-aria-label-is-well-formatted.js.map +1 -0
  126. package/dist/{src/rules → rules}/html-aria-level-must-be-valid.js +3 -3
  127. package/dist/rules/html-aria-level-must-be-valid.js.map +1 -0
  128. package/dist/{src/rules → rules}/html-aria-role-heading-requires-level.js +5 -4
  129. package/dist/rules/html-aria-role-heading-requires-level.js.map +1 -0
  130. package/dist/{src/rules → rules}/html-aria-role-must-be-valid.js +3 -3
  131. package/dist/rules/html-aria-role-must-be-valid.js.map +1 -0
  132. package/dist/{src/rules → rules}/html-attribute-double-quotes.js +4 -4
  133. package/dist/rules/html-attribute-double-quotes.js.map +1 -0
  134. package/dist/{src/rules → rules}/html-attribute-equals-spacing.js +2 -2
  135. package/dist/rules/html-attribute-equals-spacing.js.map +1 -0
  136. package/dist/{src/rules → rules}/html-attribute-values-require-quotes.js +2 -2
  137. package/dist/rules/html-attribute-values-require-quotes.js.map +1 -0
  138. package/dist/{src/rules → rules}/html-avoid-both-disabled-and-aria-disabled.js +9 -9
  139. package/dist/rules/html-avoid-both-disabled-and-aria-disabled.js.map +1 -0
  140. package/dist/{src/rules → rules}/html-body-only-elements.js +5 -4
  141. package/dist/rules/html-body-only-elements.js.map +1 -0
  142. package/dist/{src/rules → rules}/html-boolean-attributes-no-value.js +4 -3
  143. package/dist/rules/html-boolean-attributes-no-value.js.map +1 -0
  144. package/dist/rules/html-details-has-summary.js +52 -0
  145. package/dist/rules/html-details-has-summary.js.map +1 -0
  146. package/dist/{src/rules → rules}/html-head-only-elements.js +6 -5
  147. package/dist/rules/html-head-only-elements.js.map +1 -0
  148. package/dist/{src/rules → rules}/html-iframe-has-title.js +8 -11
  149. package/dist/rules/html-iframe-has-title.js.map +1 -0
  150. package/dist/{src/rules → rules}/html-img-require-alt.js +11 -5
  151. package/dist/rules/html-img-require-alt.js.map +1 -0
  152. package/dist/{src/rules → rules}/html-input-require-autocomplete.js +7 -10
  153. package/dist/rules/html-input-require-autocomplete.js.map +1 -0
  154. package/dist/{src/rules → rules}/html-navigation-has-label.js +6 -5
  155. package/dist/rules/html-navigation-has-label.js.map +1 -0
  156. package/dist/rules/html-no-abstract-roles.js +29 -0
  157. package/dist/rules/html-no-abstract-roles.js.map +1 -0
  158. package/dist/rules/html-no-aria-hidden-on-body.js +42 -0
  159. package/dist/rules/html-no-aria-hidden-on-body.js.map +1 -0
  160. package/dist/{src/rules → rules}/html-no-aria-hidden-on-focusable.js +6 -5
  161. package/dist/rules/html-no-aria-hidden-on-focusable.js.map +1 -0
  162. package/dist/{src/rules → rules}/html-no-block-inside-inline.js +6 -9
  163. package/dist/rules/html-no-block-inside-inline.js.map +1 -0
  164. package/dist/{src/rules → rules}/html-no-duplicate-attributes.js +4 -3
  165. package/dist/rules/html-no-duplicate-attributes.js.map +1 -0
  166. package/dist/{src/rules → rules}/html-no-duplicate-ids.js +14 -11
  167. package/dist/rules/html-no-duplicate-ids.js.map +1 -0
  168. package/dist/{src/rules → rules}/html-no-duplicate-meta-names.js +22 -20
  169. package/dist/rules/html-no-duplicate-meta-names.js.map +1 -0
  170. package/dist/{src/rules → rules}/html-no-empty-attributes.js +2 -2
  171. package/dist/rules/html-no-empty-attributes.js.map +1 -0
  172. package/dist/rules/html-no-empty-headings.js +98 -0
  173. package/dist/rules/html-no-empty-headings.js.map +1 -0
  174. package/dist/{src/rules → rules}/html-no-nested-links.js +23 -15
  175. package/dist/rules/html-no-nested-links.js.map +1 -0
  176. package/dist/{src/rules → rules}/html-no-positive-tab-index.js +3 -3
  177. package/dist/rules/html-no-positive-tab-index.js.map +1 -0
  178. package/dist/{src/rules → rules}/html-no-self-closing.js +4 -4
  179. package/dist/rules/html-no-self-closing.js.map +1 -0
  180. package/dist/{src/rules → rules}/html-no-space-in-tag.js +4 -6
  181. package/dist/rules/html-no-space-in-tag.js.map +1 -0
  182. package/dist/{src/rules → rules}/html-no-title-attribute.js +6 -5
  183. package/dist/rules/html-no-title-attribute.js.map +1 -0
  184. package/dist/{src/rules → rules}/html-no-underscores-in-attribute-names.js +2 -2
  185. package/dist/rules/html-no-underscores-in-attribute-names.js.map +1 -0
  186. package/dist/rules/html-require-closing-tags.js +29 -0
  187. package/dist/rules/html-require-closing-tags.js.map +1 -0
  188. package/dist/{src/rules → rules}/html-tag-name-lowercase.js +13 -9
  189. package/dist/rules/html-tag-name-lowercase.js.map +1 -0
  190. package/dist/{src/rules → rules}/index.js +19 -0
  191. package/dist/rules/index.js.map +1 -0
  192. package/dist/{src/rules → rules}/parser-no-errors.js +3 -3
  193. package/dist/rules/parser-no-errors.js.map +1 -0
  194. package/dist/{src/rules → rules}/rule-utils.js +141 -219
  195. package/dist/rules/rule-utils.js.map +1 -0
  196. package/dist/rules/string-utils.js.map +1 -0
  197. package/dist/{src/rules → rules}/svg-tag-name-capitalization.js +7 -6
  198. package/dist/rules/svg-tag-name-capitalization.js.map +1 -0
  199. package/dist/rules/turbo-permanent-require-id.js +34 -0
  200. package/dist/rules/turbo-permanent-require-id.js.map +1 -0
  201. package/dist/{src/rules.js → rules.js} +56 -10
  202. package/dist/rules.js.map +1 -0
  203. package/dist/types/cli/argument-parser.d.ts +1 -0
  204. package/dist/types/cli/file-processor.d.ts +13 -0
  205. package/dist/types/cli/file-url.d.ts +1 -0
  206. package/dist/types/cli/index.d.ts +1 -0
  207. package/dist/types/cli/lint-worker.d.ts +34 -0
  208. package/dist/types/custom-rule-loader.d.ts +4 -0
  209. package/dist/types/index.d.ts +1 -0
  210. package/dist/types/linter.d.ts +13 -6
  211. package/dist/types/parse-cache.d.ts +9 -0
  212. package/dist/types/{src/rules/html-aria-level-must-be-valid.d.ts → rules/actionview-no-silent-helper.d.ts} +4 -3
  213. package/dist/types/rules/erb-comment-syntax.d.ts +1 -1
  214. package/dist/types/rules/erb-no-case-node-children.d.ts +1 -1
  215. package/dist/types/{src/rules/herb-disable-comment-malformed.d.ts → rules/erb-no-conditional-html-element.d.ts} +3 -3
  216. package/dist/types/{src/rules/erb-prefer-image-tag-helper.d.ts → rules/erb-no-conditional-open-tag.d.ts} +3 -3
  217. package/dist/types/rules/erb-no-duplicate-branch-elements.d.ts +17 -0
  218. package/dist/types/rules/erb-no-empty-tags.d.ts +1 -1
  219. package/dist/types/rules/erb-no-extra-newline.d.ts +1 -1
  220. package/dist/types/rules/erb-no-extra-whitespace-inside-tags.d.ts +1 -1
  221. package/dist/types/{src/rules/html-no-duplicate-attributes.d.ts → rules/erb-no-inline-case-conditions.d.ts} +4 -3
  222. package/dist/types/rules/erb-no-instance-variables-in-partials.d.ts +10 -0
  223. package/dist/types/{src/rules/html-no-aria-hidden-on-focusable.d.ts → rules/erb-no-interpolated-class-names.d.ts} +2 -2
  224. package/dist/types/{src/rules/html-aria-attribute-must-be-valid.d.ts → rules/erb-no-javascript-tag-helper.d.ts} +2 -2
  225. package/dist/types/rules/erb-no-output-control-flow.d.ts +1 -1
  226. 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
  227. package/dist/types/{src/rules/herb-disable-comment-missing-rules.d.ts → rules/erb-no-output-in-attribute-position.d.ts} +2 -2
  228. package/dist/types/{src/rules/erb-no-empty-tags.d.ts → rules/erb-no-raw-output-in-attribute-value.d.ts} +2 -2
  229. package/dist/types/rules/erb-no-silent-tag-in-attribute-name.d.ts +1 -1
  230. package/dist/types/{src/rules/html-navigation-has-label.d.ts → rules/erb-no-statement-in-script.d.ts} +2 -2
  231. package/dist/types/rules/erb-no-then-in-control-flow.d.ts +9 -0
  232. package/dist/types/rules/erb-no-trailing-whitespace.d.ts +19 -0
  233. package/dist/types/{src/rules/html-no-positive-tab-index.d.ts → rules/erb-no-unsafe-js-attribute.d.ts} +2 -2
  234. package/dist/types/{src/rules/erb-no-case-node-children.d.ts → rules/erb-no-unsafe-raw.d.ts} +2 -2
  235. package/dist/types/rules/erb-no-unsafe-script-interpolation.d.ts +8 -0
  236. package/dist/types/rules/erb-prefer-image-tag-helper.d.ts +1 -1
  237. package/dist/types/rules/erb-require-trailing-newline.d.ts +1 -1
  238. package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +1 -1
  239. package/dist/types/rules/erb-right-trim.d.ts +1 -1
  240. package/dist/types/rules/erb-strict-locals-comment-syntax.d.ts +1 -1
  241. package/dist/types/rules/erb-strict-locals-required.d.ts +1 -1
  242. package/dist/types/rules/herb-disable-comment-malformed.d.ts +1 -1
  243. package/dist/types/rules/herb-disable-comment-missing-rules.d.ts +1 -1
  244. package/dist/types/rules/herb-disable-comment-no-duplicate-rules.d.ts +1 -1
  245. package/dist/types/rules/herb-disable-comment-no-redundant-all.d.ts +1 -1
  246. package/dist/types/rules/herb-disable-comment-unnecessary.d.ts +1 -1
  247. package/dist/types/rules/herb-disable-comment-valid-rule-name.d.ts +1 -1
  248. package/dist/types/{src/rules/html-anchor-require-href.d.ts → rules/html-allowed-script-type.d.ts} +2 -2
  249. package/dist/types/rules/html-anchor-require-href.d.ts +3 -2
  250. package/dist/types/rules/html-aria-attribute-must-be-valid.d.ts +1 -1
  251. package/dist/types/rules/html-aria-label-is-well-formatted.d.ts +1 -1
  252. package/dist/types/rules/html-aria-level-must-be-valid.d.ts +1 -1
  253. package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +1 -1
  254. package/dist/types/rules/html-aria-role-must-be-valid.d.ts +1 -1
  255. package/dist/types/rules/html-attribute-double-quotes.d.ts +1 -1
  256. package/dist/types/rules/html-attribute-equals-spacing.d.ts +1 -1
  257. package/dist/types/rules/html-attribute-values-require-quotes.d.ts +1 -1
  258. package/dist/types/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +1 -1
  259. package/dist/types/rules/html-body-only-elements.d.ts +1 -1
  260. package/dist/types/rules/html-boolean-attributes-no-value.d.ts +1 -1
  261. package/dist/types/{src/rules/html-no-empty-attributes.d.ts → rules/html-details-has-summary.d.ts} +4 -3
  262. package/dist/types/rules/html-head-only-elements.d.ts +1 -1
  263. package/dist/types/rules/html-iframe-has-title.d.ts +1 -1
  264. package/dist/types/rules/html-img-require-alt.d.ts +1 -1
  265. package/dist/types/rules/html-input-require-autocomplete.d.ts +1 -1
  266. package/dist/types/rules/html-navigation-has-label.d.ts +1 -1
  267. package/dist/types/{src/rules/html-no-empty-headings.d.ts → rules/html-no-abstract-roles.d.ts} +2 -2
  268. package/dist/types/{src/rules/erb-no-output-control-flow.d.ts → rules/html-no-aria-hidden-on-body.d.ts} +3 -3
  269. package/dist/types/rules/html-no-aria-hidden-on-focusable.d.ts +1 -1
  270. package/dist/types/rules/html-no-block-inside-inline.d.ts +1 -1
  271. package/dist/types/rules/html-no-duplicate-attributes.d.ts +1 -1
  272. package/dist/types/rules/html-no-duplicate-ids.d.ts +1 -1
  273. package/dist/types/rules/html-no-duplicate-meta-names.d.ts +1 -1
  274. package/dist/types/rules/html-no-empty-attributes.d.ts +1 -1
  275. package/dist/types/rules/html-no-empty-headings.d.ts +1 -1
  276. package/dist/types/rules/html-no-nested-links.d.ts +1 -1
  277. package/dist/types/rules/html-no-positive-tab-index.d.ts +1 -1
  278. package/dist/types/rules/html-no-self-closing.d.ts +1 -1
  279. package/dist/types/rules/html-no-space-in-tag.d.ts +1 -1
  280. package/dist/types/rules/html-no-title-attribute.d.ts +1 -1
  281. package/dist/types/rules/html-no-underscores-in-attribute-names.d.ts +1 -1
  282. package/dist/types/{src/rules/html-body-only-elements.d.ts → rules/html-require-closing-tags.d.ts} +4 -3
  283. package/dist/types/rules/html-tag-name-lowercase.d.ts +1 -1
  284. package/dist/types/rules/index.d.ts +19 -0
  285. package/dist/types/rules/parser-no-errors.d.ts +1 -1
  286. package/dist/types/rules/rule-utils.d.ts +35 -88
  287. package/dist/types/rules/svg-tag-name-capitalization.d.ts +1 -1
  288. package/dist/types/{src/rules/html-aria-role-must-be-valid.d.ts → rules/turbo-permanent-require-id.d.ts} +2 -2
  289. package/dist/types/types.d.ts +25 -7
  290. package/dist/types/urls.d.ts +1 -0
  291. package/dist/{src/types.js → types.js} +53 -0
  292. package/dist/types.js.map +1 -0
  293. package/dist/urls.js +5 -0
  294. package/dist/urls.js.map +1 -0
  295. package/docs/rules/README.md +23 -2
  296. package/docs/rules/actionview-no-silent-helper.md +57 -0
  297. package/docs/rules/erb-no-conditional-html-element.md +90 -0
  298. package/docs/rules/erb-no-conditional-open-tag.md +130 -0
  299. package/docs/rules/erb-no-duplicate-branch-elements.md +98 -0
  300. package/docs/rules/erb-no-inline-case-conditions.md +85 -0
  301. package/docs/rules/erb-no-instance-variables-in-partials.md +43 -0
  302. package/docs/rules/erb-no-interpolated-class-names.md +57 -0
  303. package/docs/rules/erb-no-javascript-tag-helper.md +33 -0
  304. package/docs/rules/erb-no-output-in-attribute-name.md +38 -0
  305. package/docs/rules/erb-no-output-in-attribute-position.md +60 -0
  306. package/docs/rules/erb-no-raw-output-in-attribute-value.md +37 -0
  307. package/docs/rules/erb-no-statement-in-script.md +68 -0
  308. package/docs/rules/erb-no-then-in-control-flow.md +86 -0
  309. package/docs/rules/erb-no-trailing-whitespace.md +69 -0
  310. package/docs/rules/erb-no-unsafe-js-attribute.md +41 -0
  311. package/docs/rules/erb-no-unsafe-raw.md +47 -0
  312. package/docs/rules/erb-no-unsafe-script-interpolation.md +73 -0
  313. package/docs/rules/html-allowed-script-type.md +59 -0
  314. package/docs/rules/html-anchor-require-href.md +19 -6
  315. package/docs/rules/html-details-has-summary.md +46 -0
  316. package/docs/rules/html-img-require-alt.md +5 -3
  317. package/docs/rules/html-no-abstract-roles.md +74 -0
  318. package/docs/rules/html-no-aria-hidden-on-body.md +44 -0
  319. package/docs/rules/html-require-closing-tags.md +142 -0
  320. package/docs/rules/parser-no-errors.md +4 -17
  321. package/docs/rules/turbo-permanent-require-id.md +41 -0
  322. package/package.json +12 -11
  323. package/src/cli/argument-parser.ts +20 -2
  324. package/src/cli/file-processor.ts +189 -10
  325. package/src/cli/file-url.ts +6 -0
  326. package/src/cli/formatters/detailed-formatter.ts +19 -21
  327. package/src/cli/formatters/simple-formatter.ts +23 -13
  328. package/src/cli/index.ts +2 -0
  329. package/src/cli/lint-worker.ts +208 -0
  330. package/src/cli/summary-reporter.ts +14 -15
  331. package/src/cli.ts +5 -3
  332. package/src/custom-rule-loader.ts +20 -5
  333. package/src/herb-disable-comment-utils.ts +0 -3
  334. package/src/index.ts +1 -0
  335. package/src/linter.ts +98 -79
  336. package/src/parse-cache.ts +39 -0
  337. package/src/rules/actionview-no-silent-helper.ts +58 -0
  338. package/src/rules/erb-comment-syntax.ts +2 -2
  339. package/src/rules/erb-no-case-node-children.ts +2 -2
  340. package/src/rules/erb-no-conditional-html-element.ts +53 -0
  341. package/src/rules/erb-no-conditional-open-tag.ts +37 -0
  342. package/src/rules/erb-no-duplicate-branch-elements.ts +320 -0
  343. package/src/rules/erb-no-empty-tags.ts +2 -2
  344. package/src/rules/erb-no-extra-newline.ts +5 -25
  345. package/src/rules/erb-no-extra-whitespace-inside-tags.ts +45 -15
  346. package/src/rules/erb-no-inline-case-conditions.ts +54 -0
  347. package/src/rules/erb-no-instance-variables-in-partials.ts +101 -0
  348. package/src/rules/erb-no-interpolated-class-names.ts +65 -0
  349. package/src/rules/erb-no-javascript-tag-helper.ts +47 -0
  350. package/src/rules/erb-no-output-control-flow.ts +10 -10
  351. package/src/rules/erb-no-output-in-attribute-name.ts +39 -0
  352. package/src/rules/erb-no-output-in-attribute-position.ts +39 -0
  353. package/src/rules/erb-no-raw-output-in-attribute-value.ts +47 -0
  354. package/src/rules/erb-no-silent-tag-in-attribute-name.ts +2 -2
  355. package/src/rules/erb-no-statement-in-script.ts +82 -0
  356. package/src/rules/erb-no-then-in-control-flow.ts +62 -0
  357. package/src/rules/erb-no-trailing-whitespace.ts +187 -0
  358. package/src/rules/erb-no-unsafe-js-attribute.ts +47 -0
  359. package/src/rules/erb-no-unsafe-raw.ts +83 -0
  360. package/src/rules/erb-no-unsafe-script-interpolation.ts +76 -0
  361. package/src/rules/erb-prefer-image-tag-helper.ts +5 -4
  362. package/src/rules/erb-require-trailing-newline.ts +2 -2
  363. package/src/rules/erb-require-whitespace-inside-tags.ts +42 -18
  364. package/src/rules/erb-right-trim.ts +2 -2
  365. package/src/rules/erb-strict-locals-comment-syntax.ts +5 -5
  366. package/src/rules/erb-strict-locals-required.ts +2 -2
  367. package/src/rules/herb-disable-comment-malformed.ts +2 -2
  368. package/src/rules/herb-disable-comment-missing-rules.ts +2 -2
  369. package/src/rules/herb-disable-comment-no-duplicate-rules.ts +2 -2
  370. package/src/rules/herb-disable-comment-no-redundant-all.ts +2 -2
  371. package/src/rules/herb-disable-comment-unnecessary.ts +2 -2
  372. package/src/rules/herb-disable-comment-valid-rule-name.ts +2 -2
  373. package/src/rules/html-allowed-script-type.ts +84 -0
  374. package/src/rules/html-anchor-require-href.ts +73 -11
  375. package/src/rules/html-aria-attribute-must-be-valid.ts +3 -3
  376. package/src/rules/html-aria-label-is-well-formatted.ts +3 -3
  377. package/src/rules/html-aria-level-must-be-valid.ts +3 -3
  378. package/src/rules/html-aria-role-heading-requires-level.ts +5 -4
  379. package/src/rules/html-aria-role-must-be-valid.ts +3 -3
  380. package/src/rules/html-attribute-double-quotes.ts +4 -4
  381. package/src/rules/html-attribute-equals-spacing.ts +2 -2
  382. package/src/rules/html-attribute-values-require-quotes.ts +2 -2
  383. package/src/rules/html-avoid-both-disabled-and-aria-disabled.ts +10 -11
  384. package/src/rules/html-body-only-elements.ts +5 -4
  385. package/src/rules/html-boolean-attributes-no-value.ts +4 -3
  386. package/src/rules/html-details-has-summary.ts +69 -0
  387. package/src/rules/html-head-only-elements.ts +6 -5
  388. package/src/rules/html-iframe-has-title.ts +8 -11
  389. package/src/rules/html-img-require-alt.ts +16 -5
  390. package/src/rules/html-input-require-autocomplete.ts +7 -10
  391. package/src/rules/html-navigation-has-label.ts +6 -5
  392. package/src/rules/html-no-abstract-roles.ts +40 -0
  393. package/src/rules/html-no-aria-hidden-on-body.ts +58 -0
  394. package/src/rules/html-no-aria-hidden-on-focusable.ts +6 -5
  395. package/src/rules/html-no-block-inside-inline.ts +7 -13
  396. package/src/rules/html-no-duplicate-attributes.ts +4 -3
  397. package/src/rules/html-no-duplicate-ids.ts +16 -13
  398. package/src/rules/html-no-duplicate-meta-names.ts +20 -19
  399. package/src/rules/html-no-empty-attributes.ts +2 -2
  400. package/src/rules/html-no-empty-headings.ts +44 -58
  401. package/src/rules/html-no-nested-links.ts +25 -16
  402. package/src/rules/html-no-positive-tab-index.ts +3 -3
  403. package/src/rules/html-no-self-closing.ts +5 -5
  404. package/src/rules/html-no-space-in-tag.ts +5 -8
  405. package/src/rules/html-no-title-attribute.ts +6 -5
  406. package/src/rules/html-no-underscores-in-attribute-names.ts +2 -2
  407. package/src/rules/html-require-closing-tags.ts +41 -0
  408. package/src/rules/html-tag-name-lowercase.ts +14 -9
  409. package/src/rules/index.ts +19 -0
  410. package/src/rules/parser-no-errors.ts +3 -3
  411. package/src/rules/rule-utils.ts +162 -279
  412. package/src/rules/svg-tag-name-capitalization.ts +10 -10
  413. package/src/rules/turbo-permanent-require-id.ts +49 -0
  414. package/src/rules.ts +60 -10
  415. package/src/types.ts +76 -7
  416. package/src/urls.ts +5 -0
  417. package/dist/package.json +0 -65
  418. package/dist/src/cli/argument-parser.js.map +0 -1
  419. package/dist/src/cli/file-processor.js.map +0 -1
  420. package/dist/src/cli/formatters/base-formatter.js.map +0 -1
  421. package/dist/src/cli/formatters/detailed-formatter.js.map +0 -1
  422. package/dist/src/cli/formatters/github-actions-formatter.js.map +0 -1
  423. package/dist/src/cli/formatters/index.js.map +0 -1
  424. package/dist/src/cli/formatters/json-formatter.js.map +0 -1
  425. package/dist/src/cli/formatters/simple-formatter.js +0 -44
  426. package/dist/src/cli/formatters/simple-formatter.js.map +0 -1
  427. package/dist/src/cli/index.js.map +0 -1
  428. package/dist/src/cli/output-manager.js.map +0 -1
  429. package/dist/src/cli/summary-reporter.js.map +0 -1
  430. package/dist/src/cli.js.map +0 -1
  431. package/dist/src/custom-rule-loader.js.map +0 -1
  432. package/dist/src/herb-disable-comment-utils.js.map +0 -1
  433. package/dist/src/herb-lint.js +0 -5
  434. package/dist/src/herb-lint.js.map +0 -1
  435. package/dist/src/index.js +0 -5
  436. package/dist/src/index.js.map +0 -1
  437. package/dist/src/linter-ignore.js.map +0 -1
  438. package/dist/src/linter.js.map +0 -1
  439. package/dist/src/loader.js +0 -17
  440. package/dist/src/loader.js.map +0 -1
  441. package/dist/src/rules/erb-comment-syntax.js.map +0 -1
  442. package/dist/src/rules/erb-no-case-node-children.js.map +0 -1
  443. package/dist/src/rules/erb-no-empty-tags.js.map +0 -1
  444. package/dist/src/rules/erb-no-extra-newline.js.map +0 -1
  445. package/dist/src/rules/erb-no-extra-whitespace-inside-tags.js.map +0 -1
  446. package/dist/src/rules/erb-no-output-control-flow.js.map +0 -1
  447. package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js.map +0 -1
  448. package/dist/src/rules/erb-prefer-image-tag-helper.js.map +0 -1
  449. package/dist/src/rules/erb-require-trailing-newline.js.map +0 -1
  450. package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +0 -1
  451. package/dist/src/rules/erb-right-trim.js.map +0 -1
  452. package/dist/src/rules/erb-strict-locals-comment-syntax.js.map +0 -1
  453. package/dist/src/rules/erb-strict-locals-required.js.map +0 -1
  454. package/dist/src/rules/file-utils.js.map +0 -1
  455. package/dist/src/rules/herb-disable-comment-base.js.map +0 -1
  456. package/dist/src/rules/herb-disable-comment-malformed.js.map +0 -1
  457. package/dist/src/rules/herb-disable-comment-missing-rules.js.map +0 -1
  458. package/dist/src/rules/herb-disable-comment-no-duplicate-rules.js.map +0 -1
  459. package/dist/src/rules/herb-disable-comment-no-redundant-all.js.map +0 -1
  460. package/dist/src/rules/herb-disable-comment-unnecessary.js.map +0 -1
  461. package/dist/src/rules/herb-disable-comment-valid-rule-name.js.map +0 -1
  462. package/dist/src/rules/html-anchor-require-href.js +0 -32
  463. package/dist/src/rules/html-anchor-require-href.js.map +0 -1
  464. package/dist/src/rules/html-aria-attribute-must-be-valid.js.map +0 -1
  465. package/dist/src/rules/html-aria-label-is-well-formatted.js.map +0 -1
  466. package/dist/src/rules/html-aria-level-must-be-valid.js.map +0 -1
  467. package/dist/src/rules/html-aria-role-heading-requires-level.js.map +0 -1
  468. package/dist/src/rules/html-aria-role-must-be-valid.js.map +0 -1
  469. package/dist/src/rules/html-attribute-double-quotes.js.map +0 -1
  470. package/dist/src/rules/html-attribute-equals-spacing.js.map +0 -1
  471. package/dist/src/rules/html-attribute-values-require-quotes.js.map +0 -1
  472. package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js.map +0 -1
  473. package/dist/src/rules/html-body-only-elements.js.map +0 -1
  474. package/dist/src/rules/html-boolean-attributes-no-value.js.map +0 -1
  475. package/dist/src/rules/html-head-only-elements.js.map +0 -1
  476. package/dist/src/rules/html-iframe-has-title.js.map +0 -1
  477. package/dist/src/rules/html-img-require-alt.js.map +0 -1
  478. package/dist/src/rules/html-input-require-autocomplete.js.map +0 -1
  479. package/dist/src/rules/html-navigation-has-label.js.map +0 -1
  480. package/dist/src/rules/html-no-aria-hidden-on-focusable.js.map +0 -1
  481. package/dist/src/rules/html-no-block-inside-inline.js.map +0 -1
  482. package/dist/src/rules/html-no-duplicate-attributes.js.map +0 -1
  483. package/dist/src/rules/html-no-duplicate-ids.js.map +0 -1
  484. package/dist/src/rules/html-no-duplicate-meta-names.js.map +0 -1
  485. package/dist/src/rules/html-no-empty-attributes.js.map +0 -1
  486. package/dist/src/rules/html-no-empty-headings.js +0 -115
  487. package/dist/src/rules/html-no-empty-headings.js.map +0 -1
  488. package/dist/src/rules/html-no-nested-links.js.map +0 -1
  489. package/dist/src/rules/html-no-positive-tab-index.js.map +0 -1
  490. package/dist/src/rules/html-no-self-closing.js.map +0 -1
  491. package/dist/src/rules/html-no-space-in-tag.js.map +0 -1
  492. package/dist/src/rules/html-no-title-attribute.js.map +0 -1
  493. package/dist/src/rules/html-no-underscores-in-attribute-names.js.map +0 -1
  494. package/dist/src/rules/html-tag-name-lowercase.js.map +0 -1
  495. package/dist/src/rules/index.js.map +0 -1
  496. package/dist/src/rules/parser-no-errors.js.map +0 -1
  497. package/dist/src/rules/rule-utils.js.map +0 -1
  498. package/dist/src/rules/string-utils.js.map +0 -1
  499. package/dist/src/rules/svg-tag-name-capitalization.js.map +0 -1
  500. package/dist/src/rules.js.map +0 -1
  501. package/dist/src/types.js.map +0 -1
  502. package/dist/tsconfig.tsbuildinfo +0 -1
  503. package/dist/types/src/cli/argument-parser.d.ts +0 -25
  504. package/dist/types/src/cli/file-processor.d.ts +0 -43
  505. package/dist/types/src/cli/formatters/base-formatter.d.ts +0 -6
  506. package/dist/types/src/cli/formatters/detailed-formatter.d.ts +0 -13
  507. package/dist/types/src/cli/formatters/github-actions-formatter.d.ts +0 -17
  508. package/dist/types/src/cli/formatters/index.d.ts +0 -5
  509. package/dist/types/src/cli/formatters/json-formatter.d.ts +0 -48
  510. package/dist/types/src/cli/formatters/simple-formatter.d.ts +0 -8
  511. package/dist/types/src/cli/index.d.ts +0 -5
  512. package/dist/types/src/cli/output-manager.d.ts +0 -32
  513. package/dist/types/src/cli/summary-reporter.d.ts +0 -28
  514. package/dist/types/src/cli.d.ts +0 -28
  515. package/dist/types/src/custom-rule-loader.d.ts +0 -62
  516. package/dist/types/src/herb-disable-comment-utils.d.ts +0 -69
  517. package/dist/types/src/herb-lint.d.ts +0 -2
  518. package/dist/types/src/index.d.ts +0 -4
  519. package/dist/types/src/linter-ignore.d.ts +0 -12
  520. package/dist/types/src/linter.d.ts +0 -133
  521. package/dist/types/src/loader.d.ts +0 -20
  522. package/dist/types/src/rules/erb-comment-syntax.d.ts +0 -14
  523. package/dist/types/src/rules/erb-no-extra-newline.d.ts +0 -14
  524. package/dist/types/src/rules/erb-no-extra-whitespace-inside-tags.d.ts +0 -18
  525. package/dist/types/src/rules/erb-require-trailing-newline.d.ts +0 -9
  526. package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +0 -18
  527. package/dist/types/src/rules/erb-right-trim.d.ts +0 -14
  528. package/dist/types/src/rules/erb-strict-locals-comment-syntax.d.ts +0 -9
  529. package/dist/types/src/rules/erb-strict-locals-required.d.ts +0 -9
  530. package/dist/types/src/rules/file-utils.d.ts +0 -13
  531. package/dist/types/src/rules/herb-disable-comment-base.d.ts +0 -37
  532. package/dist/types/src/rules/herb-disable-comment-no-duplicate-rules.d.ts +0 -8
  533. package/dist/types/src/rules/herb-disable-comment-no-redundant-all.d.ts +0 -8
  534. package/dist/types/src/rules/herb-disable-comment-unnecessary.d.ts +0 -8
  535. package/dist/types/src/rules/herb-disable-comment-valid-rule-name.d.ts +0 -8
  536. package/dist/types/src/rules/html-aria-label-is-well-formatted.d.ts +0 -8
  537. package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +0 -8
  538. package/dist/types/src/rules/html-attribute-double-quotes.d.ts +0 -15
  539. package/dist/types/src/rules/html-attribute-equals-spacing.d.ts +0 -14
  540. package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +0 -15
  541. package/dist/types/src/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +0 -8
  542. package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +0 -14
  543. package/dist/types/src/rules/html-head-only-elements.d.ts +0 -9
  544. package/dist/types/src/rules/html-iframe-has-title.d.ts +0 -8
  545. package/dist/types/src/rules/html-img-require-alt.d.ts +0 -8
  546. package/dist/types/src/rules/html-input-require-autocomplete.d.ts +0 -8
  547. package/dist/types/src/rules/html-no-block-inside-inline.d.ts +0 -8
  548. package/dist/types/src/rules/html-no-duplicate-ids.d.ts +0 -8
  549. package/dist/types/src/rules/html-no-duplicate-meta-names.d.ts +0 -9
  550. package/dist/types/src/rules/html-no-nested-links.d.ts +0 -8
  551. package/dist/types/src/rules/html-no-self-closing.d.ts +0 -16
  552. package/dist/types/src/rules/html-no-space-in-tag.d.ts +0 -16
  553. package/dist/types/src/rules/html-no-title-attribute.d.ts +0 -8
  554. package/dist/types/src/rules/html-no-underscores-in-attribute-names.d.ts +0 -8
  555. package/dist/types/src/rules/html-tag-name-lowercase.d.ts +0 -18
  556. package/dist/types/src/rules/index.d.ts +0 -54
  557. package/dist/types/src/rules/parser-no-errors.d.ts +0 -9
  558. package/dist/types/src/rules/rule-utils.d.ts +0 -351
  559. package/dist/types/src/rules/string-utils.d.ts +0 -15
  560. package/dist/types/src/rules/svg-tag-name-capitalization.d.ts +0 -16
  561. package/dist/types/src/rules.d.ts +0 -2
  562. package/dist/types/src/types.d.ts +0 -190
  563. /package/dist/{src/cli → cli}/formatters/base-formatter.js +0 -0
  564. /package/dist/{src/cli → cli}/formatters/github-actions-formatter.js +0 -0
  565. /package/dist/{src/cli → cli}/formatters/index.js +0 -0
  566. /package/dist/{src/cli → cli}/formatters/json-formatter.js +0 -0
  567. /package/dist/{src/cli → cli}/index.js +0 -0
  568. /package/dist/{src/cli → cli}/output-manager.js +0 -0
  569. /package/dist/{src/herb-disable-comment-utils.js → herb-disable-comment-utils.js} +0 -0
  570. /package/dist/{src/linter-ignore.js → linter-ignore.js} +0 -0
  571. /package/dist/{src/rules → rules}/file-utils.js +0 -0
  572. /package/dist/{src/rules → rules}/herb-disable-comment-base.js +0 -0
  573. /package/dist/{src/rules → rules}/string-utils.js +0 -0
@@ -1,7 +1,7 @@
1
- import { Visitor, Location } from "@herb-tools/core";
2
- import type { HTMLAttributeNode, HTMLAttributeValueNode, HTMLElementNode, HTMLOpenTagNode, LexResult, Token, Node } from "@herb-tools/core";
1
+ import { Visitor, Location, Position } from "@herb-tools/core";
2
+ import type { HTMLAttributeNode, HTMLOpenTagNode, LexResult, Token, Node } from "@herb-tools/core";
3
3
  import type * as Nodes from "@herb-tools/core";
4
- import type { UnboundLintOffense, LintContext, BaseAutofixContext } from "../types.js";
4
+ import type { UnboundLintOffense, LintContext, LintSeverity, BaseAutofixContext } from "../types.js";
5
5
  export declare enum ControlFlowType {
6
6
  CONDITIONAL = 0,
7
7
  LOOP = 1
@@ -18,11 +18,11 @@ export declare abstract class BaseRuleVisitor<TAutofixContext extends BaseAutofi
18
18
  * Helper method to create an unbound lint offense (without severity).
19
19
  * The Linter will bind severity based on the rule's config.
20
20
  */
21
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext): UnboundLintOffense<TAutofixContext>;
21
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext>;
22
22
  /**
23
23
  * Helper method to add an offense to the offenses array
24
24
  */
25
- protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext): void;
25
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): void;
26
26
  }
27
27
  /**
28
28
  * Mixin that adds control flow tracking capabilities to rule visitors
@@ -59,77 +59,6 @@ export declare abstract class ControlFlowTrackingVisitor<TAutofixContext extends
59
59
  protected abstract onEnterBranch(): TBranchState;
60
60
  protected abstract onExitBranch(stateToRestore: TBranchState): void;
61
61
  }
62
- /**
63
- * Gets attributes from an HTMLOpenTagNode
64
- */
65
- export declare function getAttributes(node: HTMLOpenTagNode): HTMLAttributeNode[];
66
- /**
67
- * Gets the tag name from an HTML tag node (lowercased)
68
- */
69
- export declare function getTagName(node: HTMLElementNode | HTMLOpenTagNode | null | undefined): string | null;
70
- /**
71
- * Gets the attribute name from an HTMLAttributeNode (lowercased)
72
- * Returns null if the attribute name contains dynamic content (ERB)
73
- */
74
- export declare function getAttributeName(attributeNode: HTMLAttributeNode, lowercase?: boolean): string | null;
75
- /**
76
- * Checks if an attribute has a dynamic (ERB-containing) name
77
- */
78
- export declare function hasDynamicAttributeName(attributeNode: HTMLAttributeNode): boolean;
79
- /**
80
- * Gets the combined string representation of an attribute name (for debugging)
81
- * This includes both static content and ERB syntax
82
- */
83
- export declare function getCombinedAttributeNameString(attributeNode: HTMLAttributeNode): string;
84
- /**
85
- * Checks if an attribute value contains only static content (no ERB)
86
- */
87
- export declare function hasStaticAttributeValue(attributeNode: HTMLAttributeNode): boolean;
88
- /**
89
- * Checks if an attribute value contains dynamic content (ERB)
90
- */
91
- export declare function hasDynamicAttributeValue(attributeNode: HTMLAttributeNode): boolean;
92
- /**
93
- * Gets the static string value of an attribute (returns null if it contains ERB)
94
- */
95
- export declare function getStaticAttributeValue(attributeNode: HTMLAttributeNode): string | null;
96
- /**
97
- * Gets the value nodes array for dynamic inspection
98
- */
99
- export declare function getAttributeValueNodes(attributeNode: HTMLAttributeNode): Node[];
100
- /**
101
- * Checks if an attribute value contains any static content (for validation purposes)
102
- */
103
- export declare function hasStaticAttributeValueContent(attributeNode: HTMLAttributeNode): boolean;
104
- /**
105
- * Gets the static content of an attribute value (all literal parts combined)
106
- * Returns the concatenated literal content, or null if no literal nodes exist
107
- */
108
- export declare function getStaticAttributeValueContent(attributeNode: HTMLAttributeNode): string | null;
109
- /**
110
- * Gets the attribute value content from an HTMLAttributeValueNode
111
- */
112
- export declare function getAttributeValue(attributeNode: HTMLAttributeNode): string | null;
113
- /**
114
- * Checks if an attribute has a value
115
- */
116
- export declare function hasAttributeValue(attributeNode: HTMLAttributeNode): boolean;
117
- /**
118
- * Gets the quote type used for an attribute value
119
- */
120
- export declare function getAttributeValueQuoteType(nodeOrAttribute: HTMLAttributeNode | HTMLAttributeValueNode): "single" | "double" | "none" | null;
121
- /**
122
- * Finds an attribute by name in a list of attributes
123
- */
124
- export declare function findAttributeByName(attributes: Node[], attributeName: string): HTMLAttributeNode | null;
125
- /**
126
- * Checks if a tag has a specific attribute
127
- */
128
- export declare function hasAttribute(node: HTMLOpenTagNode | null | undefined, attributeName: string): boolean;
129
- /**
130
- * Checks if a tag has a specific attribute
131
- */
132
- export declare function getAttribute(node: HTMLOpenTagNode, attributeName: string): HTMLAttributeNode | null;
133
62
  /**
134
63
  * Common HTML element categorization
135
64
  */
@@ -148,6 +77,12 @@ export declare const SVG_CAMEL_CASE_ELEMENTS: Set<string>;
148
77
  */
149
78
  export declare const SVG_LOWERCASE_TO_CAMELCASE: Map<string, string>;
150
79
  export declare const VALID_ARIA_ROLES: Set<string>;
80
+ /**
81
+ * Abstract ARIA roles used to support the WAI-ARIA Roles Model.
82
+ * Authors MUST NOT use abstract roles in content.
83
+ * @see https://www.w3.org/TR/wai-aria-1.0/roles#abstract_roles
84
+ */
85
+ export declare const ABSTRACT_ARIA_ROLES: Set<string>;
151
86
  /**
152
87
  * Parameter types for AttributeVisitorMixin methods
153
88
  */
@@ -233,14 +168,6 @@ export declare abstract class AttributeVisitorMixin<TAutofixContext extends Base
233
168
  */
234
169
  protected checkDynamicAttributeDynamicValue(_params: DynamicAttributeDynamicValueParams): void;
235
170
  }
236
- /**
237
- * Checks if an attribute value is quoted
238
- */
239
- export declare function isAttributeValueQuoted(attributeNode: HTMLAttributeNode): boolean;
240
- /**
241
- * Iterates over all attributes of a tag node, calling the callback for each attribute
242
- */
243
- export declare function forEachAttribute(node: HTMLOpenTagNode, callback: (attributeNode: HTMLAttributeNode) => void): void;
244
171
  /**
245
172
  * Base lexer visitor class that provides common functionality for lexer-based rule visitors
246
173
  */
@@ -253,11 +180,11 @@ export declare abstract class BaseLexerRuleVisitor<TAutofixContext extends BaseA
253
180
  * Helper method to create an unbound lint offense (without severity).
254
181
  * The Linter will bind severity based on the rule's config.
255
182
  */
256
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext): UnboundLintOffense<TAutofixContext>;
183
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext>;
257
184
  /**
258
185
  * Helper method to add an offense to the offenses array
259
186
  */
260
- protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext): void;
187
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): void;
261
188
  /**
262
189
  * Main entry point for lexer rule visitors
263
190
  * @param lexResult - The lexer result containing tokens and source
@@ -286,11 +213,11 @@ export declare abstract class BaseSourceRuleVisitor<TAutofixContext extends Base
286
213
  * Helper method to create an unbound lint offense (without severity).
287
214
  * The Linter will bind severity based on the rule's config.
288
215
  */
289
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext): UnboundLintOffense<TAutofixContext>;
216
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext>;
290
217
  /**
291
218
  * Helper method to add an offense to the offenses array
292
219
  */
293
- protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext): void;
220
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): void;
294
221
  /**
295
222
  * Main entry point for source rule visitors
296
223
  * @param source - The raw source code
@@ -349,3 +276,23 @@ export declare function isHeadAndBodyTag(tagName: string): boolean;
349
276
  export declare function isBodyOnlyTag(tagName: string): boolean;
350
277
  export declare function isBodyTag(tagName: string): boolean;
351
278
  export declare function isHeadTag(tagName: string): boolean;
279
+ /**
280
+ * Converts a character offset in a source string to a Position (line, column).
281
+ * Lines are 1-based, columns are 0-based.
282
+ */
283
+ export declare function positionFromOffset(source: string, offset: number): Position;
284
+ /**
285
+ * Creates a Location from a source string, a start offset, and a length.
286
+ */
287
+ export declare function locationFromOffset(source: string, startOffset: number, length: number): Location;
288
+ /**
289
+ * Finds a node in the AST that contains a specific position.
290
+ * Returns the deepest (most specific) node that matches the position and optional predicate.
291
+ *
292
+ * @param root - The root node to search from
293
+ * @param line - Line number (1-based)
294
+ * @param column - Column number (0-based)
295
+ * @param predicate - Optional predicate function to filter nodes
296
+ * @returns The matching node or null if not found
297
+ */
298
+ export declare function findNodeAtPosition(root: Node, line: number, column: number, predicate?: (node: Node) => boolean): Node | null;
@@ -8,7 +8,7 @@ interface SVGTagNameCapitalizationAutofixContext extends BaseAutofixContext {
8
8
  }
9
9
  export declare class SVGTagNameCapitalizationRule extends ParserRule<SVGTagNameCapitalizationAutofixContext> {
10
10
  static autocorrectable: boolean;
11
- name: string;
11
+ static ruleName: string;
12
12
  get defaultConfig(): FullRuleConfig;
13
13
  check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<SVGTagNameCapitalizationAutofixContext>[];
14
14
  autofix(offense: LintOffense<SVGTagNameCapitalizationAutofixContext>, result: ParseResult, _context?: Partial<LintContext>): ParseResult | null;
@@ -1,8 +1,8 @@
1
1
  import { ParserRule } from "../types.js";
2
2
  import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js";
3
3
  import type { ParseResult } from "@herb-tools/core";
4
- export declare class HTMLAriaRoleMustBeValidRule extends ParserRule {
5
- name: string;
4
+ export declare class TurboPermanentRequireIdRule extends ParserRule {
5
+ static ruleName: string;
6
6
  get defaultConfig(): FullRuleConfig;
7
7
  check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[];
8
8
  }
@@ -1,16 +1,17 @@
1
- import { Diagnostic, LexResult, ParseResult } from "@herb-tools/core";
1
+ import { Diagnostic, LexResult, ParseResult, Location } from "@herb-tools/core";
2
2
  import type { rules } from "./rules.js";
3
- import type { Node } from "@herb-tools/core";
3
+ import type { Node, ParserOptions } from "@herb-tools/core";
4
4
  import type { RuleConfig } from "@herb-tools/config";
5
5
  import type { Mutable } from "@herb-tools/rewriter";
6
6
  export type { Mutable } from "@herb-tools/rewriter";
7
7
  export type LintSeverity = "error" | "warning" | "info" | "hint";
8
+ export declare const DEFAULT_LINTER_PARSER_OPTIONS: Partial<ParserOptions>;
8
9
  export type FullRuleConfig = Required<Pick<RuleConfig, 'enabled' | 'severity'>> & Omit<RuleConfig, 'enabled' | 'severity'>;
9
10
  /**
10
11
  * Automatically inferred union type of all available linter rule names.
11
- * This type extracts the 'name' property from each rule class instance.
12
+ * This type extracts the 'ruleName' property from each rule class.
12
13
  */
13
- export type LinterRule = InstanceType<typeof rules[number]>['name'];
14
+ export type LinterRule = (typeof rules[number])['ruleName'];
14
15
  /**
15
16
  * Base context for autofix operations. Contains the offending node.
16
17
  * Rules can extend this interface to include rule-specific autofix data.
@@ -19,6 +20,8 @@ export type LinterRule = InstanceType<typeof rules[number]>['name'];
19
20
  export interface BaseAutofixContext {
20
21
  /** The AST node, token, or data structure that caused the offense (mutable) */
21
22
  node: Mutable<Node>;
23
+ /** If true, this fix requires --fix-unsafely to be applied */
24
+ unsafe?: boolean;
22
25
  }
23
26
  /**
24
27
  * A lint offense without severity bound. Rules produce these, and the Linter
@@ -28,6 +31,8 @@ export interface UnboundLintOffense<TAutofixContext extends BaseAutofixContext =
28
31
  rule: LinterRule;
29
32
  /** Context data for autofix, including the offending node and rule-specific data */
30
33
  autofixContext?: TAutofixContext;
34
+ /** If set, overrides rule-level severity for this specific offense */
35
+ severity?: LintSeverity;
31
36
  }
32
37
  /**
33
38
  * A lint offense with severity bound. The Linter produces these by binding
@@ -66,12 +71,17 @@ export declare const DEFAULT_RULE_CONFIG: FullRuleConfig;
66
71
  */
67
72
  export declare abstract class ParserRule<TAutofixContext extends BaseAutofixContext = BaseAutofixContext> {
68
73
  static type: "parser";
74
+ static ruleName: string;
69
75
  /** Indicates whether this rule supports autofix. Defaults to false. */
70
76
  static autocorrectable: boolean;
71
77
  /** Indicates whether this rule supports unsafe autofix (requires --fix-unsafely). Defaults to false. */
72
78
  static unsafeAutocorrectable: boolean;
73
- abstract name: string;
79
+ /** Indicates whether the source should be re-indented after autofix. Defaults to false. */
80
+ static reindentAfterAutofix: boolean;
81
+ get ruleName(): string;
74
82
  get defaultConfig(): FullRuleConfig;
83
+ get parserOptions(): Partial<ParserOptions>;
84
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext>;
75
85
  abstract check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<TAutofixContext>[];
76
86
  /**
77
87
  * Optional method to determine if this rule should run.
@@ -96,12 +106,14 @@ export declare abstract class ParserRule<TAutofixContext extends BaseAutofixCont
96
106
  */
97
107
  export declare abstract class LexerRule<TAutofixContext extends BaseAutofixContext = BaseAutofixContext> {
98
108
  static type: "lexer";
109
+ static ruleName: string;
99
110
  /** Indicates whether this rule supports autofix. Defaults to false. */
100
111
  static autocorrectable: boolean;
101
112
  /** Indicates whether this rule supports unsafe autofix (requires --fix-unsafely). Defaults to false. */
102
113
  static unsafeAutocorrectable: boolean;
103
- abstract name: string;
114
+ get ruleName(): string;
104
115
  get defaultConfig(): FullRuleConfig;
116
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext>;
105
117
  abstract check(lexResult: LexResult, context?: Partial<LintContext>): UnboundLintOffense<TAutofixContext>[];
106
118
  /**
107
119
  * Optional method to determine if this rule should run.
@@ -124,6 +136,7 @@ export declare abstract class LexerRule<TAutofixContext extends BaseAutofixConte
124
136
  export interface LexerRuleConstructor {
125
137
  type: "lexer";
126
138
  new (): LexerRule;
139
+ ruleName: string;
127
140
  }
128
141
  /**
129
142
  * Complete lint context with all properties defined.
@@ -141,12 +154,14 @@ export interface LintContext {
141
154
  export declare const DEFAULT_LINT_CONTEXT: LintContext;
142
155
  export declare abstract class SourceRule<TAutofixContext extends BaseAutofixContext = BaseAutofixContext> {
143
156
  static type: "source";
157
+ static ruleName: string;
144
158
  /** Indicates whether this rule supports autofix. Defaults to false. */
145
159
  static autocorrectable: boolean;
146
160
  /** Indicates whether this rule supports unsafe autofix (requires --fix-unsafely). Defaults to false. */
147
161
  static unsafeAutocorrectable: boolean;
148
- abstract name: string;
162
+ get ruleName(): string;
149
163
  get defaultConfig(): FullRuleConfig;
164
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext>;
150
165
  abstract check(source: string, context?: Partial<LintContext>): UnboundLintOffense<TAutofixContext>[];
151
166
  /**
152
167
  * Optional method to determine if this rule should run.
@@ -169,6 +184,7 @@ export declare abstract class SourceRule<TAutofixContext extends BaseAutofixCont
169
184
  export interface SourceRuleConstructor {
170
185
  type: "source";
171
186
  new (): SourceRule;
187
+ ruleName: string;
172
188
  }
173
189
  /**
174
190
  * Type representing a parser/AST rule class constructor.
@@ -177,6 +193,8 @@ export interface SourceRuleConstructor {
177
193
  */
178
194
  export type ParserRuleClass = (new () => ParserRule) & {
179
195
  type?: "parser";
196
+ ruleName: string;
197
+ reindentAfterAutofix?: boolean;
180
198
  };
181
199
  export type LexerRuleClass = LexerRuleConstructor;
182
200
  export type SourceRuleClass = SourceRuleConstructor;
@@ -0,0 +1 @@
1
+ export declare function ruleDocumentationUrl(ruleId: string): string;
@@ -1,3 +1,6 @@
1
+ export const DEFAULT_LINTER_PARSER_OPTIONS = {
2
+ track_whitespace: true,
3
+ };
1
4
  /**
2
5
  * Default configuration for rules when defaultConfig is not specified.
3
6
  * Custom rules can omit defaultConfig and will use these defaults.
@@ -12,26 +15,61 @@ export const DEFAULT_RULE_CONFIG = {
12
15
  */
13
16
  export class ParserRule {
14
17
  static type = "parser";
18
+ static ruleName;
15
19
  /** Indicates whether this rule supports autofix. Defaults to false. */
16
20
  static autocorrectable = false;
17
21
  /** Indicates whether this rule supports unsafe autofix (requires --fix-unsafely). Defaults to false. */
18
22
  static unsafeAutocorrectable = false;
23
+ /** Indicates whether the source should be re-indented after autofix. Defaults to false. */
24
+ static reindentAfterAutofix = false;
25
+ get ruleName() {
26
+ return this.constructor.ruleName;
27
+ }
19
28
  get defaultConfig() {
20
29
  return DEFAULT_RULE_CONFIG;
21
30
  }
31
+ get parserOptions() {
32
+ return DEFAULT_LINTER_PARSER_OPTIONS;
33
+ }
34
+ createOffense(message, location, autofixContext, severity) {
35
+ return {
36
+ rule: this.ruleName,
37
+ code: this.ruleName,
38
+ source: "Herb Linter",
39
+ message,
40
+ location,
41
+ autofixContext,
42
+ severity,
43
+ };
44
+ }
22
45
  }
23
46
  /**
24
47
  * Base class for lexer rules.
25
48
  */
26
49
  export class LexerRule {
27
50
  static type = "lexer";
51
+ static ruleName;
28
52
  /** Indicates whether this rule supports autofix. Defaults to false. */
29
53
  static autocorrectable = false;
30
54
  /** Indicates whether this rule supports unsafe autofix (requires --fix-unsafely). Defaults to false. */
31
55
  static unsafeAutocorrectable = false;
56
+ get ruleName() {
57
+ return this.constructor.ruleName;
58
+ }
32
59
  get defaultConfig() {
33
60
  return DEFAULT_RULE_CONFIG;
34
61
  }
62
+ createOffense(message, location, autofixContext, severity) {
63
+ return {
64
+ rule: this.ruleName,
65
+ code: this.ruleName,
66
+ source: "Herb Linter",
67
+ message,
68
+ location,
69
+ autofixContext,
70
+ severity,
71
+ };
72
+ }
35
73
  }
36
74
  /**
37
75
  * Default context object with all keys defined but set to undefined
@@ -44,12 +82,27 @@ export const DEFAULT_LINT_CONTEXT = {
44
82
  };
45
83
  export class SourceRule {
46
84
  static type = "source";
85
+ static ruleName;
47
86
  /** Indicates whether this rule supports autofix. Defaults to false. */
48
87
  static autocorrectable = false;
49
88
  /** Indicates whether this rule supports unsafe autofix (requires --fix-unsafely). Defaults to false. */
50
89
  static unsafeAutocorrectable = false;
90
+ get ruleName() {
91
+ return this.constructor.ruleName;
92
+ }
51
93
  get defaultConfig() {
52
94
  return DEFAULT_RULE_CONFIG;
53
95
  }
96
+ createOffense(message, location, autofixContext, severity) {
97
+ return {
98
+ rule: this.ruleName,
99
+ code: this.ruleName,
100
+ source: "Herb Linter",
101
+ message,
102
+ location,
103
+ autofixContext,
104
+ severity,
105
+ };
106
+ }
54
107
  }
55
108
  //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,6BAA6B,GAA2B;IACnE,gBAAgB,EAAE,IAAI;CACvB,CAAA;AAiED;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,EAAE;CACZ,CAAA;AAED;;GAEG;AACH,MAAM,OAAgB,UAAU;IAC9B,MAAM,CAAC,IAAI,GAAG,QAAiB,CAAA;IAC/B,MAAM,CAAC,QAAQ,CAAQ;IACvB,uEAAuE;IACvE,MAAM,CAAC,eAAe,GAAG,KAAK,CAAA;IAC9B,wGAAwG;IACxG,MAAM,CAAC,qBAAqB,GAAG,KAAK,CAAA;IACpC,2FAA2F;IAC3F,MAAM,CAAC,oBAAoB,GAAG,KAAK,CAAA;IAEnC,IAAI,QAAQ;QACV,OAAQ,IAAI,CAAC,WAAiC,CAAC,QAAQ,CAAA;IACzD,CAAC;IAED,IAAI,aAAa;QACf,OAAO,mBAAmB,CAAA;IAC5B,CAAC;IAED,IAAI,aAAa;QACf,OAAO,6BAA6B,CAAA;IACtC,CAAC;IAES,aAAa,CAAC,OAAe,EAAE,QAAkB,EAAE,cAAgC,EAAE,QAAuB;QACpH,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,MAAM,EAAE,aAAa;YACrB,OAAO;YACP,QAAQ;YACR,cAAc;YACd,QAAQ;SACT,CAAA;IACH,CAAC;;AAwBH;;GAEG;AACH,MAAM,OAAgB,SAAS;IAC7B,MAAM,CAAC,IAAI,GAAG,OAAgB,CAAA;IAC9B,MAAM,CAAC,QAAQ,CAAQ;IACvB,uEAAuE;IACvE,MAAM,CAAC,eAAe,GAAG,KAAK,CAAA;IAC9B,wGAAwG;IACxG,MAAM,CAAC,qBAAqB,GAAG,KAAK,CAAA;IAEpC,IAAI,QAAQ;QACV,OAAQ,IAAI,CAAC,WAAgC,CAAC,QAAQ,CAAA;IACxD,CAAC;IAED,IAAI,aAAa;QACf,OAAO,mBAAmB,CAAA;IAC5B,CAAC;IAES,aAAa,CAAC,OAAe,EAAE,QAAkB,EAAE,cAAgC,EAAE,QAAuB;QACpH,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,MAAM,EAAE,aAAa;YACrB,OAAO;YACP,QAAQ;YACR,cAAc;YACd,QAAQ;SACT,CAAA;IACH,CAAC;;AAyCH;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAC/C,QAAQ,EAAE,SAAS;IACnB,cAAc,EAAE,SAAS;IACzB,qBAAqB,EAAE,SAAS;IAChC,qBAAqB,EAAE,SAAS;CACxB,CAAA;AAEV,MAAM,OAAgB,UAAU;IAC9B,MAAM,CAAC,IAAI,GAAG,QAAiB,CAAA;IAC/B,MAAM,CAAC,QAAQ,CAAQ;IACvB,uEAAuE;IACvE,MAAM,CAAC,eAAe,GAAG,KAAK,CAAA;IAC9B,wGAAwG;IACxG,MAAM,CAAC,qBAAqB,GAAG,KAAK,CAAA;IAEpC,IAAI,QAAQ;QACV,OAAQ,IAAI,CAAC,WAAiC,CAAC,QAAQ,CAAA;IACzD,CAAC;IAED,IAAI,aAAa;QACf,OAAO,mBAAmB,CAAA;IAC5B,CAAC;IAES,aAAa,CAAC,OAAe,EAAE,QAAkB,EAAE,cAAgC,EAAE,QAAuB;QACpH,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,MAAM,EAAE,aAAa;YACrB,OAAO;YACP,QAAQ;YACR,cAAc;YACd,QAAQ;SACT,CAAA;IACH,CAAC"}
package/dist/urls.js ADDED
@@ -0,0 +1,5 @@
1
+ const DOCS_BASE_URL = "https://herb-tools.dev/linter/rules";
2
+ export function ruleDocumentationUrl(ruleId) {
3
+ return `${DOCS_BASE_URL}/${ruleId}`;
4
+ }
5
+ //# sourceMappingURL=urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../src/urls.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,qCAAqC,CAAA;AAE3D,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,GAAG,aAAa,IAAI,MAAM,EAAE,CAAA;AACrC,CAAC"}
@@ -4,16 +4,32 @@ This page contains documentation for all Herb Linter rules.
4
4
 
5
5
  ## Available Rules
6
6
 
7
+ - [`actionview-no-silent-helper`](./actionview-no-silent-helper.md) - Disallow silent ERB tags for Action View helpers
7
8
  - [`erb-comment-syntax`](./erb-comment-syntax.md) - Disallow Ruby comments immediately after ERB tags
8
9
  - [`erb-no-case-node-children`](./erb-no-case-node-children.md) - Don't use `children` for `case/when` and `case/in` nodes
10
+ - [`erb-no-conditional-html-element`](./erb-no-conditional-html-element.md) - Disallow conditional HTML elements
11
+ - [`erb-no-duplicate-branch-elements`](./erb-no-duplicate-branch-elements.md) - Disallow duplicate elements across conditional branches
9
12
  - [`erb-no-empty-tags`](./erb-no-empty-tags.md) - Disallow empty ERB tags
10
13
  - [`erb-no-extra-newline`](./erb-no-extra-newline.md) - Disallow extra newlines.
11
14
  - [`erb-no-extra-whitespace-inside-tags`](./erb-no-extra-whitespace-inside-tags.md) - Disallow multiple consecutive spaces inside ERB tags
15
+ - [`erb-no-inline-case-conditions`](./erb-no-inline-case-conditions.md) - Disallow inline `case`/`when` and `case`/`in` conditions in a single ERB tag
16
+ - [`erb-no-instance-variables-in-partials`](./erb-no-instance-variables-in-partials.md) - Disallow instance variables in partials
17
+ - [`erb-no-interpolated-class-names`](./erb-no-interpolated-class-names.md) - Disallow ERB interpolation inside CSS class names
18
+ - [`erb-no-javascript-tag-helper`](./erb-no-javascript-tag-helper.md) - Disallow `javascript_tag` helper
12
19
  - [`erb-no-output-control-flow`](./erb-no-output-control-flow.md) - Prevents outputting control flow blocks
20
+ - [`erb-no-output-in-attribute-name`](./erb-no-output-in-attribute-name.md) - Disallow ERB output in attribute names
21
+ - [`erb-no-output-in-attribute-position`](./erb-no-output-in-attribute-position.md) - Disallow ERB output in attribute position
22
+ - [`erb-no-raw-output-in-attribute-value`](./erb-no-raw-output-in-attribute-value.md) - Disallow `<%==` in attribute values
13
23
  - [`erb-no-silent-tag-in-attribute-name`](./erb-no-silent-tag-in-attribute-name.md) - Disallow ERB silent tags in HTML attribute names
24
+ - [`erb-no-statement-in-script`](./erb-no-statement-in-script.md) - Disallow ERB statements inside `<script>` tags
25
+ - [`erb-no-then-in-control-flow`](./erb-no-then-in-control-flow.md) - Disallow `then` in ERB control flow expressions
26
+ - [`erb-no-trailing-whitespace`](./erb-no-trailing-whitespace.md) - Disallow trailing whitespace at end of lines.
27
+ - [`erb-no-unsafe-js-attribute`](./erb-no-unsafe-js-attribute.md) - Disallow unsafe ERB output in JavaScript attributes
28
+ - [`erb-no-unsafe-raw`](./erb-no-unsafe-raw.md) - Disallow `raw()` and `.html_safe` in ERB output
29
+ - [`erb-no-unsafe-script-interpolation`](./erb-no-unsafe-script-interpolation.md) - Disallow unsafe ERB output inside `<script>` tags
14
30
  - [`erb-prefer-image-tag-helper`](./erb-prefer-image-tag-helper.md) - Prefer `image_tag` helper over `<img>` with ERB expressions
15
- - [`erb-require-whitespace-inside-tags`](./erb-require-whitespace-inside-tags.md) - Requires whitespace around ERB tags
16
31
  - [`erb-require-trailing-newline`](./erb-require-trailing-newline.md) - Enforces that all HTML+ERB template files end with exactly one trailing newline character.
32
+ - [`erb-require-whitespace-inside-tags`](./erb-require-whitespace-inside-tags.md) - Requires whitespace around ERB tags
17
33
  - [`erb-right-trim`](./erb-right-trim.md) - Enforce consistent right-trimming syntax.
18
34
  - [`erb-strict-locals-comment-syntax`](./erb-strict-locals-comment-syntax.md) - Enforce strict locals comment syntax.
19
35
  - [`herb-disable-comment-malformed`](./herb-disable-comment-malformed.md) - Detect malformed `herb:disable` comments.
@@ -22,6 +38,7 @@ This page contains documentation for all Herb Linter rules.
22
38
  - [`herb-disable-comment-no-redundant-all`](./herb-disable-comment-no-redundant-all.md) - Disallow redundant use of `all` in `herb:disable` comments.
23
39
  - [`herb-disable-comment-unnecessary`](./herb-disable-comment-unnecessary.md) - Detect unnecessary `herb:disable` comments.
24
40
  - [`herb-disable-comment-valid-rule-name`](./herb-disable-comment-valid-rule-name.md) - Validate rule names in `herb:disable` comments.
41
+ - [`html-allowed-script-type`](./html-allowed-script-type.md) - Restrict allowed `type` attributes for `<script>` tags
25
42
  - [`html-anchor-require-href`](./html-anchor-require-href.md) - Requires an href attribute on anchor tags
26
43
  - [`html-aria-attribute-must-be-valid`](./html-aria-attribute-must-be-valid.md) - Disallow invalid or unknown `aria-*` attributes.
27
44
  - [`html-aria-label-is-well-formatted`](./html-aria-label-is-well-formatted.md) - `aria-label` must be well-formatted
@@ -33,12 +50,15 @@ This page contains documentation for all Herb Linter rules.
33
50
  - [`html-attribute-values-require-quotes`](./html-attribute-values-require-quotes.md) - Requires quotes around attribute values
34
51
  - [`html-avoid-both-disabled-and-aria-disabled`](./html-avoid-both-disabled-and-aria-disabled.md) - Avoid using both `disabled` and `aria-disabled` attributes
35
52
  - [`html-body-only-elements`](./html-body-only-elements.md) - Require content elements inside `<body>`.
53
+ - [`html-details-has-summary`](./html-details-has-summary.md) - Require `<summary>` in `<details>` elements
36
54
  - [`html-boolean-attributes-no-value`](./html-boolean-attributes-no-value.md) - Prevents values on boolean attributes
37
55
  - [`html-head-only-elements`](./html-head-only-elements.md) - Require head-scoped elements inside `<head>`.
38
56
  - [`html-iframe-has-title`](./html-iframe-has-title.md) - `iframe` elements must have a `title` attribute
39
- - [`html-input-require-autocomplete`](./html-input-require-autocomplete.md) - Require `autocomplete` attributes on `<input>` tags.
40
57
  - [`html-img-require-alt`](./html-img-require-alt.md) - Requires `alt` attributes on `<img>` tags
58
+ - [`html-input-require-autocomplete`](./html-input-require-autocomplete.md) - Require `autocomplete` attributes on `<input>` tags.
41
59
  - [`html-navigation-has-label`](./html-navigation-has-label.md) - Navigation landmarks must have accessible labels
60
+ - [`html-no-abstract-roles`](./html-no-abstract-roles.md) - No abstract ARIA roles
61
+ - [`html-no-aria-hidden-on-body`](./html-no-aria-hidden-on-body.md) - No `aria-hidden` on `<body>`
42
62
  - [`html-no-aria-hidden-on-focusable`](./html-no-aria-hidden-on-focusable.md) - Focusable elements should not have `aria-hidden="true"`
43
63
  - [`html-no-block-inside-inline`](./html-no-block-inside-inline.md) - Prevents block-level elements inside inline elements
44
64
  - [`html-no-duplicate-attributes`](./html-no-duplicate-attributes.md) - Prevents duplicate attributes on HTML elements
@@ -54,6 +74,7 @@ This page contains documentation for all Herb Linter rules.
54
74
  - [`html-tag-name-lowercase`](./html-tag-name-lowercase.md) - Enforces lowercase tag names in HTML
55
75
  - [`parser-no-errors`](./parser-no-errors.md) - Disallow parser errors in HTML+ERB documents
56
76
  - [`svg-tag-name-capitalization`](./svg-tag-name-capitalization.md) - Enforces proper camelCase capitalization for SVG elements
77
+ - [`turbo-permanent-require-id`](./turbo-permanent-require-id.md) - Require `id` attribute on elements with `data-turbo-permanent`
57
78
 
58
79
  ## Contributing
59
80
 
@@ -0,0 +1,57 @@
1
+ # Linter Rule: Disallow silent ERB tags for Action View helpers
2
+
3
+ **Rule:** `actionview-no-silent-helper`
4
+
5
+ ## Description
6
+
7
+ Action View helpers like `link_to`, `form_with`, and `content_tag` must use output ERB tags (`<%= %>`) so their rendered HTML is included in the page. Using silent ERB tags (`<% %>`) discards the helper's output entirely.
8
+
9
+ ## Rationale
10
+
11
+ Action View helpers generate HTML that needs to be rendered into the template. When a helper is called with a silent ERB tag (`<% %>`), the return value is evaluated but silently discarded, meaning the generated HTML never appears in the output. This is almost always a mistake and can lead to confusing bugs where elements are missing from the page with no obvious cause. Using `<%= %>` ensures the helper's output is properly rendered.
12
+
13
+ ## Examples
14
+
15
+ ### ✅ Good
16
+
17
+ ```erb
18
+ <%= link_to "Home", root_path %>
19
+ ```
20
+
21
+ ```erb
22
+ <%= form_with model: @user do |f| %>
23
+ <%= f.text_field :name %>
24
+ <% end %>
25
+ ```
26
+
27
+ ```erb
28
+ <%= button_to "Delete", user_path(@user), method: :delete %>
29
+ ```
30
+
31
+ ```erb
32
+ <%= content_tag :div, "Hello", class: "greeting" %>
33
+ ```
34
+
35
+ ### 🚫 Bad
36
+
37
+ ```erb
38
+ <% link_to "Home", root_path %>
39
+ ```
40
+
41
+ ```erb
42
+ <% form_with model: @user do |f| %>
43
+ <%= f.text_field :name %>
44
+ <% end %>
45
+ ```
46
+
47
+ ```erb
48
+ <% button_to "Delete", user_path(@user), method: :delete %>
49
+ ```
50
+
51
+ ```erb
52
+ <% content_tag :div, "Hello", class: "greeting" %>
53
+ ```
54
+
55
+ ## References
56
+
57
+ * [Rails Action View Helpers documentation](https://api.rubyonrails.org/classes/ActionView/Helpers.html)
@@ -0,0 +1,90 @@
1
+ # Linter Rule: Disallow conditional HTML elements
2
+
3
+ **Rule:** `erb-no-conditional-html-element`
4
+
5
+ ## Description
6
+
7
+ Disallow the pattern of opening an HTML tag in one conditional block and closing it in another conditional block with the same condition. This creates a "conditional element" where the element's existence depends on a runtime condition.
8
+
9
+ ## Rationale
10
+
11
+ This pattern is difficult to read, maintain, and reason about. It can lead to confusion when trying to understand the document structure, and it makes the template harder to format, lint, and analyze. The opening and closing tags are visually separated, making it non-obvious that they form a matched pair.
12
+
13
+ Instead, prefer using `capture` blocks with `content_tag` helpers, which make the conditional wrapping explicit and keeps the logic together.
14
+
15
+ ## Examples
16
+
17
+ ### ✅ Good
18
+
19
+ Using a `capture` block to conditionally wrap content:
20
+
21
+ ```erb
22
+ <% content = capture do %>
23
+ <div>Content</div>
24
+ <% end %>
25
+
26
+ <% if wrap_in_dialog? %>
27
+ <dialog><%= content %></dialog>
28
+ <% else %>
29
+ <%= content %>
30
+ <% end %>
31
+ ```
32
+
33
+ ```erb
34
+ <% content = capture do %>
35
+ <div>Content</div>
36
+ <% end %>
37
+
38
+ <%= wrap_in_dialog? ? content_tag(:dialog, content) : content %>
39
+ ```
40
+
41
+ Complete elements within conditional branches:
42
+
43
+ ```erb
44
+ <% if some_condition %>
45
+ <div class="a">Content</div>
46
+ <% else %>
47
+ <div class="b">Content</div>
48
+ <% end %>
49
+ ```
50
+
51
+ ### 🚫 Bad
52
+
53
+ Opening and closing tags in separate conditional blocks:
54
+
55
+ ```erb
56
+ <% if wrap_in_dialog? %>
57
+ <dialog>
58
+ <% end %>
59
+
60
+ <div>Stuff</div>
61
+
62
+ <% if wrap_in_dialog? %>
63
+ </dialog>
64
+ <% end %>
65
+ ```
66
+
67
+ ```erb
68
+ <% if @with_icon %>
69
+ <div class="icon">
70
+ <% end %>
71
+ <span>Hello</span>
72
+ <% if @with_icon %>
73
+ </div>
74
+ <% end %>
75
+ ```
76
+
77
+ ```erb
78
+ <% if @with_icon %>
79
+ <div class="icon">
80
+ <% else %>
81
+ <div class="no-icon">
82
+ <% end %>
83
+
84
+ <span>Hello</span>
85
+ </div>
86
+ ```
87
+
88
+ ## References
89
+
90
+ \-