@herb-tools/linter 0.7.5 → 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 (394) hide show
  1. package/README.md +253 -13
  2. package/dist/herb-lint.js +26023 -3424
  3. package/dist/herb-lint.js.map +1 -1
  4. package/dist/index.cjs +5759 -1583
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.js +5727 -1584
  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 +38 -33
  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 +107 -42
  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 +31 -2
  42. package/dist/src/rules/erb-comment-syntax.js.map +1 -1
  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 +69 -11
  60. package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -1
  61. package/dist/src/rules/erb-right-trim.js +26 -9
  62. package/dist/src/rules/erb-right-trim.js.map +1 -1
  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 +19 -3
  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} +44 -16
  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 +12 -5
  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 +12 -5
  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 +19 -3
  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 +12 -5
  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 +12 -5
  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 +19 -3
  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 +16 -2
  285. package/docs/rules/erb-no-case-node-children.md +50 -0
  286. package/docs/rules/erb-no-extra-newline.md +74 -0
  287. package/docs/rules/erb-no-extra-whitespace-inside-tags.md +39 -0
  288. package/docs/rules/{erb-requires-trailing-newline.md → erb-require-trailing-newline.md} +1 -1
  289. package/docs/rules/erb-right-trim.md +5 -10
  290. package/docs/rules/herb-disable-comment-malformed.md +45 -0
  291. package/docs/rules/herb-disable-comment-missing-rules.md +60 -0
  292. package/docs/rules/herb-disable-comment-no-duplicate-rules.md +49 -0
  293. package/docs/rules/herb-disable-comment-no-redundant-all.md +53 -0
  294. package/docs/rules/herb-disable-comment-unnecessary.md +44 -0
  295. package/docs/rules/herb-disable-comment-valid-rule-name.md +41 -0
  296. package/docs/rules/html-aria-attribute-must-be-valid.md +2 -5
  297. package/docs/rules/html-aria-label-is-well-formatted.md +1 -1
  298. package/docs/rules/html-attribute-double-quotes.md +2 -2
  299. package/docs/rules/html-attribute-equals-spacing.md +2 -2
  300. package/docs/rules/html-attribute-values-require-quotes.md +3 -3
  301. package/docs/rules/html-avoid-both-disabled-and-aria-disabled.md +2 -2
  302. package/docs/rules/html-body-only-elements.md +99 -0
  303. package/docs/rules/html-boolean-attributes-no-value.md +2 -2
  304. package/docs/rules/html-head-only-elements.md +81 -0
  305. package/docs/rules/html-input-require-autocomplete.md +64 -0
  306. package/docs/rules/html-no-aria-hidden-on-focusable.md +2 -2
  307. package/docs/rules/html-no-duplicate-attributes.md +2 -2
  308. package/docs/rules/html-no-duplicate-meta-names.md +64 -0
  309. package/docs/rules/html-no-empty-attributes.md +3 -3
  310. package/docs/rules/html-no-empty-headings.md +4 -26
  311. package/docs/rules/html-no-positive-tab-index.md +1 -2
  312. package/docs/rules/html-no-self-closing.md +17 -2
  313. package/docs/rules/html-no-space-in-tag.md +66 -0
  314. package/docs/rules/html-no-title-attribute.md +2 -2
  315. package/docs/rules/html-no-underscores-in-attribute-names.md +2 -2
  316. package/docs/rules/html-tag-name-lowercase.md +2 -2
  317. package/package.json +13 -5
  318. package/src/cli/argument-parser.ts +46 -37
  319. package/src/cli/file-processor.ts +159 -28
  320. package/src/cli/formatters/detailed-formatter.ts +21 -3
  321. package/src/cli/formatters/github-actions-formatter.ts +17 -1
  322. package/src/cli/formatters/json-formatter.ts +9 -0
  323. package/src/cli/formatters/simple-formatter.ts +24 -8
  324. package/src/cli/output-manager.ts +23 -3
  325. package/src/cli/summary-reporter.ts +40 -3
  326. package/src/cli.ts +134 -51
  327. package/src/custom-rule-loader.ts +189 -0
  328. package/src/herb-disable-comment-utils.ts +175 -0
  329. package/src/index.ts +2 -0
  330. package/src/linter.ts +501 -36
  331. package/src/loader.ts +30 -0
  332. package/src/rules/erb-comment-syntax.ts +53 -10
  333. package/src/rules/erb-no-case-node-children.ts +68 -0
  334. package/src/rules/erb-no-empty-tags.ts +9 -3
  335. package/src/rules/erb-no-extra-newline.ts +91 -0
  336. package/src/rules/erb-no-extra-whitespace-inside-tags.ts +147 -0
  337. package/src/rules/erb-no-output-control-flow.ts +9 -3
  338. package/src/rules/erb-no-silent-tag-in-attribute-name.ts +9 -3
  339. package/src/rules/erb-prefer-image-tag-helper.ts +9 -3
  340. package/src/rules/erb-require-trailing-newline.ts +47 -0
  341. package/src/rules/erb-require-whitespace-inside-tags.ts +94 -16
  342. package/src/rules/erb-right-trim.ts +45 -22
  343. package/src/rules/herb-disable-comment-base.ts +76 -0
  344. package/src/rules/herb-disable-comment-malformed.ts +66 -0
  345. package/src/rules/herb-disable-comment-missing-rules.ts +41 -0
  346. package/src/rules/herb-disable-comment-no-duplicate-rules.ts +46 -0
  347. package/src/rules/herb-disable-comment-no-redundant-all.ts +40 -0
  348. package/src/rules/herb-disable-comment-unnecessary.ts +103 -0
  349. package/src/rules/herb-disable-comment-valid-rule-name.ts +62 -0
  350. package/src/rules/html-anchor-require-href.ts +9 -3
  351. package/src/rules/html-aria-attribute-must-be-valid.ts +9 -3
  352. package/src/rules/html-aria-label-is-well-formatted.ts +9 -5
  353. package/src/rules/html-aria-level-must-be-valid.ts +9 -2
  354. package/src/rules/html-aria-role-heading-requires-level.ts +9 -3
  355. package/src/rules/html-aria-role-must-be-valid.ts +9 -3
  356. package/src/rules/html-attribute-double-quotes.ts +42 -8
  357. package/src/rules/html-attribute-equals-spacing.ts +31 -7
  358. package/src/rules/html-attribute-values-require-quotes.ts +56 -10
  359. package/src/rules/html-avoid-both-disabled-and-aria-disabled.ts +9 -3
  360. package/src/rules/html-body-only-elements.ts +60 -0
  361. package/src/rules/html-boolean-attributes-no-value.ts +31 -6
  362. package/src/rules/html-head-only-elements.ts +65 -0
  363. package/src/rules/html-iframe-has-title.ts +9 -4
  364. package/src/rules/html-img-require-alt.ts +10 -4
  365. package/src/rules/html-input-require-autocomplete.ts +85 -0
  366. package/src/rules/html-navigation-has-label.ts +9 -3
  367. package/src/rules/html-no-aria-hidden-on-focusable.ts +9 -3
  368. package/src/rules/html-no-block-inside-inline.ts +9 -3
  369. package/src/rules/html-no-duplicate-attributes.ts +9 -3
  370. package/src/rules/html-no-duplicate-ids.ts +11 -7
  371. package/src/rules/html-no-duplicate-meta-names.ts +188 -0
  372. package/src/rules/html-no-empty-attributes.ts +58 -10
  373. package/src/rules/html-no-empty-headings.ts +10 -8
  374. package/src/rules/html-no-nested-links.ts +10 -4
  375. package/src/rules/html-no-positive-tab-index.ts +9 -3
  376. package/src/rules/html-no-self-closing.ts +69 -9
  377. package/src/rules/html-no-space-in-tag.ts +221 -0
  378. package/src/rules/html-no-title-attribute.ts +9 -3
  379. package/src/rules/html-no-underscores-in-attribute-names.ts +12 -4
  380. package/src/rules/html-tag-name-lowercase.ts +41 -10
  381. package/src/rules/index.ts +23 -3
  382. package/src/rules/parser-no-errors.ts +8 -1
  383. package/src/rules/rule-utils.ts +248 -42
  384. package/src/rules/svg-tag-name-capitalization.ts +39 -6
  385. package/src/{default-rules.ts → rules.ts} +51 -15
  386. package/src/types.ts +133 -15
  387. package/dist/src/default-rules.js.map +0 -1
  388. package/dist/src/rules/erb-requires-trailing-newline.js +0 -22
  389. package/dist/src/rules/erb-requires-trailing-newline.js.map +0 -1
  390. package/dist/types/default-rules.d.ts +0 -2
  391. package/dist/types/rules/erb-requires-trailing-newline.d.ts +0 -6
  392. package/dist/types/src/default-rules.d.ts +0 -2
  393. package/dist/types/src/rules/erb-requires-trailing-newline.d.ts +0 -6
  394. package/src/rules/erb-requires-trailing-newline.ts +0 -29
@@ -1,9 +1,6 @@
1
1
  import dedent from "dedent"
2
2
 
3
3
  import { parseArgs } from "util"
4
- import { statSync } from "fs"
5
- import { join } from "path"
6
-
7
4
  import { Herb } from "@herb-tools/node-wasm"
8
5
 
9
6
  import { THEME_NAMES, DEFAULT_THEME } from "@herb-tools/highlighter"
@@ -14,37 +11,48 @@ import { name, version, dependencies } from "../../package.json"
14
11
  export type FormatOption = "simple" | "detailed" | "json"
15
12
 
16
13
  export interface ParsedArguments {
17
- pattern: string
14
+ patterns: string[]
15
+ configFile?: string
18
16
  formatOption: FormatOption
19
17
  showTiming: boolean
20
18
  theme: ThemeInput
21
19
  wrapLines: boolean
22
20
  truncateLines: boolean
23
21
  useGitHubActions: boolean
22
+ fix: boolean
23
+ ignoreDisableComments: boolean
24
+ force: boolean
25
+ init: boolean
26
+ loadCustomRules: boolean
24
27
  }
25
28
 
26
29
  export class ArgumentParser {
27
30
  private readonly usage = dedent`
28
- Usage: herb-lint [file|glob-pattern|directory] [options]
31
+ Usage: herb-lint [files|directories|glob-patterns...] [options]
29
32
 
30
33
  Arguments:
31
- file Single file to lint
32
- glob-pattern Files to lint (defaults to **/*.html.erb)
33
- directory Directory to lint (automatically appends **/*.html.erb)
34
+ files Files, directories, or glob patterns to lint (defaults to configured extensions in .herb.yml)
35
+ Multiple arguments are supported (e.g., herb-lint file1.erb file2.erb dir/ "**/*.erb")
34
36
 
35
37
  Options:
36
- -h, --help show help
37
- -v, --version show version
38
- --format output format (simple|detailed|json) [default: detailed]
39
- --simple use simple output format (shortcut for --format simple)
40
- --json use JSON output format (shortcut for --format json)
41
- --github enable GitHub Actions annotations (combines with --format)
42
- --no-github disable GitHub Actions annotations (even in GitHub Actions environment)
43
- --theme syntax highlighting theme (${THEME_NAMES.join("|")}) or path to custom theme file [default: ${DEFAULT_THEME}]
44
- --no-color disable colored output
45
- --no-timing hide timing information
46
- --no-wrap-lines disable line wrapping
47
- --truncate-lines enable line truncation (mutually exclusive with line wrapping)
38
+ -h, --help show help
39
+ -v, --version show version
40
+ --init create a .herb.yml configuration file in the current directory
41
+ -c, --config-file <path> explicitly specify path to .herb.yml config file
42
+ --force force linting even if disabled in .herb.yml
43
+ --fix automatically fix auto-correctable offenses
44
+ --ignore-disable-comments report offenses even when suppressed with <%# herb:disable %> comments
45
+ --format output format (simple|detailed|json) [default: detailed]
46
+ --simple use simple output format (shortcut for --format simple)
47
+ --json use JSON output format (shortcut for --format json)
48
+ --github enable GitHub Actions annotations (combines with --format)
49
+ --no-github disable GitHub Actions annotations (even in GitHub Actions environment)
50
+ --no-custom-rules disable loading custom rules from project (custom rules are loaded by default from .herb/rules/**/*.{mjs,js})
51
+ --theme syntax highlighting theme (${THEME_NAMES.join("|")}) or path to custom theme file [default: ${DEFAULT_THEME}]
52
+ --no-color disable colored output
53
+ --no-timing hide timing information
54
+ --no-wrap-lines disable line wrapping
55
+ --truncate-lines enable line truncation (mutually exclusive with line wrapping)
48
56
  `
49
57
 
50
58
  parse(argv: string[]): ParsedArguments {
@@ -53,6 +61,11 @@ export class ArgumentParser {
53
61
  options: {
54
62
  help: { type: "boolean", short: "h" },
55
63
  version: { type: "boolean", short: "v" },
64
+ init: { type: "boolean" },
65
+ "config-file": { type: "string", short: "c" },
66
+ force: { type: "boolean" },
67
+ fix: { type: "boolean" },
68
+ "ignore-disable-comments": { type: "boolean" },
56
69
  format: { type: "string" },
57
70
  simple: { type: "boolean" },
58
71
  json: { type: "boolean" },
@@ -62,7 +75,8 @@ export class ArgumentParser {
62
75
  "no-color": { type: "boolean" },
63
76
  "no-timing": { type: "boolean" },
64
77
  "no-wrap-lines": { type: "boolean" },
65
- "truncate-lines": { type: "boolean" }
78
+ "truncate-lines": { type: "boolean" },
79
+ "no-custom-rules": { type: "boolean" }
66
80
  },
67
81
  allowPositionals: true
68
82
  })
@@ -122,23 +136,18 @@ export class ArgumentParser {
122
136
  }
123
137
 
124
138
  const theme = values.theme || DEFAULT_THEME
125
- const pattern = this.getFilePattern(positionals)
126
-
127
- return { pattern, formatOption, showTiming, theme, wrapLines, truncateLines, useGitHubActions }
139
+ const patterns = this.getFilePatterns(positionals)
140
+ const fix = values.fix || false
141
+ const force = !!values.force
142
+ const ignoreDisableComments = values["ignore-disable-comments"] || false
143
+ const configFile = values["config-file"]
144
+ const init = values.init || false
145
+ const loadCustomRules = !values["no-custom-rules"]
146
+
147
+ return { patterns, configFile, formatOption, showTiming, theme, wrapLines, truncateLines, useGitHubActions, fix, ignoreDisableComments, force, init, loadCustomRules }
128
148
  }
129
149
 
130
- private getFilePattern(positionals: string[]): string {
131
- let pattern = positionals.length > 0 ? positionals[0] : "**/*.html.erb"
132
-
133
- try {
134
- const stat = statSync(pattern)
135
- if (stat.isDirectory()) {
136
- pattern = join(pattern, "**/*.html.erb")
137
- }
138
- } catch {
139
- // Not a file/directory, treat as glob pattern
140
- }
141
-
142
- return pattern
150
+ private getFilePatterns(positionals: string[]): string[] {
151
+ return positionals
143
152
  }
144
153
  }
@@ -1,26 +1,42 @@
1
- import { readFileSync } from "fs"
2
- import { resolve } from "path"
3
1
  import { Herb } from "@herb-tools/node-wasm"
4
2
  import { Linter } from "../linter.js"
3
+ import { loadCustomRules } from "../loader.js"
4
+ import { Config } from "@herb-tools/config"
5
+
6
+ import { readFileSync, writeFileSync } from "fs"
7
+ import { resolve } from "path"
5
8
  import { colorize } from "@herb-tools/highlighter"
9
+
6
10
  import type { Diagnostic } from "@herb-tools/core"
7
11
  import type { FormatOption } from "./argument-parser.js"
12
+ import type { HerbConfigOptions } from "@herb-tools/config"
8
13
 
9
14
  export interface ProcessedFile {
10
15
  filename: string
11
16
  offense: Diagnostic
12
17
  content: string
18
+ autocorrectable?: boolean
13
19
  }
14
20
 
15
21
  export interface ProcessingContext {
16
22
  projectPath?: string
17
23
  pattern?: string
24
+ fix?: boolean
25
+ ignoreDisableComments?: boolean
26
+ linterConfig?: HerbConfigOptions['linter']
27
+ config?: Config
28
+ loadCustomRules?: boolean
18
29
  }
19
30
 
20
31
  export interface ProcessingResult {
21
32
  totalErrors: number
22
33
  totalWarnings: number
34
+ totalInfo: number
35
+ totalHints: number
36
+ totalIgnored: number
37
+ totalWouldBeIgnored?: number
23
38
  filesWithOffenses: number
39
+ filesFixed: number
24
40
  ruleCount: number
25
41
  allOffenses: ProcessedFile[]
26
42
  ruleOffenses: Map<string, { count: number, files: Set<string> }>
@@ -29,55 +45,146 @@ export interface ProcessingResult {
29
45
 
30
46
  export class FileProcessor {
31
47
  private linter: Linter | null = null
48
+ private customRulesLoaded: boolean = false
49
+
50
+ private isRuleAutocorrectable(ruleName: string): boolean {
51
+ if (!this.linter) return false
52
+
53
+ const RuleClass = (this.linter as any).rules.find((rule: any) => {
54
+ const instance = new rule()
55
+
56
+ return instance.name === ruleName
57
+ })
58
+
59
+ if (!RuleClass) return false
60
+
61
+ return RuleClass.autocorrectable === true
62
+ }
32
63
 
33
64
  async processFiles(files: string[], formatOption: FormatOption = 'detailed', context?: ProcessingContext): Promise<ProcessingResult> {
34
65
  let totalErrors = 0
35
66
  let totalWarnings = 0
67
+ let totalInfo = 0
68
+ let totalHints = 0
69
+ let totalIgnored = 0
70
+ let totalWouldBeIgnored = 0
36
71
  let filesWithOffenses = 0
72
+ let filesFixed = 0
37
73
  let ruleCount = 0
74
+
38
75
  const allOffenses: ProcessedFile[] = []
39
76
  const ruleOffenses = new Map<string, { count: number, files: Set<string> }>()
40
77
 
41
- for (const filename of files) {
42
- const filePath = context?.projectPath ? resolve(context.projectPath, filename) : resolve(filename)
43
- const content = readFileSync(filePath, "utf-8")
44
- const parseResult = Herb.parse(content)
45
-
46
- if (parseResult.errors.length > 0) {
47
- if (formatOption !== 'json') {
48
- console.error(`${colorize(filename, "cyan")} - ${colorize("Parse errors:", "brightRed")}`)
49
-
50
- for (const error of parseResult.errors) {
51
- console.error(` ${colorize("✗", "brightRed")} ${error.message}`)
78
+ if (!this.linter) {
79
+ let customRules = undefined
80
+ let customRuleInfo: Array<{ name: string, path: string }> = []
81
+ let customRuleWarnings: string[] = []
82
+
83
+ if (context?.loadCustomRules && !this.customRulesLoaded) {
84
+ try {
85
+ const result = await loadCustomRules({
86
+ baseDir: context.projectPath,
87
+ silent: formatOption === 'json'
88
+ })
89
+
90
+ customRules = result.rules
91
+ customRuleInfo = result.ruleInfo
92
+ customRuleWarnings = result.warnings
93
+
94
+ this.customRulesLoaded = true
95
+
96
+ if (customRules.length > 0 && formatOption !== 'json') {
97
+ const ruleText = customRules.length === 1 ? 'rule' : 'rules'
98
+ console.log(colorize(`\nLoaded ${customRules.length} custom ${ruleText}:`, "green"))
99
+
100
+ for (const { name, path } of customRuleInfo) {
101
+ const relativePath = context.projectPath ? path.replace(context.projectPath + '/', '') : path
102
+ console.log(colorize(` • ${name}`, "cyan") + colorize(` (${relativePath})`, "dim"))
103
+ }
104
+
105
+ if (customRuleWarnings.length > 0) {
106
+ console.log()
107
+ for (const warning of customRuleWarnings) {
108
+ console.warn(colorize(` ⚠ ${warning}`, "yellow"))
109
+ }
110
+ }
111
+
112
+ console.log()
113
+ }
114
+ } catch (error) {
115
+ if (formatOption !== 'json') {
116
+ console.warn(colorize(`Warning: Failed to load custom rules: ${error}`, "yellow"))
52
117
  }
53
118
  }
54
-
55
- for (const error of parseResult.errors) {
56
- allOffenses.push({ filename, offense: error, content })
57
- }
58
-
59
- totalErrors++
60
- filesWithOffenses++
61
- continue
62
119
  }
63
120
 
64
- if (!this.linter) {
65
- this.linter = new Linter(Herb)
66
- }
121
+ this.linter = Linter.from(Herb, context?.config, customRules)
122
+ }
67
123
 
68
- const lintResult = this.linter.lint(content, { fileName: filename })
124
+ for (const filename of files) {
125
+ const filePath = context?.projectPath ? resolve(context.projectPath, filename) : resolve(filename)
126
+ let content = readFileSync(filePath, "utf-8")
127
+
128
+ const lintResult = this.linter.lint(content, {
129
+ fileName: filename,
130
+ ignoreDisableComments: context?.ignoreDisableComments
131
+ })
69
132
 
70
133
  if (ruleCount === 0) {
71
134
  ruleCount = this.linter.getRuleCount()
72
135
  }
73
136
 
74
- if (lintResult.offenses.length === 0) {
137
+ if (context?.fix && lintResult.offenses.length > 0) {
138
+ const autofixResult = this.linter.autofix(content, {
139
+ fileName: filename,
140
+ ignoreDisableComments: context?.ignoreDisableComments
141
+ })
142
+
143
+ if (autofixResult.fixed.length > 0) {
144
+ writeFileSync(filePath, autofixResult.source, "utf-8")
145
+
146
+ filesFixed++
147
+
148
+ if (formatOption !== 'json') {
149
+ console.log(`${colorize("✓", "brightGreen")} ${colorize(filename, "cyan")} - ${colorize(`Fixed ${autofixResult.fixed.length} offense(s)`, "green")}`)
150
+ }
151
+ }
152
+
153
+ content = autofixResult.source
154
+
155
+ for (const offense of autofixResult.unfixed) {
156
+ allOffenses.push({
157
+ filename,
158
+ offense: offense,
159
+ content,
160
+ autocorrectable: this.isRuleAutocorrectable(offense.rule)
161
+ })
162
+
163
+ const ruleData = ruleOffenses.get(offense.rule) || { count: 0, files: new Set() }
164
+ ruleData.count++
165
+ ruleData.files.add(filename)
166
+ ruleOffenses.set(offense.rule, ruleData)
167
+ }
168
+
169
+ if (autofixResult.unfixed.length > 0) {
170
+ totalErrors += autofixResult.unfixed.filter(offense => offense.severity === "error").length
171
+ totalWarnings += autofixResult.unfixed.filter(offense => offense.severity === "warning").length
172
+ totalInfo += autofixResult.unfixed.filter(offense => offense.severity === "info").length
173
+ totalHints += autofixResult.unfixed.filter(offense => offense.severity === "hint").length
174
+ filesWithOffenses++
175
+ }
176
+ } else if (lintResult.offenses.length === 0) {
75
177
  if (files.length === 1 && formatOption !== 'json') {
76
178
  console.log(`${colorize("✓", "brightGreen")} ${colorize(filename, "cyan")} - ${colorize("No issues found", "green")}`)
77
179
  }
78
180
  } else {
79
181
  for (const offense of lintResult.offenses) {
80
- allOffenses.push({ filename, offense: offense, content })
182
+ allOffenses.push({
183
+ filename,
184
+ offense: offense,
185
+ content,
186
+ autocorrectable: this.isRuleAutocorrectable(offense.rule)
187
+ })
81
188
 
82
189
  const ruleData = ruleOffenses.get(offense.rule) || { count: 0, files: new Set() }
83
190
  ruleData.count++
@@ -87,10 +194,34 @@ export class FileProcessor {
87
194
 
88
195
  totalErrors += lintResult.errors
89
196
  totalWarnings += lintResult.warnings
197
+ totalInfo += lintResult.offenses.filter(o => o.severity === "info").length
198
+ totalHints += lintResult.offenses.filter(o => o.severity === "hint").length
90
199
  filesWithOffenses++
91
200
  }
201
+ totalIgnored += lintResult.ignored
202
+ if (lintResult.wouldBeIgnored) {
203
+ totalWouldBeIgnored += lintResult.wouldBeIgnored
204
+ }
205
+ }
206
+
207
+ const result: ProcessingResult = {
208
+ totalErrors,
209
+ totalWarnings,
210
+ totalInfo,
211
+ totalHints,
212
+ totalIgnored,
213
+ filesWithOffenses,
214
+ filesFixed,
215
+ ruleCount,
216
+ allOffenses,
217
+ ruleOffenses,
218
+ context
219
+ }
220
+
221
+ if (totalWouldBeIgnored > 0) {
222
+ result.totalWouldBeIgnored = totalWouldBeIgnored
92
223
  }
93
224
 
94
- return { totalErrors, totalWarnings, filesWithOffenses, ruleCount, allOffenses, ruleOffenses, context }
225
+ return result
95
226
  }
96
227
  }
@@ -29,7 +29,15 @@ export class DetailedFormatter extends BaseFormatter {
29
29
 
30
30
  if (isSingleFile) {
31
31
  const { filename, content } = allOffenses[0]
32
- const diagnostics = allOffenses.map(item => item.offense)
32
+ const diagnostics = allOffenses.map(item => {
33
+ if (item.autocorrectable && item.offense.code) {
34
+ return {
35
+ ...item.offense,
36
+ message: `${item.offense.message} ${colorize(colorize("[Correctable]", "green"), "bold")}`
37
+ }
38
+ }
39
+ return item.offense
40
+ })
33
41
 
34
42
  const highlighted = this.highlighter.highlight(filename, content, {
35
43
  diagnostics: diagnostics,
@@ -44,8 +52,18 @@ export class DetailedFormatter extends BaseFormatter {
44
52
  const totalMessageCount = allOffenses.length
45
53
 
46
54
  for (let i = 0; i < allOffenses.length; i++) {
47
- const { filename, offense, content } = allOffenses[i]
48
- const formatted = this.highlighter.highlightDiagnostic(filename, offense, content, {
55
+ const { filename, offense, content, autocorrectable } = allOffenses[i]
56
+
57
+ let modifiedOffense = offense
58
+
59
+ if (autocorrectable && offense.code) {
60
+ modifiedOffense = {
61
+ ...offense,
62
+ message: `${offense.message} ${colorize(colorize("[Correctable]", "green"), "bold")}`
63
+ }
64
+ }
65
+
66
+ const formatted = this.highlighter.highlightDiagnostic(filename, modifiedOffense, content, {
49
67
  contextLines: 2,
50
68
  wrapLines: this.wrapLines,
51
69
  truncateLines: this.truncateLines
@@ -79,7 +79,23 @@ export class GitHubActionsFormatter extends BaseFormatter {
79
79
  // ::{level} file={file},line={line},col={col},title={title}::{message}
80
80
  //
81
81
  private formatDiagnostic(filename: string, diagnostic: Diagnostic, codePreview: string = ""): void {
82
- const level = diagnostic.severity === "error" ? "error" : "warning"
82
+ let level: string
83
+
84
+ switch (diagnostic.severity) {
85
+ case "error":
86
+ level = "error"
87
+ break
88
+ case "warning":
89
+ level = "warning"
90
+ break
91
+ case "info":
92
+ case "hint":
93
+ level = "notice"
94
+ break
95
+ default:
96
+ level = "warning"
97
+ }
98
+
83
99
  const { line, column } = diagnostic.location.start
84
100
 
85
101
  const escapedFilename = this.escapeParam(filename)
@@ -12,6 +12,9 @@ interface JSONSummary {
12
12
  filesWithOffenses: number
13
13
  totalErrors: number
14
14
  totalWarnings: number
15
+ totalInfo: number
16
+ totalHints: number
17
+ totalIgnored: number
15
18
  totalOffenses: number
16
19
  ruleCount: number
17
20
  }
@@ -34,6 +37,9 @@ interface JSONFormatOptions {
34
37
  files: string[]
35
38
  totalErrors: number
36
39
  totalWarnings: number
40
+ totalInfo: number
41
+ totalHints: number
42
+ totalIgnored: number
37
43
  filesWithOffenses: number
38
44
  ruleCount: number
39
45
  startTime: number
@@ -79,6 +85,9 @@ export class JSONFormatter extends BaseFormatter {
79
85
  filesWithOffenses: options.filesWithOffenses,
80
86
  totalErrors: options.totalErrors,
81
87
  totalWarnings: options.totalWarnings,
88
+ totalInfo: options.totalInfo,
89
+ totalHints: options.totalHints,
90
+ totalIgnored: options.totalIgnored,
82
91
  totalOffenses: options.totalErrors + options.totalWarnings,
83
92
  ruleCount: options.ruleCount
84
93
  }
@@ -9,17 +9,17 @@ export class SimpleFormatter extends BaseFormatter {
9
9
  async format(allOffenses: ProcessedFile[]): Promise<void> {
10
10
  if (allOffenses.length === 0) return
11
11
 
12
- const groupedOffenses = new Map<string, Diagnostic[]>()
12
+ const groupedOffenses = new Map<string, ProcessedFile[]>()
13
13
 
14
- for (const { filename, offense } of allOffenses) {
15
- const offenses = groupedOffenses.get(filename) || []
16
- offenses.push(offense)
17
- groupedOffenses.set(filename, offenses)
14
+ for (const processedFile of allOffenses) {
15
+ const offenses = groupedOffenses.get(processedFile.filename) || []
16
+ offenses.push(processedFile)
17
+ groupedOffenses.set(processedFile.filename, offenses)
18
18
  }
19
19
 
20
- for (const [filename, offenses] of groupedOffenses) {
20
+ for (const [filename, processedFiles] of groupedOffenses) {
21
21
  console.log("")
22
- this.formatFile(filename, offenses)
22
+ this.formatFileProcessed(filename, processedFiles)
23
23
  }
24
24
  }
25
25
 
@@ -31,10 +31,26 @@ export class SimpleFormatter extends BaseFormatter {
31
31
  const severity = isError ? colorize("✗", "brightRed") : colorize("⚠", "brightYellow")
32
32
  const rule = colorize(`(${offense.code})`, "blue")
33
33
  const locationString = `${offense.location.start.line}:${offense.location.start.column}`
34
- const paddedLocation = locationString.padEnd(4) // Pad to 4 characters for alignment
34
+ const paddedLocation = locationString.padEnd(4)
35
35
 
36
36
  console.log(` ${colorize(paddedLocation, "gray")} ${severity} ${offense.message} ${rule}`)
37
37
  }
38
38
  console.log("")
39
39
  }
40
+
41
+ formatFileProcessed(filename: string, processedFiles: ProcessedFile[]): void {
42
+ console.log(`${colorize(filename, "cyan")}:`)
43
+
44
+ for (const { offense, autocorrectable } of processedFiles) {
45
+ const isError = offense.severity === "error"
46
+ const severity = isError ? colorize("✗", "brightRed") : colorize("⚠", "brightYellow")
47
+ const rule = colorize(`(${offense.code})`, "blue")
48
+ const locationString = `${offense.location.start.line}:${offense.location.start.column}`
49
+ const paddedLocation = locationString.padEnd(4)
50
+ const correctable = autocorrectable ? colorize(colorize(" [Correctable]", "green"), "bold") : ""
51
+
52
+ console.log(` ${colorize(paddedLocation, "gray")} ${severity} ${offense.message} ${rule}${correctable}`)
53
+ }
54
+ console.log("")
55
+ }
40
56
  }
@@ -27,7 +27,9 @@ export class OutputManager {
27
27
  * Output successful lint results
28
28
  */
29
29
  async outputResults(results: LintResults, options: OutputOptions): Promise<void> {
30
- const { allOffenses, files, totalErrors, totalWarnings, filesWithOffenses, ruleCount, ruleOffenses } = results
30
+ const { allOffenses, files, totalErrors, totalWarnings, totalInfo, totalHints, totalIgnored, totalWouldBeIgnored, filesWithOffenses, ruleCount, ruleOffenses, context } = results
31
+
32
+ const autofixableCount = allOffenses.filter(offense => offense.autocorrectable).length
31
33
 
32
34
  if (options.useGitHubActions) {
33
35
  const githubFormatter = new GitHubActionsFormatter(options.wrapLines, options.truncateLines)
@@ -45,12 +47,18 @@ export class OutputManager {
45
47
  files,
46
48
  totalErrors,
47
49
  totalWarnings,
50
+ totalInfo,
51
+ totalHints,
52
+ totalIgnored,
53
+ totalWouldBeIgnored,
48
54
  filesWithOffenses,
49
55
  ruleCount,
50
56
  startTime: options.startTime,
51
57
  startDate: options.startDate,
52
58
  showTiming: options.showTiming,
53
- ruleOffenses
59
+ ruleOffenses,
60
+ autofixableCount,
61
+ ignoreDisableComments: context?.ignoreDisableComments,
54
62
  })
55
63
  }
56
64
  } else if (options.formatOption === "json") {
@@ -68,6 +76,9 @@ export class OutputManager {
68
76
  filesWithOffenses,
69
77
  totalErrors,
70
78
  totalWarnings,
79
+ totalInfo,
80
+ totalHints,
81
+ totalIgnored,
71
82
  totalOffenses: totalErrors + totalWarnings,
72
83
  ruleCount
73
84
  },
@@ -96,12 +107,18 @@ export class OutputManager {
96
107
  files,
97
108
  totalErrors,
98
109
  totalWarnings,
110
+ totalInfo,
111
+ totalHints,
112
+ totalIgnored,
113
+ totalWouldBeIgnored,
99
114
  filesWithOffenses,
100
115
  ruleCount,
101
116
  startTime: options.startTime,
102
117
  startDate: options.startDate,
103
118
  showTiming: options.showTiming,
104
- ruleOffenses
119
+ ruleOffenses,
120
+ autofixableCount,
121
+ ignoreDisableComments: context?.ignoreDisableComments,
105
122
  })
106
123
  }
107
124
  }
@@ -120,6 +137,9 @@ export class OutputManager {
120
137
  filesWithOffenses: 0,
121
138
  totalErrors: 0,
122
139
  totalWarnings: 0,
140
+ totalInfo: 0,
141
+ totalHints: 0,
142
+ totalIgnored: 0,
123
143
  totalOffenses: 0,
124
144
  ruleCount: 0
125
145
  },