@herb-tools/linter 0.7.4 → 0.8.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 (395) hide show
  1. package/README.md +253 -13
  2. package/dist/herb-lint.js +26087 -3414
  3. package/dist/herb-lint.js.map +1 -1
  4. package/dist/index.cjs +5783 -1568
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.js +5749 -1569
  7. package/dist/index.js.map +1 -1
  8. package/dist/loader.cjs +17010 -0
  9. package/dist/loader.cjs.map +1 -0
  10. package/dist/loader.js +16879 -0
  11. package/dist/loader.js.map +1 -0
  12. package/dist/package.json +13 -5
  13. package/dist/src/cli/argument-parser.js +42 -35
  14. package/dist/src/cli/argument-parser.js.map +1 -1
  15. package/dist/src/cli/file-processor.js +124 -23
  16. package/dist/src/cli/file-processor.js.map +1 -1
  17. package/dist/src/cli/formatters/detailed-formatter.js +18 -3
  18. package/dist/src/cli/formatters/detailed-formatter.js.map +1 -1
  19. package/dist/src/cli/formatters/github-actions-formatter.js +15 -1
  20. package/dist/src/cli/formatters/github-actions-formatter.js.map +1 -1
  21. package/dist/src/cli/formatters/json-formatter.js +3 -0
  22. package/dist/src/cli/formatters/json-formatter.js.map +1 -1
  23. package/dist/src/cli/formatters/simple-formatter.js +20 -7
  24. package/dist/src/cli/formatters/simple-formatter.js.map +1 -1
  25. package/dist/src/cli/output-manager.js +22 -3
  26. package/dist/src/cli/output-manager.js.map +1 -1
  27. package/dist/src/cli/summary-reporter.js +26 -3
  28. package/dist/src/cli/summary-reporter.js.map +1 -1
  29. package/dist/src/cli.js +109 -43
  30. package/dist/src/cli.js.map +1 -1
  31. package/dist/src/custom-rule-loader.js +139 -0
  32. package/dist/src/custom-rule-loader.js.map +1 -0
  33. package/dist/src/herb-disable-comment-utils.js +129 -0
  34. package/dist/src/herb-disable-comment-utils.js.map +1 -0
  35. package/dist/src/index.js +1 -0
  36. package/dist/src/index.js.map +1 -1
  37. package/dist/src/linter.js +369 -34
  38. package/dist/src/linter.js.map +1 -1
  39. package/dist/src/loader.js +17 -0
  40. package/dist/src/loader.js.map +1 -0
  41. package/dist/src/rules/erb-comment-syntax.js +48 -0
  42. package/dist/src/rules/erb-comment-syntax.js.map +1 -0
  43. package/dist/src/rules/erb-no-case-node-children.js +52 -0
  44. package/dist/src/rules/erb-no-case-node-children.js.map +1 -0
  45. package/dist/src/rules/erb-no-empty-tags.js +7 -1
  46. package/dist/src/rules/erb-no-empty-tags.js.map +1 -1
  47. package/dist/src/rules/erb-no-extra-newline.js +65 -0
  48. package/dist/src/rules/erb-no-extra-newline.js.map +1 -0
  49. package/dist/src/rules/erb-no-extra-whitespace-inside-tags.js +95 -0
  50. package/dist/src/rules/erb-no-extra-whitespace-inside-tags.js.map +1 -0
  51. package/dist/src/rules/erb-no-output-control-flow.js +7 -1
  52. package/dist/src/rules/erb-no-output-control-flow.js.map +1 -1
  53. package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js +7 -1
  54. package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js.map +1 -1
  55. package/dist/src/rules/erb-prefer-image-tag-helper.js +7 -1
  56. package/dist/src/rules/erb-prefer-image-tag-helper.js.map +1 -1
  57. package/dist/src/rules/erb-require-trailing-newline.js +35 -0
  58. package/dist/src/rules/erb-require-trailing-newline.js.map +1 -0
  59. package/dist/src/rules/erb-require-whitespace-inside-tags.js +70 -20
  60. package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -1
  61. package/dist/src/rules/erb-right-trim.js +45 -0
  62. package/dist/src/rules/erb-right-trim.js.map +1 -0
  63. package/dist/src/rules/herb-disable-comment-base.js +51 -0
  64. package/dist/src/rules/herb-disable-comment-base.js.map +1 -0
  65. package/dist/src/rules/herb-disable-comment-malformed.js +51 -0
  66. package/dist/src/rules/herb-disable-comment-malformed.js.map +1 -0
  67. package/dist/src/rules/herb-disable-comment-missing-rules.js +29 -0
  68. package/dist/src/rules/herb-disable-comment-missing-rules.js.map +1 -0
  69. package/dist/src/rules/herb-disable-comment-no-duplicate-rules.js +32 -0
  70. package/dist/src/rules/herb-disable-comment-no-duplicate-rules.js.map +1 -0
  71. package/dist/src/rules/herb-disable-comment-no-redundant-all.js +31 -0
  72. package/dist/src/rules/herb-disable-comment-no-redundant-all.js.map +1 -0
  73. package/dist/src/rules/herb-disable-comment-unnecessary.js +65 -0
  74. package/dist/src/rules/herb-disable-comment-unnecessary.js.map +1 -0
  75. package/dist/src/rules/herb-disable-comment-valid-rule-name.js +44 -0
  76. package/dist/src/rules/herb-disable-comment-valid-rule-name.js.map +1 -0
  77. package/dist/src/rules/html-anchor-require-href.js +7 -1
  78. package/dist/src/rules/html-anchor-require-href.js.map +1 -1
  79. package/dist/src/rules/html-aria-attribute-must-be-valid.js +7 -1
  80. package/dist/src/rules/html-aria-attribute-must-be-valid.js.map +1 -1
  81. package/dist/src/rules/html-aria-label-is-well-formatted.js +9 -3
  82. package/dist/src/rules/html-aria-label-is-well-formatted.js.map +1 -1
  83. package/dist/src/rules/html-aria-level-must-be-valid.js +6 -0
  84. package/dist/src/rules/html-aria-level-must-be-valid.js.map +1 -1
  85. package/dist/src/rules/html-aria-role-heading-requires-level.js +7 -1
  86. package/dist/src/rules/html-aria-role-heading-requires-level.js.map +1 -1
  87. package/dist/src/rules/html-aria-role-must-be-valid.js +7 -1
  88. package/dist/src/rules/html-aria-role-must-be-valid.js.map +1 -1
  89. package/dist/src/rules/html-attribute-double-quotes.js +29 -2
  90. package/dist/src/rules/html-attribute-double-quotes.js.map +1 -1
  91. package/dist/src/rules/html-attribute-equals-spacing.js +18 -2
  92. package/dist/src/rules/html-attribute-equals-spacing.js.map +1 -1
  93. package/dist/src/rules/html-attribute-values-require-quotes.js +39 -3
  94. package/dist/src/rules/html-attribute-values-require-quotes.js.map +1 -1
  95. package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js +7 -1
  96. package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js.map +1 -1
  97. package/dist/src/rules/html-body-only-elements.js +46 -0
  98. package/dist/src/rules/html-body-only-elements.js.map +1 -0
  99. package/dist/src/rules/html-boolean-attributes-no-value.js +18 -1
  100. package/dist/src/rules/html-boolean-attributes-no-value.js.map +1 -1
  101. package/dist/src/rules/html-head-only-elements.js +51 -0
  102. package/dist/src/rules/html-head-only-elements.js.map +1 -0
  103. package/dist/src/rules/html-iframe-has-title.js +8 -2
  104. package/dist/src/rules/html-iframe-has-title.js.map +1 -1
  105. package/dist/src/rules/html-img-require-alt.js +7 -1
  106. package/dist/src/rules/html-img-require-alt.js.map +1 -1
  107. package/dist/src/rules/html-input-require-autocomplete.js +70 -0
  108. package/dist/src/rules/html-input-require-autocomplete.js.map +1 -0
  109. package/dist/src/rules/html-navigation-has-label.js +7 -1
  110. package/dist/src/rules/html-navigation-has-label.js.map +1 -1
  111. package/dist/src/rules/html-no-aria-hidden-on-focusable.js +7 -1
  112. package/dist/src/rules/html-no-aria-hidden-on-focusable.js.map +1 -1
  113. package/dist/src/rules/html-no-block-inside-inline.js +7 -1
  114. package/dist/src/rules/html-no-block-inside-inline.js.map +1 -1
  115. package/dist/src/rules/html-no-duplicate-attributes.js +7 -1
  116. package/dist/src/rules/html-no-duplicate-attributes.js.map +1 -1
  117. package/dist/src/rules/html-no-duplicate-ids.js +9 -3
  118. package/dist/src/rules/html-no-duplicate-ids.js.map +1 -1
  119. package/dist/src/rules/html-no-duplicate-meta-names.js +136 -0
  120. package/dist/src/rules/html-no-duplicate-meta-names.js.map +1 -0
  121. package/dist/src/rules/html-no-empty-attributes.js +45 -7
  122. package/dist/src/rules/html-no-empty-attributes.js.map +1 -1
  123. package/dist/src/rules/html-no-empty-headings.js +7 -6
  124. package/dist/src/rules/html-no-empty-headings.js.map +1 -1
  125. package/dist/src/rules/html-no-nested-links.js +7 -1
  126. package/dist/src/rules/html-no-nested-links.js.map +1 -1
  127. package/dist/src/rules/html-no-positive-tab-index.js +7 -1
  128. package/dist/src/rules/html-no-positive-tab-index.js.map +1 -1
  129. package/dist/src/rules/html-no-self-closing.js +48 -3
  130. package/dist/src/rules/html-no-self-closing.js.map +1 -1
  131. package/dist/src/rules/html-no-space-in-tag.js +173 -0
  132. package/dist/src/rules/html-no-space-in-tag.js.map +1 -0
  133. package/dist/src/rules/html-no-title-attribute.js +7 -1
  134. package/dist/src/rules/html-no-title-attribute.js.map +1 -1
  135. package/dist/src/rules/html-no-underscores-in-attribute-names.js +7 -1
  136. package/dist/src/rules/html-no-underscores-in-attribute-names.js.map +1 -1
  137. package/dist/src/rules/html-tag-name-lowercase.js +23 -5
  138. package/dist/src/rules/html-tag-name-lowercase.js.map +1 -1
  139. package/dist/src/rules/index.js +20 -2
  140. package/dist/src/rules/index.js.map +1 -1
  141. package/dist/src/rules/parser-no-errors.js +6 -0
  142. package/dist/src/rules/parser-no-errors.js.map +1 -1
  143. package/dist/src/rules/rule-utils.js +211 -31
  144. package/dist/src/rules/rule-utils.js.map +1 -1
  145. package/dist/src/rules/svg-tag-name-capitalization.js +22 -2
  146. package/dist/src/rules/svg-tag-name-capitalization.js.map +1 -1
  147. package/dist/src/{default-rules.js → rules.js} +46 -14
  148. package/dist/src/rules.js.map +1 -0
  149. package/dist/src/types.js +34 -1
  150. package/dist/src/types.js.map +1 -1
  151. package/dist/tsconfig.tsbuildinfo +1 -1
  152. package/dist/types/cli/argument-parser.d.ts +8 -2
  153. package/dist/types/cli/file-processor.d.ts +15 -0
  154. package/dist/types/cli/formatters/json-formatter.d.ts +6 -0
  155. package/dist/types/cli/formatters/simple-formatter.d.ts +1 -0
  156. package/dist/types/cli/summary-reporter.d.ts +6 -0
  157. package/dist/types/cli.d.ts +9 -4
  158. package/dist/types/custom-rule-loader.d.ts +62 -0
  159. package/dist/types/herb-disable-comment-utils.d.ts +69 -0
  160. package/dist/types/index.d.ts +1 -0
  161. package/dist/types/linter.d.ts +99 -3
  162. package/dist/types/loader.d.ts +20 -0
  163. package/dist/types/rules/erb-comment-syntax.d.ts +14 -0
  164. package/dist/types/rules/erb-no-case-node-children.d.ts +8 -0
  165. package/dist/types/rules/erb-no-empty-tags.d.ts +3 -2
  166. package/dist/types/rules/erb-no-extra-newline.d.ts +14 -0
  167. package/dist/types/rules/erb-no-extra-whitespace-inside-tags.d.ts +18 -0
  168. package/dist/types/rules/erb-no-output-control-flow.d.ts +3 -2
  169. package/dist/types/rules/erb-no-silent-tag-in-attribute-name.d.ts +3 -2
  170. package/dist/types/rules/erb-prefer-image-tag-helper.d.ts +3 -2
  171. package/dist/types/rules/erb-require-trailing-newline.d.ts +9 -0
  172. package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +16 -5
  173. package/dist/types/rules/erb-right-trim.d.ts +14 -0
  174. package/dist/types/rules/herb-disable-comment-base.d.ts +37 -0
  175. package/dist/types/rules/herb-disable-comment-malformed.d.ts +8 -0
  176. package/dist/types/rules/herb-disable-comment-missing-rules.d.ts +8 -0
  177. package/dist/types/rules/herb-disable-comment-no-duplicate-rules.d.ts +8 -0
  178. package/dist/types/rules/herb-disable-comment-no-redundant-all.d.ts +8 -0
  179. package/dist/types/rules/herb-disable-comment-unnecessary.d.ts +8 -0
  180. package/dist/types/rules/herb-disable-comment-valid-rule-name.d.ts +8 -0
  181. package/dist/types/rules/html-anchor-require-href.d.ts +3 -2
  182. package/dist/types/rules/html-aria-attribute-must-be-valid.d.ts +3 -2
  183. package/dist/types/rules/html-aria-label-is-well-formatted.d.ts +3 -2
  184. package/dist/types/rules/html-aria-level-must-be-valid.d.ts +3 -2
  185. package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +3 -2
  186. package/dist/types/rules/html-aria-role-must-be-valid.d.ts +3 -2
  187. package/dist/types/rules/html-attribute-double-quotes.d.ts +13 -5
  188. package/dist/types/rules/html-attribute-equals-spacing.d.ts +12 -5
  189. package/dist/types/rules/html-attribute-values-require-quotes.d.ts +13 -5
  190. package/dist/types/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +3 -2
  191. package/dist/types/rules/html-body-only-elements.d.ts +9 -0
  192. package/dist/types/rules/html-boolean-attributes-no-value.d.ts +12 -5
  193. package/dist/types/rules/html-head-only-elements.d.ts +9 -0
  194. package/dist/types/rules/html-iframe-has-title.d.ts +3 -2
  195. package/dist/types/rules/html-img-require-alt.d.ts +3 -2
  196. package/dist/types/rules/html-input-require-autocomplete.d.ts +8 -0
  197. package/dist/types/rules/html-navigation-has-label.d.ts +3 -2
  198. package/dist/types/rules/html-no-aria-hidden-on-focusable.d.ts +3 -2
  199. package/dist/types/rules/html-no-block-inside-inline.d.ts +3 -2
  200. package/dist/types/rules/html-no-duplicate-attributes.d.ts +3 -2
  201. package/dist/types/rules/html-no-duplicate-ids.d.ts +3 -2
  202. package/dist/types/rules/html-no-duplicate-meta-names.d.ts +9 -0
  203. package/dist/types/rules/html-no-empty-attributes.d.ts +3 -2
  204. package/dist/types/rules/html-no-empty-headings.d.ts +3 -2
  205. package/dist/types/rules/html-no-nested-links.d.ts +3 -2
  206. package/dist/types/rules/html-no-positive-tab-index.d.ts +3 -2
  207. package/dist/types/rules/html-no-self-closing.d.ts +14 -5
  208. package/dist/types/rules/html-no-space-in-tag.d.ts +16 -0
  209. package/dist/types/rules/html-no-title-attribute.d.ts +3 -2
  210. package/dist/types/rules/html-no-underscores-in-attribute-names.d.ts +3 -2
  211. package/dist/types/rules/html-tag-name-lowercase.d.ts +16 -6
  212. package/dist/types/rules/index.d.ts +20 -2
  213. package/dist/types/rules/parser-no-errors.d.ts +2 -1
  214. package/dist/types/rules/rule-utils.d.ts +72 -25
  215. package/dist/types/rules/svg-tag-name-capitalization.d.ts +13 -4
  216. package/dist/types/rules.d.ts +2 -0
  217. package/dist/types/src/cli/argument-parser.d.ts +8 -2
  218. package/dist/types/src/cli/file-processor.d.ts +15 -0
  219. package/dist/types/src/cli/formatters/json-formatter.d.ts +6 -0
  220. package/dist/types/src/cli/formatters/simple-formatter.d.ts +1 -0
  221. package/dist/types/src/cli/summary-reporter.d.ts +6 -0
  222. package/dist/types/src/cli.d.ts +9 -4
  223. package/dist/types/src/custom-rule-loader.d.ts +62 -0
  224. package/dist/types/src/herb-disable-comment-utils.d.ts +69 -0
  225. package/dist/types/src/index.d.ts +1 -0
  226. package/dist/types/src/linter.d.ts +99 -3
  227. package/dist/types/src/loader.d.ts +20 -0
  228. package/dist/types/src/rules/erb-comment-syntax.d.ts +14 -0
  229. package/dist/types/src/rules/erb-no-case-node-children.d.ts +8 -0
  230. package/dist/types/src/rules/erb-no-empty-tags.d.ts +3 -2
  231. package/dist/types/src/rules/erb-no-extra-newline.d.ts +14 -0
  232. package/dist/types/src/rules/erb-no-extra-whitespace-inside-tags.d.ts +18 -0
  233. package/dist/types/src/rules/erb-no-output-control-flow.d.ts +3 -2
  234. package/dist/types/src/rules/erb-no-silent-tag-in-attribute-name.d.ts +3 -2
  235. package/dist/types/src/rules/erb-prefer-image-tag-helper.d.ts +3 -2
  236. package/dist/types/src/rules/erb-require-trailing-newline.d.ts +9 -0
  237. package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +16 -5
  238. package/dist/types/src/rules/erb-right-trim.d.ts +14 -0
  239. package/dist/types/src/rules/herb-disable-comment-base.d.ts +37 -0
  240. package/dist/types/src/rules/herb-disable-comment-malformed.d.ts +8 -0
  241. package/dist/types/src/rules/herb-disable-comment-missing-rules.d.ts +8 -0
  242. package/dist/types/src/rules/herb-disable-comment-no-duplicate-rules.d.ts +8 -0
  243. package/dist/types/src/rules/herb-disable-comment-no-redundant-all.d.ts +8 -0
  244. package/dist/types/src/rules/herb-disable-comment-unnecessary.d.ts +8 -0
  245. package/dist/types/src/rules/herb-disable-comment-valid-rule-name.d.ts +8 -0
  246. package/dist/types/src/rules/html-anchor-require-href.d.ts +3 -2
  247. package/dist/types/src/rules/html-aria-attribute-must-be-valid.d.ts +3 -2
  248. package/dist/types/src/rules/html-aria-label-is-well-formatted.d.ts +3 -2
  249. package/dist/types/src/rules/html-aria-level-must-be-valid.d.ts +3 -2
  250. package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +3 -2
  251. package/dist/types/src/rules/html-aria-role-must-be-valid.d.ts +3 -2
  252. package/dist/types/src/rules/html-attribute-double-quotes.d.ts +13 -5
  253. package/dist/types/src/rules/html-attribute-equals-spacing.d.ts +12 -5
  254. package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +13 -5
  255. package/dist/types/src/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +3 -2
  256. package/dist/types/src/rules/html-body-only-elements.d.ts +9 -0
  257. package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +12 -5
  258. package/dist/types/src/rules/html-head-only-elements.d.ts +9 -0
  259. package/dist/types/src/rules/html-iframe-has-title.d.ts +3 -2
  260. package/dist/types/src/rules/html-img-require-alt.d.ts +3 -2
  261. package/dist/types/src/rules/html-input-require-autocomplete.d.ts +8 -0
  262. package/dist/types/src/rules/html-navigation-has-label.d.ts +3 -2
  263. package/dist/types/src/rules/html-no-aria-hidden-on-focusable.d.ts +3 -2
  264. package/dist/types/src/rules/html-no-block-inside-inline.d.ts +3 -2
  265. package/dist/types/src/rules/html-no-duplicate-attributes.d.ts +3 -2
  266. package/dist/types/src/rules/html-no-duplicate-ids.d.ts +3 -2
  267. package/dist/types/src/rules/html-no-duplicate-meta-names.d.ts +9 -0
  268. package/dist/types/src/rules/html-no-empty-attributes.d.ts +3 -2
  269. package/dist/types/src/rules/html-no-empty-headings.d.ts +3 -2
  270. package/dist/types/src/rules/html-no-nested-links.d.ts +3 -2
  271. package/dist/types/src/rules/html-no-positive-tab-index.d.ts +3 -2
  272. package/dist/types/src/rules/html-no-self-closing.d.ts +14 -5
  273. package/dist/types/src/rules/html-no-space-in-tag.d.ts +16 -0
  274. package/dist/types/src/rules/html-no-title-attribute.d.ts +3 -2
  275. package/dist/types/src/rules/html-no-underscores-in-attribute-names.d.ts +3 -2
  276. package/dist/types/src/rules/html-tag-name-lowercase.d.ts +16 -6
  277. package/dist/types/src/rules/index.d.ts +20 -2
  278. package/dist/types/src/rules/parser-no-errors.d.ts +2 -1
  279. package/dist/types/src/rules/rule-utils.d.ts +72 -25
  280. package/dist/types/src/rules/svg-tag-name-capitalization.d.ts +13 -4
  281. package/dist/types/src/rules.d.ts +2 -0
  282. package/dist/types/src/types.d.ts +102 -11
  283. package/dist/types/types.d.ts +102 -11
  284. package/docs/rules/README.md +19 -3
  285. package/docs/rules/erb-comment-syntax.md +44 -0
  286. package/docs/rules/erb-no-case-node-children.md +50 -0
  287. package/docs/rules/erb-no-extra-newline.md +74 -0
  288. package/docs/rules/erb-no-extra-whitespace-inside-tags.md +39 -0
  289. package/docs/rules/{erb-requires-trailing-newline.md → erb-require-trailing-newline.md} +1 -1
  290. package/docs/rules/erb-right-trim.md +52 -0
  291. package/docs/rules/herb-disable-comment-malformed.md +45 -0
  292. package/docs/rules/herb-disable-comment-missing-rules.md +60 -0
  293. package/docs/rules/herb-disable-comment-no-duplicate-rules.md +49 -0
  294. package/docs/rules/herb-disable-comment-no-redundant-all.md +53 -0
  295. package/docs/rules/herb-disable-comment-unnecessary.md +44 -0
  296. package/docs/rules/herb-disable-comment-valid-rule-name.md +41 -0
  297. package/docs/rules/html-aria-attribute-must-be-valid.md +2 -5
  298. package/docs/rules/html-aria-label-is-well-formatted.md +1 -1
  299. package/docs/rules/html-attribute-double-quotes.md +2 -2
  300. package/docs/rules/html-attribute-equals-spacing.md +2 -2
  301. package/docs/rules/html-attribute-values-require-quotes.md +3 -3
  302. package/docs/rules/html-avoid-both-disabled-and-aria-disabled.md +2 -2
  303. package/docs/rules/html-body-only-elements.md +99 -0
  304. package/docs/rules/html-boolean-attributes-no-value.md +2 -2
  305. package/docs/rules/html-head-only-elements.md +81 -0
  306. package/docs/rules/html-input-require-autocomplete.md +64 -0
  307. package/docs/rules/html-no-aria-hidden-on-focusable.md +2 -2
  308. package/docs/rules/html-no-duplicate-attributes.md +2 -2
  309. package/docs/rules/html-no-duplicate-meta-names.md +64 -0
  310. package/docs/rules/html-no-empty-attributes.md +3 -3
  311. package/docs/rules/html-no-empty-headings.md +4 -26
  312. package/docs/rules/html-no-positive-tab-index.md +1 -2
  313. package/docs/rules/html-no-self-closing.md +17 -2
  314. package/docs/rules/html-no-space-in-tag.md +66 -0
  315. package/docs/rules/html-no-title-attribute.md +2 -2
  316. package/docs/rules/html-no-underscores-in-attribute-names.md +2 -2
  317. package/docs/rules/html-tag-name-lowercase.md +2 -2
  318. package/package.json +13 -5
  319. package/src/cli/argument-parser.ts +50 -39
  320. package/src/cli/file-processor.ts +159 -28
  321. package/src/cli/formatters/detailed-formatter.ts +21 -3
  322. package/src/cli/formatters/github-actions-formatter.ts +17 -1
  323. package/src/cli/formatters/json-formatter.ts +9 -0
  324. package/src/cli/formatters/simple-formatter.ts +24 -8
  325. package/src/cli/output-manager.ts +23 -3
  326. package/src/cli/summary-reporter.ts +40 -3
  327. package/src/cli.ts +137 -52
  328. package/src/custom-rule-loader.ts +189 -0
  329. package/src/herb-disable-comment-utils.ts +175 -0
  330. package/src/index.ts +2 -0
  331. package/src/linter.ts +501 -36
  332. package/src/loader.ts +30 -0
  333. package/src/rules/erb-comment-syntax.ts +73 -0
  334. package/src/rules/erb-no-case-node-children.ts +68 -0
  335. package/src/rules/erb-no-empty-tags.ts +9 -3
  336. package/src/rules/erb-no-extra-newline.ts +91 -0
  337. package/src/rules/erb-no-extra-whitespace-inside-tags.ts +147 -0
  338. package/src/rules/erb-no-output-control-flow.ts +9 -3
  339. package/src/rules/erb-no-silent-tag-in-attribute-name.ts +9 -3
  340. package/src/rules/erb-prefer-image-tag-helper.ts +9 -3
  341. package/src/rules/erb-require-trailing-newline.ts +47 -0
  342. package/src/rules/erb-require-whitespace-inside-tags.ts +96 -26
  343. package/src/rules/erb-right-trim.ts +67 -0
  344. package/src/rules/herb-disable-comment-base.ts +76 -0
  345. package/src/rules/herb-disable-comment-malformed.ts +66 -0
  346. package/src/rules/herb-disable-comment-missing-rules.ts +41 -0
  347. package/src/rules/herb-disable-comment-no-duplicate-rules.ts +46 -0
  348. package/src/rules/herb-disable-comment-no-redundant-all.ts +40 -0
  349. package/src/rules/herb-disable-comment-unnecessary.ts +103 -0
  350. package/src/rules/herb-disable-comment-valid-rule-name.ts +62 -0
  351. package/src/rules/html-anchor-require-href.ts +9 -3
  352. package/src/rules/html-aria-attribute-must-be-valid.ts +9 -3
  353. package/src/rules/html-aria-label-is-well-formatted.ts +9 -5
  354. package/src/rules/html-aria-level-must-be-valid.ts +9 -2
  355. package/src/rules/html-aria-role-heading-requires-level.ts +9 -3
  356. package/src/rules/html-aria-role-must-be-valid.ts +9 -3
  357. package/src/rules/html-attribute-double-quotes.ts +42 -8
  358. package/src/rules/html-attribute-equals-spacing.ts +31 -7
  359. package/src/rules/html-attribute-values-require-quotes.ts +56 -10
  360. package/src/rules/html-avoid-both-disabled-and-aria-disabled.ts +9 -3
  361. package/src/rules/html-body-only-elements.ts +60 -0
  362. package/src/rules/html-boolean-attributes-no-value.ts +31 -6
  363. package/src/rules/html-head-only-elements.ts +65 -0
  364. package/src/rules/html-iframe-has-title.ts +9 -4
  365. package/src/rules/html-img-require-alt.ts +10 -4
  366. package/src/rules/html-input-require-autocomplete.ts +85 -0
  367. package/src/rules/html-navigation-has-label.ts +9 -3
  368. package/src/rules/html-no-aria-hidden-on-focusable.ts +9 -3
  369. package/src/rules/html-no-block-inside-inline.ts +9 -3
  370. package/src/rules/html-no-duplicate-attributes.ts +9 -3
  371. package/src/rules/html-no-duplicate-ids.ts +11 -7
  372. package/src/rules/html-no-duplicate-meta-names.ts +188 -0
  373. package/src/rules/html-no-empty-attributes.ts +58 -10
  374. package/src/rules/html-no-empty-headings.ts +10 -8
  375. package/src/rules/html-no-nested-links.ts +10 -4
  376. package/src/rules/html-no-positive-tab-index.ts +9 -3
  377. package/src/rules/html-no-self-closing.ts +69 -9
  378. package/src/rules/html-no-space-in-tag.ts +221 -0
  379. package/src/rules/html-no-title-attribute.ts +9 -3
  380. package/src/rules/html-no-underscores-in-attribute-names.ts +12 -4
  381. package/src/rules/html-tag-name-lowercase.ts +41 -10
  382. package/src/rules/index.ts +24 -2
  383. package/src/rules/parser-no-errors.ts +8 -1
  384. package/src/rules/rule-utils.ts +250 -44
  385. package/src/rules/svg-tag-name-capitalization.ts +39 -6
  386. package/src/{default-rules.ts → rules.ts} +53 -13
  387. package/src/types.ts +133 -15
  388. package/dist/src/default-rules.js.map +0 -1
  389. package/dist/src/rules/erb-requires-trailing-newline.js +0 -22
  390. package/dist/src/rules/erb-requires-trailing-newline.js.map +0 -1
  391. package/dist/types/default-rules.d.ts +0 -2
  392. package/dist/types/rules/erb-requires-trailing-newline.d.ts +0 -6
  393. package/dist/types/src/default-rules.d.ts +0 -2
  394. package/dist/types/src/rules/erb-requires-trailing-newline.d.ts +0 -6
  395. package/src/rules/erb-requires-trailing-newline.ts +0 -29
@@ -1,7 +1,7 @@
1
1
  import { Visitor, Location } from "@herb-tools/core";
2
- import type { HTMLAttributeNode, HTMLAttributeValueNode, HTMLOpenTagNode, LexResult, Token, Node } from "@herb-tools/core";
2
+ import type { HTMLAttributeNode, HTMLAttributeValueNode, HTMLElementNode, HTMLOpenTagNode, LexResult, Token, Node } from "@herb-tools/core";
3
3
  import type * as Nodes from "@herb-tools/core";
4
- import type { LintOffense, LintSeverity, LintContext } from "../types.js";
4
+ import type { UnboundLintOffense, LintContext, BaseAutofixContext } from "../types.js";
5
5
  export declare enum ControlFlowType {
6
6
  CONDITIONAL = 0,
7
7
  LOOP = 1
@@ -9,35 +9,37 @@ export declare enum ControlFlowType {
9
9
  /**
10
10
  * Base visitor class that provides common functionality for rule visitors
11
11
  */
12
- export declare abstract class BaseRuleVisitor extends Visitor {
13
- readonly offenses: LintOffense[];
12
+ export declare abstract class BaseRuleVisitor<TAutofixContext extends BaseAutofixContext = BaseAutofixContext> extends Visitor {
13
+ readonly offenses: UnboundLintOffense<TAutofixContext>[];
14
14
  protected ruleName: string;
15
15
  protected context: LintContext;
16
16
  constructor(ruleName: string, context?: Partial<LintContext>);
17
17
  /**
18
- * Helper method to create a lint offense
18
+ * Helper method to create an unbound lint offense (without severity).
19
+ * The Linter will bind severity based on the rule's config.
19
20
  */
20
- protected createOffense(message: string, location: Location, severity?: LintSeverity): LintOffense;
21
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext): UnboundLintOffense<TAutofixContext>;
21
22
  /**
22
23
  * Helper method to add an offense to the offenses array
23
24
  */
24
- protected addOffense(message: string, location: Location, severity?: LintSeverity): void;
25
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext): void;
25
26
  }
26
27
  /**
27
28
  * Mixin that adds control flow tracking capabilities to rule visitors
28
29
  * This allows rules to track state across different control flow structures
29
30
  * like if/else branches, loops, etc.
30
31
  *
32
+ * @template TAutofixContext - Type for autofix context (node + custom data)
31
33
  * @template TControlFlowState - Type for state passed between onEnterControlFlow and onExitControlFlow
32
34
  * @template TBranchState - Type for state passed between onEnterBranch and onExitBranch
33
35
  */
34
- export declare abstract class ControlFlowTrackingVisitor<TControlFlowState = any, TBranchState = any> extends BaseRuleVisitor {
36
+ export declare abstract class ControlFlowTrackingVisitor<TAutofixContext extends BaseAutofixContext = BaseAutofixContext, TControlFlowState = any, TBranchState = any> extends BaseRuleVisitor<TAutofixContext> {
35
37
  protected isInControlFlow: boolean;
36
38
  protected currentControlFlowType: ControlFlowType | null;
37
39
  /**
38
40
  * Handle visiting a control flow node with proper scope management
39
41
  */
40
- protected handleControlFlowNode(node: Node, controlFlowType: ControlFlowType, visitChildren: () => void): void;
42
+ protected handleControlFlowNode(_node: Node, controlFlowType: ControlFlowType, visitChildren: () => void): void;
41
43
  /**
42
44
  * Handle visiting a branch node (like else, when) with proper scope management
43
45
  */
@@ -64,7 +66,7 @@ export declare function getAttributes(node: HTMLOpenTagNode): HTMLAttributeNode[
64
66
  /**
65
67
  * Gets the tag name from an HTML tag node (lowercased)
66
68
  */
67
- export declare function getTagName(node: HTMLOpenTagNode): string | null;
69
+ export declare function getTagName(node: HTMLElementNode | HTMLOpenTagNode | null | undefined): string | null;
68
70
  /**
69
71
  * Gets the attribute name from an HTMLAttributeNode (lowercased)
70
72
  * Returns null if the attribute name contains dynamic content (ERB)
@@ -210,7 +212,7 @@ export declare function isBooleanAttribute(attributeName: string): boolean;
210
212
  * - checkDynamicAttributeStaticValue() - name="data-<%= key %>" value="foo"
211
213
  * - checkDynamicAttributeDynamicValue() - name="data-<%= key %>" value="<%= value %>"
212
214
  */
213
- export declare abstract class AttributeVisitorMixin extends BaseRuleVisitor {
215
+ export declare abstract class AttributeVisitorMixin<TAutofixContext extends BaseAutofixContext = BaseAutofixContext> extends BaseRuleVisitor<TAutofixContext> {
214
216
  constructor(ruleName: string, context?: Partial<LintContext>);
215
217
  visitHTMLOpenTagNode(node: HTMLOpenTagNode): void;
216
218
  private checkAttributesOnNode;
@@ -242,19 +244,20 @@ export declare function forEachAttribute(node: HTMLOpenTagNode, callback: (attri
242
244
  /**
243
245
  * Base lexer visitor class that provides common functionality for lexer-based rule visitors
244
246
  */
245
- export declare abstract class BaseLexerRuleVisitor {
246
- readonly offenses: LintOffense[];
247
+ export declare abstract class BaseLexerRuleVisitor<TAutofixContext extends BaseAutofixContext = BaseAutofixContext> {
248
+ readonly offenses: UnboundLintOffense<TAutofixContext>[];
247
249
  protected ruleName: string;
248
250
  protected context: LintContext;
249
251
  constructor(ruleName: string, context?: Partial<LintContext>);
250
252
  /**
251
- * Helper method to create a lint offense for lexer rules
253
+ * Helper method to create an unbound lint offense (without severity).
254
+ * The Linter will bind severity based on the rule's config.
252
255
  */
253
- protected createOffense(message: string, location: Location, severity?: LintSeverity): LintOffense;
256
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext): UnboundLintOffense<TAutofixContext>;
254
257
  /**
255
258
  * Helper method to add an offense to the offenses array
256
259
  */
257
- protected addOffense(message: string, location: Location, severity?: LintSeverity): void;
260
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext): void;
258
261
  /**
259
262
  * Main entry point for lexer rule visitors
260
263
  * @param lexResult - The lexer result containing tokens and source
@@ -274,19 +277,20 @@ export declare abstract class BaseLexerRuleVisitor {
274
277
  /**
275
278
  * Base source visitor class that provides common functionality for source-based rule visitors
276
279
  */
277
- export declare abstract class BaseSourceRuleVisitor {
278
- readonly offenses: LintOffense[];
280
+ export declare abstract class BaseSourceRuleVisitor<TAutofixContext extends BaseAutofixContext = BaseAutofixContext> {
281
+ readonly offenses: UnboundLintOffense<TAutofixContext>[];
279
282
  protected ruleName: string;
280
283
  protected context: LintContext;
281
284
  constructor(ruleName: string, context?: Partial<LintContext>);
282
285
  /**
283
- * Helper method to create a lint offense for source rules
286
+ * Helper method to create an unbound lint offense (without severity).
287
+ * The Linter will bind severity based on the rule's config.
284
288
  */
285
- protected createOffense(message: string, location: Location, severity?: LintSeverity): LintOffense;
289
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext): UnboundLintOffense<TAutofixContext>;
286
290
  /**
287
291
  * Helper method to add an offense to the offenses array
288
292
  */
289
- protected addOffense(message: string, location: Location, severity?: LintSeverity): void;
293
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext): void;
290
294
  /**
291
295
  * Main entry point for source rule visitors
292
296
  * @param source - The raw source code
@@ -297,8 +301,51 @@ export declare abstract class BaseSourceRuleVisitor {
297
301
  * Override this method to implement source-level checks
298
302
  */
299
303
  protected abstract visitSource(source: string): void;
300
- /**
301
- * Helper method to create a location for a specific position in the source
302
- */
303
- protected createLocationAt(source: string, position: number): Location;
304
304
  }
305
+ /**
306
+ * Autofix utilities for applying string replacements
307
+ */
308
+ /**
309
+ * Checks if two locations are equal
310
+ * @param a - First location
311
+ * @param b - Second location
312
+ * @returns true if locations are equal
313
+ */
314
+ export declare function locationsEqual(a: Location, b: Location): boolean;
315
+ /**
316
+ * Finds a node in the AST that has a specific location
317
+ * Uses direct recursive traversal for reliability
318
+ * @param root - The root node to search from
319
+ * @param location - The location to match
320
+ * @param predicate - Optional predicate function to filter nodes (e.g., isERBNode)
321
+ * @returns The matching node or null if not found
322
+ */
323
+ export declare function findNodeByLocation(root: Node, location: Location, predicate?: (node: Node) => boolean): any;
324
+ /**
325
+ * AST Navigation Utilities
326
+ * These utilities help navigate the AST tree for complex autofix operations
327
+ */
328
+ /**
329
+ * Finds the parent node of a given child node in the AST
330
+ * @param root - The root node to search from (typically the document node)
331
+ * @param target - The child node to find the parent of
332
+ * @returns The parent node, or null if not found
333
+ *
334
+ * @example
335
+ * const parent = findParent(result.value, offense.autofixContext.node)
336
+ * if (parent?.type === "AST_HTML_ELEMENT_NODE") {
337
+ * // Modify parent...
338
+ * }
339
+ */
340
+ export declare function findParent(root: Node, target: Node): Node | null;
341
+ export declare const DOCUMENT_ONLY_TAG_NAMES: Set<string>;
342
+ export declare const HTML_ONLY_TAG_NAMES: Set<string>;
343
+ export declare const HEAD_ONLY_TAG_NAMES: Set<string>;
344
+ export declare const HEAD_AND_BODY_TAG_NAMES: Set<string>;
345
+ export declare function isDocumentOnlyTag(tagName: string): boolean;
346
+ export declare function isHtmlOnlyTag(tagName: string): boolean;
347
+ export declare function isHeadOnlyTag(tagName: string): boolean;
348
+ export declare function isHeadAndBodyTag(tagName: string): boolean;
349
+ export declare function isBodyOnlyTag(tagName: string): boolean;
350
+ export declare function isBodyTag(tagName: string): boolean;
351
+ export declare function isHeadTag(tagName: string): boolean;
@@ -1,7 +1,16 @@
1
1
  import { ParserRule } from "../types.js";
2
- import type { LintOffense, LintContext } from "../types.js";
3
- import type { ParseResult } from "@herb-tools/core";
4
- export declare class SVGTagNameCapitalizationRule extends ParserRule {
2
+ import type { UnboundLintOffense, LintOffense, LintContext, BaseAutofixContext, Mutable, FullRuleConfig } from "../types.js";
3
+ import type { HTMLOpenTagNode, HTMLCloseTagNode, ParseResult } from "@herb-tools/core";
4
+ interface SVGTagNameCapitalizationAutofixContext extends BaseAutofixContext {
5
+ node: Mutable<HTMLOpenTagNode | HTMLCloseTagNode>;
6
+ currentTagName: string;
7
+ correctCamelCase: string;
8
+ }
9
+ export declare class SVGTagNameCapitalizationRule extends ParserRule<SVGTagNameCapitalizationAutofixContext> {
10
+ static autocorrectable: boolean;
5
11
  name: string;
6
- check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
12
+ get defaultConfig(): FullRuleConfig;
13
+ check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<SVGTagNameCapitalizationAutofixContext>[];
14
+ autofix(offense: LintOffense<SVGTagNameCapitalizationAutofixContext>, result: ParseResult, _context?: Partial<LintContext>): ParseResult | null;
7
15
  }
16
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { RuleClass } from "./types.js";
2
+ export declare const rules: RuleClass[];
@@ -1,16 +1,22 @@
1
1
  import type { ThemeInput } from "@herb-tools/highlighter";
2
2
  export type FormatOption = "simple" | "detailed" | "json";
3
3
  export interface ParsedArguments {
4
- pattern: string;
4
+ patterns: string[];
5
+ configFile?: string;
5
6
  formatOption: FormatOption;
6
7
  showTiming: boolean;
7
8
  theme: ThemeInput;
8
9
  wrapLines: boolean;
9
10
  truncateLines: boolean;
10
11
  useGitHubActions: boolean;
12
+ fix: boolean;
13
+ ignoreDisableComments: boolean;
14
+ force: boolean;
15
+ init: boolean;
16
+ loadCustomRules: boolean;
11
17
  }
12
18
  export declare class ArgumentParser {
13
19
  private readonly usage;
14
20
  parse(argv: string[]): ParsedArguments;
15
- private getFilePattern;
21
+ private getFilePatterns;
16
22
  }
@@ -1,18 +1,31 @@
1
+ import { Config } from "@herb-tools/config";
1
2
  import type { Diagnostic } from "@herb-tools/core";
2
3
  import type { FormatOption } from "./argument-parser.js";
4
+ import type { HerbConfigOptions } from "@herb-tools/config";
3
5
  export interface ProcessedFile {
4
6
  filename: string;
5
7
  offense: Diagnostic;
6
8
  content: string;
9
+ autocorrectable?: boolean;
7
10
  }
8
11
  export interface ProcessingContext {
9
12
  projectPath?: string;
10
13
  pattern?: string;
14
+ fix?: boolean;
15
+ ignoreDisableComments?: boolean;
16
+ linterConfig?: HerbConfigOptions['linter'];
17
+ config?: Config;
18
+ loadCustomRules?: boolean;
11
19
  }
12
20
  export interface ProcessingResult {
13
21
  totalErrors: number;
14
22
  totalWarnings: number;
23
+ totalInfo: number;
24
+ totalHints: number;
25
+ totalIgnored: number;
26
+ totalWouldBeIgnored?: number;
15
27
  filesWithOffenses: number;
28
+ filesFixed: number;
16
29
  ruleCount: number;
17
30
  allOffenses: ProcessedFile[];
18
31
  ruleOffenses: Map<string, {
@@ -23,5 +36,7 @@ export interface ProcessingResult {
23
36
  }
24
37
  export declare class FileProcessor {
25
38
  private linter;
39
+ private customRulesLoaded;
40
+ private isRuleAutocorrectable;
26
41
  processFiles(files: string[], formatOption?: FormatOption, context?: ProcessingContext): Promise<ProcessingResult>;
27
42
  }
@@ -9,6 +9,9 @@ interface JSONSummary {
9
9
  filesWithOffenses: number;
10
10
  totalErrors: number;
11
11
  totalWarnings: number;
12
+ totalInfo: number;
13
+ totalHints: number;
14
+ totalIgnored: number;
12
15
  totalOffenses: number;
13
16
  ruleCount: number;
14
17
  }
@@ -28,6 +31,9 @@ interface JSONFormatOptions {
28
31
  files: string[];
29
32
  totalErrors: number;
30
33
  totalWarnings: number;
34
+ totalInfo: number;
35
+ totalHints: number;
36
+ totalIgnored: number;
31
37
  filesWithOffenses: number;
32
38
  ruleCount: number;
33
39
  startTime: number;
@@ -4,4 +4,5 @@ import type { ProcessedFile } from "../file-processor.js";
4
4
  export declare class SimpleFormatter extends BaseFormatter {
5
5
  format(allOffenses: ProcessedFile[]): Promise<void>;
6
6
  formatFile(filename: string, offenses: Diagnostic[]): void;
7
+ formatFileProcessed(filename: string, processedFiles: ProcessedFile[]): void;
7
8
  }
@@ -2,6 +2,10 @@ export interface SummaryData {
2
2
  files: string[];
3
3
  totalErrors: number;
4
4
  totalWarnings: number;
5
+ totalInfo?: number;
6
+ totalHints?: number;
7
+ totalIgnored: number;
8
+ totalWouldBeIgnored?: number;
5
9
  filesWithOffenses: number;
6
10
  ruleCount: number;
7
11
  startTime: number;
@@ -11,6 +15,8 @@ export interface SummaryData {
11
15
  count: number;
12
16
  files: Set<string>;
13
17
  }>;
18
+ autofixableCount: number;
19
+ ignoreDisableComments?: boolean;
14
20
  }
15
21
  export declare class SummaryReporter {
16
22
  private pluralize;
@@ -1,6 +1,8 @@
1
- import { ArgumentParser, type FormatOption } from "./cli/argument-parser.js";
1
+ import { Config } from "@herb-tools/config";
2
+ import { ArgumentParser } from "./cli/argument-parser.js";
2
3
  import { FileProcessor } from "./cli/file-processor.js";
3
4
  import { OutputManager } from "./cli/output-manager.js";
5
+ import type { FormatOption } from "./cli/argument-parser.js";
4
6
  export * from "./cli/index.js";
5
7
  export declare class CLI {
6
8
  protected argumentParser: ArgumentParser;
@@ -8,15 +10,18 @@ export declare class CLI {
8
10
  protected outputManager: OutputManager;
9
11
  protected projectPath: string;
10
12
  getProjectPath(): string;
11
- protected findProjectRoot(startPath: string): string;
12
13
  protected exitWithError(message: string, formatOption: FormatOption, exitCode?: number): void;
13
14
  protected exitWithInfo(message: string, formatOption: FormatOption, exitCode?: number, timingData?: {
14
15
  startTime: number;
15
16
  startDate: Date;
16
17
  showTiming: boolean;
17
18
  }): void;
18
- protected determineProjectPath(pattern: string | undefined): void;
19
- protected adjustPattern(pattern: string | undefined): string;
19
+ protected determineProjectPath(patterns: string[]): void;
20
+ protected adjustPattern(pattern: string | undefined, configGlobPattern: string): string;
21
+ protected resolvePatternToFiles(pattern: string, config: Config, force: boolean): Promise<{
22
+ files: string[];
23
+ explicitFile: string | undefined;
24
+ }>;
20
25
  protected beforeProcess(): Promise<void>;
21
26
  protected afterProcess(_results: any, _outputOptions: any): Promise<void>;
22
27
  run(): Promise<void>;
@@ -0,0 +1,62 @@
1
+ import type { RuleClass } from "./types.js";
2
+ export interface CustomRuleLoaderOptions {
3
+ /**
4
+ * Base directory to search for custom rules
5
+ * Defaults to current working directory
6
+ */
7
+ baseDir?: string;
8
+ /**
9
+ * Glob patterns to search for custom rule files
10
+ * Defaults to looking in common locations
11
+ */
12
+ patterns?: string[];
13
+ /**
14
+ * Whether to suppress errors when loading custom rules
15
+ * Defaults to false
16
+ */
17
+ silent?: boolean;
18
+ }
19
+ /**
20
+ * Loads custom linter rules from the user's project
21
+ */
22
+ export declare class CustomRuleLoader {
23
+ private baseDir;
24
+ private patterns;
25
+ private silent;
26
+ constructor(options?: CustomRuleLoaderOptions);
27
+ /**
28
+ * Discovers custom rule files in the project
29
+ */
30
+ discoverRuleFiles(): Promise<string[]>;
31
+ /**
32
+ * Loads a single rule file
33
+ */
34
+ loadRuleFile(filePath: string): Promise<RuleClass[]>;
35
+ /**
36
+ * Type guard to check if an export is a valid rule class
37
+ */
38
+ private isValidRuleClass;
39
+ /**
40
+ * Loads all custom rules from the project
41
+ */
42
+ loadRules(): Promise<RuleClass[]>;
43
+ /**
44
+ * Loads all custom rules and returns detailed information about each rule
45
+ */
46
+ loadRulesWithInfo(): Promise<{
47
+ rules: RuleClass[];
48
+ ruleInfo: Array<{
49
+ name: string;
50
+ path: string;
51
+ }>;
52
+ duplicateWarnings: string[];
53
+ }>;
54
+ /**
55
+ * Static helper to check if custom rules exist in a project
56
+ */
57
+ static hasCustomRules(baseDir?: string): Promise<boolean>;
58
+ /**
59
+ * Static helper to load custom rules and merge with default rules
60
+ */
61
+ static loadAndMergeRules(defaultRules: RuleClass[], options?: CustomRuleLoaderOptions): Promise<RuleClass[]>;
62
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Utilities for parsing herb:disable comments
3
+ */
4
+ /**
5
+ * Information about a single rule name in a herb:disable comment
6
+ */
7
+ export interface HerbDisableRuleName {
8
+ /** The rule name */
9
+ name: string;
10
+ /** The starting offset of this rule name within the content/line */
11
+ offset: number;
12
+ /** The length of the rule name */
13
+ length: number;
14
+ }
15
+ /**
16
+ * Result of parsing a herb:disable comment
17
+ */
18
+ export interface HerbDisableComment {
19
+ /** The full matched string */
20
+ match: string;
21
+ /** Array of rule names specified in the comment */
22
+ ruleNames: string[];
23
+ /** Array of rule name information with positions */
24
+ ruleNameDetails: HerbDisableRuleName[];
25
+ /** The original rules string (e.g., "rule1, rule2") */
26
+ rulesString: string;
27
+ }
28
+ /**
29
+ * Parse a herb:disable comment from ERB comment content.
30
+ * Use this when you have the content inside <%# ... %> (e.g., from ERBContentNode.content.value)
31
+ *
32
+ * @param content - The content string (without <%# %> delimiters)
33
+ * @returns Parsed comment data or null if not a valid herb:disable comment
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const result = parseHerbDisableContent("herb:disable rule1, rule2")
38
+ * // { match: "herb:disable rule1, rule2", ruleNames: ["rule1", "rule2"], rulesString: "rule1, rule2" }
39
+ * ```
40
+ */
41
+ export declare function parseHerbDisableContent(content: string): HerbDisableComment | null;
42
+ /**
43
+ * Parse a herb:disable comment from a full source line.
44
+ * Use this when you have a complete line that may contain <%# herb:disable ... %>
45
+ *
46
+ * @param line - The source line that may contain a herb:disable comment
47
+ * @returns Parsed comment data or null if not a valid herb:disable comment
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const result = parseHerbDisableLine("<div>test</div> <%# herb:disable rule1, rule2 %>")
52
+ * // { match: "<%# herb:disable rule1, rule2 %>", ruleNames: ["rule1", "rule2"], rulesString: "rule1, rule2" }
53
+ * ```
54
+ */
55
+ export declare function parseHerbDisableLine(line: string): HerbDisableComment | null;
56
+ /**
57
+ * Check if an ERB comment content contains a herb:disable directive.
58
+ *
59
+ * @param content - The content string (without <%# %> delimiters)
60
+ * @returns true if the content contains a herb:disable directive
61
+ */
62
+ export declare function isHerbDisableContent(content: string): boolean;
63
+ /**
64
+ * Check if a source line contains a herb:disable comment.
65
+ *
66
+ * @param line - The source line
67
+ * @returns true if the line contains a herb:disable comment
68
+ */
69
+ export declare function isHerbDisableLine(line: string): boolean;
@@ -1,3 +1,4 @@
1
1
  export * from "./linter.js";
2
2
  export * from "./rules/index.js";
3
3
  export * from "./types.js";
4
+ export { rules } from "./rules.js";
@@ -1,20 +1,89 @@
1
- import type { RuleClass, Rule, LexerRule, SourceRule, LintResult, LintOffense, LintContext } from "./types.js";
1
+ import type { RuleClass, Rule, LexerRule, SourceRule, LintResult, LintOffense, UnboundLintOffense, LintContext, AutofixResult } from "./types.js";
2
2
  import type { HerbBackend } from "@herb-tools/core";
3
+ import type { RuleConfig, Config } from "@herb-tools/config";
4
+ export interface LinterOptions {
5
+ /**
6
+ * Array of rule classes to use. If not provided, uses default rules.
7
+ */
8
+ rules?: RuleClass[];
9
+ /**
10
+ * Whether to load custom rules from the project.
11
+ * Defaults to false for backward compatibility.
12
+ */
13
+ loadCustomRules?: boolean;
14
+ /**
15
+ * Base directory to search for custom rules.
16
+ * Defaults to current working directory.
17
+ */
18
+ customRulesBaseDir?: string;
19
+ /**
20
+ * Custom glob patterns to search for rule files.
21
+ */
22
+ customRulesPatterns?: string[];
23
+ /**
24
+ * Whether to suppress custom rule loading errors.
25
+ * Defaults to false.
26
+ */
27
+ silentCustomRules?: boolean;
28
+ }
3
29
  export declare class Linter {
4
30
  protected rules: RuleClass[];
31
+ protected allAvailableRules: RuleClass[];
5
32
  protected herb: HerbBackend;
6
33
  protected offenses: LintOffense[];
34
+ protected config?: Config;
35
+ /**
36
+ * Creates a new Linter instance with automatic rule filtering based on config.
37
+ *
38
+ * @param herb - The Herb backend instance for parsing and lexing
39
+ * @param config - Optional full Config instance for rule filtering, severity overrides, and path-based filtering
40
+ * @param customRules - Optional array of custom rules to include alongside built-in rules
41
+ * @returns A configured Linter instance
42
+ */
43
+ static from(herb: HerbBackend, config?: Config, customRules?: RuleClass[]): Linter;
7
44
  /**
8
45
  * Creates a new Linter instance.
46
+ *
47
+ * For most use cases, prefer `Linter.from()` which handles config-based filtering.
48
+ * Use this constructor directly when you need explicit control over rules.
49
+ *
9
50
  * @param herb - The Herb backend instance for parsing and lexing
10
51
  * @param rules - Array of rule classes (Parser/AST or Lexer) to use. If not provided, uses default rules.
52
+ * @param config - Optional full Config instance for severity overrides and path-based rule filtering
53
+ * @param allAvailableRules - Optional array of ALL available rules (including disabled) for herb:disable validation
54
+ */
55
+ constructor(herb: HerbBackend, rules?: RuleClass[], config?: Config, allAvailableRules?: RuleClass[]);
56
+ /**
57
+ * Filters rules based on default config and optional user config overrides.
58
+ *
59
+ * Priority:
60
+ * 1. User config override (if rule config exists in userRulesConfig)
61
+ * 2. Default config from rule's defaultConfig getter
62
+ *
63
+ * @param allRules - All available rule classes to filter from
64
+ * @param userRulesConfig - Optional user configuration for rules
65
+ * @returns Filtered array of rule classes that should be enabled
11
66
  */
12
- constructor(herb: HerbBackend, rules?: RuleClass[]);
67
+ static filterRulesByConfig(allRules: RuleClass[], userRulesConfig?: Record<string, RuleConfig>): RuleClass[];
13
68
  /**
14
69
  * Returns the default set of rule classes used by the linter.
15
- * @returns Array of rule classes
70
+ * These are the rules enabled when no custom rules are provided.
71
+ * Filters all available rules to only include those enabled by default.
72
+ * @returns Array of default rule classes
16
73
  */
17
74
  protected getDefaultRules(): RuleClass[];
75
+ /**
76
+ * Returns all available rule classes that can be referenced in herb:disable comments.
77
+ * This includes all rules that exist, regardless of whether they're currently enabled.
78
+ * Includes both built-in rules and any loaded custom rules.
79
+ * @returns Array of all available rule classes
80
+ */
81
+ protected getAvailableRules(): RuleClass[];
82
+ /**
83
+ * Meta-linting rules for herb:disable comments cannot be disabled
84
+ * This ensures that invalid herb:disable comments are always caught
85
+ */
86
+ protected get nonExcludableRules(): string[];
18
87
  getRuleCount(): number;
19
88
  /**
20
89
  * Type guard to check if a rule is a LexerRule
@@ -24,10 +93,37 @@ export declare class Linter {
24
93
  * Type guard to check if a rule is a SourceRule
25
94
  */
26
95
  protected isSourceRule(rule: Rule): rule is SourceRule;
96
+ /**
97
+ * Execute a single rule and return its unbound offenses.
98
+ * Handles rule type checking (Lexer/Parser/Source) and isEnabled checks.
99
+ */
100
+ private executeRule;
101
+ private filterOffenses;
27
102
  /**
28
103
  * Lint source code using Parser/AST, Lexer, and Source rules.
29
104
  * @param source - The source code to lint
30
105
  * @param context - Optional context for linting (e.g., fileName for distinguishing files vs snippets)
31
106
  */
32
107
  lint(source: string, context?: Partial<LintContext>): LintResult;
108
+ /**
109
+ * Bind severity to unbound offenses based on rule's defaultConfig and user config overrides.
110
+ *
111
+ * Priority:
112
+ * 1. User config severity override (if specified in config)
113
+ * 2. Rule's default severity (from defaultConfig.severity)
114
+ *
115
+ * @param unboundOffenses - Array of offenses without severity
116
+ * @param ruleName - Name of the rule that produced the offenses
117
+ * @returns Array of offenses with severity bound
118
+ */
119
+ protected bindSeverity(unboundOffenses: UnboundLintOffense[], ruleName: string): LintOffense[];
120
+ /**
121
+ * Automatically fix offenses in the source code.
122
+ * Uses AST mutation for parser rules and token mutation for lexer rules.
123
+ * @param source - The source code to fix
124
+ * @param context - Optional context for linting (e.g., fileName)
125
+ * @param offensesToFix - Optional array of specific offenses to fix. If not provided, all fixable offenses will be fixed.
126
+ * @returns AutofixResult containing the corrected source and lists of fixed/unfixed offenses
127
+ */
128
+ autofix(source: string, context?: Partial<LintContext>, offensesToFix?: LintOffense[]): AutofixResult;
33
129
  }
@@ -0,0 +1,20 @@
1
+ export * from "./index.js";
2
+ export { CustomRuleLoader } from "./custom-rule-loader.js";
3
+ export type { CustomRuleLoaderOptions } from "./custom-rule-loader.js";
4
+ import type { RuleClass } from "./types.js";
5
+ /**
6
+ * Loads custom rules from the filesystem.
7
+ * Only available in Node.js environments.
8
+ */
9
+ export declare function loadCustomRules(options?: {
10
+ baseDir?: string;
11
+ patterns?: string[];
12
+ silent?: boolean;
13
+ }): Promise<{
14
+ rules: RuleClass[];
15
+ ruleInfo: Array<{
16
+ name: string;
17
+ path: string;
18
+ }>;
19
+ warnings: string[];
20
+ }>;
@@ -0,0 +1,14 @@
1
+ import { ParserRule, BaseAutofixContext, Mutable } from "../types.js";
2
+ import type { UnboundLintOffense, LintOffense, LintContext, FullRuleConfig } from "../types.js";
3
+ import type { ParseResult, ERBContentNode } from "@herb-tools/core";
4
+ interface ERBCommentSyntaxAutofixContext extends BaseAutofixContext {
5
+ node: Mutable<ERBContentNode>;
6
+ }
7
+ export declare class ERBCommentSyntax extends ParserRule<ERBCommentSyntaxAutofixContext> {
8
+ static autocorrectable: boolean;
9
+ name: string;
10
+ get defaultConfig(): FullRuleConfig;
11
+ check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<ERBCommentSyntaxAutofixContext>[];
12
+ autofix(offense: LintOffense<ERBCommentSyntaxAutofixContext>, result: ParseResult, _context?: Partial<LintContext>): ParseResult | null;
13
+ }
14
+ export {};
@@ -0,0 +1,8 @@
1
+ import { ParserRule } from "../types.js";
2
+ import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js";
3
+ import type { ParseResult } from "@herb-tools/core";
4
+ export declare class ERBNoCaseNodeChildrenRule extends ParserRule {
5
+ name: string;
6
+ get defaultConfig(): FullRuleConfig;
7
+ check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[];
8
+ }