@herb-tools/linter 0.4.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 (175) hide show
  1. package/README.md +34 -0
  2. package/bin/herb-lint +3 -0
  3. package/dist/herb-lint.js +16505 -0
  4. package/dist/herb-lint.js.map +1 -0
  5. package/dist/index.cjs +834 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.js +820 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/package.json +49 -0
  10. package/dist/src/cli/argument-parser.js +96 -0
  11. package/dist/src/cli/argument-parser.js.map +1 -0
  12. package/dist/src/cli/file-processor.js +58 -0
  13. package/dist/src/cli/file-processor.js.map +1 -0
  14. package/dist/src/cli/formatters/base-formatter.js +3 -0
  15. package/dist/src/cli/formatters/base-formatter.js.map +1 -0
  16. package/dist/src/cli/formatters/detailed-formatter.js +62 -0
  17. package/dist/src/cli/formatters/detailed-formatter.js.map +1 -0
  18. package/dist/src/cli/formatters/index.js +4 -0
  19. package/dist/src/cli/formatters/index.js.map +1 -0
  20. package/dist/src/cli/formatters/simple-formatter.js +31 -0
  21. package/dist/src/cli/formatters/simple-formatter.js.map +1 -0
  22. package/dist/src/cli/index.js +5 -0
  23. package/dist/src/cli/index.js.map +1 -0
  24. package/dist/src/cli/summary-reporter.js +96 -0
  25. package/dist/src/cli/summary-reporter.js.map +1 -0
  26. package/dist/src/cli.js +50 -0
  27. package/dist/src/cli.js.map +1 -0
  28. package/dist/src/default-rules.js +31 -0
  29. package/dist/src/default-rules.js.map +1 -0
  30. package/dist/src/herb-lint.js +5 -0
  31. package/dist/src/herb-lint.js.map +1 -0
  32. package/dist/src/index.js +4 -0
  33. package/dist/src/index.js.map +1 -0
  34. package/dist/src/linter.js +39 -0
  35. package/dist/src/linter.js.map +1 -0
  36. package/dist/src/rules/erb-no-empty-tags.js +23 -0
  37. package/dist/src/rules/erb-no-empty-tags.js.map +1 -0
  38. package/dist/src/rules/erb-no-output-control-flow.js +47 -0
  39. package/dist/src/rules/erb-no-output-control-flow.js.map +1 -0
  40. package/dist/src/rules/erb-require-whitespace-inside-tags.js +43 -0
  41. package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -0
  42. package/dist/src/rules/html-anchor-require-href.js +25 -0
  43. package/dist/src/rules/html-anchor-require-href.js.map +1 -0
  44. package/dist/src/rules/html-aria-role-heading-requires-level.js +26 -0
  45. package/dist/src/rules/html-aria-role-heading-requires-level.js.map +1 -0
  46. package/dist/src/rules/html-attribute-double-quotes.js +21 -0
  47. package/dist/src/rules/html-attribute-double-quotes.js.map +1 -0
  48. package/dist/src/rules/html-attribute-values-require-quotes.js +22 -0
  49. package/dist/src/rules/html-attribute-values-require-quotes.js.map +1 -0
  50. package/dist/src/rules/html-boolean-attributes-no-value.js +19 -0
  51. package/dist/src/rules/html-boolean-attributes-no-value.js.map +1 -0
  52. package/dist/src/rules/html-img-require-alt.js +29 -0
  53. package/dist/src/rules/html-img-require-alt.js.map +1 -0
  54. package/dist/src/rules/html-no-block-inside-inline.js +59 -0
  55. package/dist/src/rules/html-no-block-inside-inline.js.map +1 -0
  56. package/dist/src/rules/html-no-duplicate-attributes.js +43 -0
  57. package/dist/src/rules/html-no-duplicate-attributes.js.map +1 -0
  58. package/dist/src/rules/html-no-empty-headings.js +148 -0
  59. package/dist/src/rules/html-no-empty-headings.js.map +1 -0
  60. package/dist/src/rules/html-no-nested-links.js +45 -0
  61. package/dist/src/rules/html-no-nested-links.js.map +1 -0
  62. package/dist/src/rules/html-tag-name-lowercase.js +39 -0
  63. package/dist/src/rules/html-tag-name-lowercase.js.map +1 -0
  64. package/dist/src/rules/index.js +13 -0
  65. package/dist/src/rules/index.js.map +1 -0
  66. package/dist/src/rules/rule-utils.js +198 -0
  67. package/dist/src/rules/rule-utils.js.map +1 -0
  68. package/dist/src/types.js +2 -0
  69. package/dist/src/types.js.map +1 -0
  70. package/dist/tsconfig.tsbuildinfo +1 -0
  71. package/dist/types/cli/argument-parser.d.ts +14 -0
  72. package/dist/types/cli/file-processor.d.ts +21 -0
  73. package/dist/types/cli/formatters/base-formatter.d.ts +6 -0
  74. package/dist/types/cli/formatters/detailed-formatter.d.ts +13 -0
  75. package/dist/types/cli/formatters/index.d.ts +3 -0
  76. package/dist/types/cli/formatters/simple-formatter.d.ts +7 -0
  77. package/dist/types/cli/summary-reporter.d.ts +22 -0
  78. package/dist/types/cli.d.ts +6 -0
  79. package/dist/types/default-rules.d.ts +2 -0
  80. package/dist/types/herb-lint.d.ts +2 -0
  81. package/dist/types/index.d.ts +3 -0
  82. package/dist/types/linter.d.ts +18 -0
  83. package/dist/types/rules/erb-no-empty-tags.d.ts +6 -0
  84. package/dist/types/rules/erb-no-output-control-flow.d.ts +6 -0
  85. package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +6 -0
  86. package/dist/types/rules/html-anchor-require-href.d.ts +6 -0
  87. package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +6 -0
  88. package/dist/types/rules/html-attribute-double-quotes.d.ts +6 -0
  89. package/dist/types/rules/html-attribute-values-require-quotes.d.ts +6 -0
  90. package/dist/types/rules/html-boolean-attributes-no-value.d.ts +6 -0
  91. package/dist/types/rules/html-img-require-alt.d.ts +6 -0
  92. package/dist/types/rules/html-no-block-inside-inline.d.ts +6 -0
  93. package/dist/types/rules/html-no-duplicate-attributes.d.ts +6 -0
  94. package/dist/types/rules/html-no-empty-headings.d.ts +6 -0
  95. package/dist/types/rules/html-no-nested-links.d.ts +6 -0
  96. package/dist/types/rules/html-tag-name-lowercase.d.ts +6 -0
  97. package/dist/types/rules/index.d.ts +12 -0
  98. package/dist/types/rules/rule-utils.d.ts +89 -0
  99. package/dist/types/src/cli/argument-parser.d.ts +14 -0
  100. package/dist/types/src/cli/file-processor.d.ts +21 -0
  101. package/dist/types/src/cli/formatters/base-formatter.d.ts +6 -0
  102. package/dist/types/src/cli/formatters/detailed-formatter.d.ts +13 -0
  103. package/dist/types/src/cli/formatters/index.d.ts +3 -0
  104. package/dist/types/src/cli/formatters/simple-formatter.d.ts +7 -0
  105. package/dist/types/src/cli/index.d.ts +4 -0
  106. package/dist/types/src/cli/summary-reporter.d.ts +22 -0
  107. package/dist/types/src/cli.d.ts +6 -0
  108. package/dist/types/src/default-rules.d.ts +2 -0
  109. package/dist/types/src/herb-lint.d.ts +2 -0
  110. package/dist/types/src/index.d.ts +3 -0
  111. package/dist/types/src/linter.d.ts +18 -0
  112. package/dist/types/src/rules/erb-no-empty-tags.d.ts +6 -0
  113. package/dist/types/src/rules/erb-no-output-control-flow.d.ts +6 -0
  114. package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +6 -0
  115. package/dist/types/src/rules/html-anchor-require-href.d.ts +6 -0
  116. package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +6 -0
  117. package/dist/types/src/rules/html-attribute-double-quotes.d.ts +6 -0
  118. package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +6 -0
  119. package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +6 -0
  120. package/dist/types/src/rules/html-img-require-alt.d.ts +6 -0
  121. package/dist/types/src/rules/html-no-block-inside-inline.d.ts +6 -0
  122. package/dist/types/src/rules/html-no-duplicate-attributes.d.ts +6 -0
  123. package/dist/types/src/rules/html-no-empty-headings.d.ts +6 -0
  124. package/dist/types/src/rules/html-no-nested-links.d.ts +6 -0
  125. package/dist/types/src/rules/html-tag-name-lowercase.d.ts +6 -0
  126. package/dist/types/src/rules/index.d.ts +12 -0
  127. package/dist/types/src/rules/rule-utils.d.ts +89 -0
  128. package/dist/types/src/types.d.ts +26 -0
  129. package/dist/types/types.d.ts +26 -0
  130. package/docs/rules/README.md +39 -0
  131. package/docs/rules/erb-no-empty-tags.md +38 -0
  132. package/docs/rules/erb-no-output-control-flow.md +45 -0
  133. package/docs/rules/erb-require-whitespace-inside-tags.md +43 -0
  134. package/docs/rules/html-anchor-require-href.md +32 -0
  135. package/docs/rules/html-aria-role-heading-requires-level.md +34 -0
  136. package/docs/rules/html-attribute-double-quotes.md +43 -0
  137. package/docs/rules/html-attribute-values-require-quotes.md +43 -0
  138. package/docs/rules/html-boolean-attributes-no-value.md +39 -0
  139. package/docs/rules/html-img-require-alt.md +44 -0
  140. package/docs/rules/html-no-block-inside-inline.md +66 -0
  141. package/docs/rules/html-no-duplicate-attributes.md +35 -0
  142. package/docs/rules/html-no-empty-headings.md +78 -0
  143. package/docs/rules/html-no-nested-links.md +44 -0
  144. package/docs/rules/html-tag-name-lowercase.md +44 -0
  145. package/package.json +49 -0
  146. package/src/cli/argument-parser.ts +125 -0
  147. package/src/cli/file-processor.ts +86 -0
  148. package/src/cli/formatters/base-formatter.ts +11 -0
  149. package/src/cli/formatters/detailed-formatter.ts +74 -0
  150. package/src/cli/formatters/index.ts +3 -0
  151. package/src/cli/formatters/simple-formatter.ts +40 -0
  152. package/src/cli/index.ts +4 -0
  153. package/src/cli/summary-reporter.ts +127 -0
  154. package/src/cli.ts +60 -0
  155. package/src/default-rules.ts +33 -0
  156. package/src/herb-lint.ts +6 -0
  157. package/src/index.ts +3 -0
  158. package/src/linter.ts +50 -0
  159. package/src/rules/erb-no-empty-tags.ts +34 -0
  160. package/src/rules/erb-no-output-control-flow.ts +61 -0
  161. package/src/rules/erb-require-whitespace-inside-tags.ts +61 -0
  162. package/src/rules/html-anchor-require-href.ts +39 -0
  163. package/src/rules/html-aria-role-heading-requires-level.ts +44 -0
  164. package/src/rules/html-attribute-double-quotes.ts +28 -0
  165. package/src/rules/html-attribute-values-require-quotes.ts +30 -0
  166. package/src/rules/html-boolean-attributes-no-value.ts +27 -0
  167. package/src/rules/html-img-require-alt.ts +42 -0
  168. package/src/rules/html-no-block-inside-inline.ts +84 -0
  169. package/src/rules/html-no-duplicate-attributes.ts +59 -0
  170. package/src/rules/html-no-empty-headings.ts +185 -0
  171. package/src/rules/html-no-nested-links.ts +65 -0
  172. package/src/rules/html-tag-name-lowercase.ts +50 -0
  173. package/src/rules/index.ts +12 -0
  174. package/src/rules/rule-utils.ts +257 -0
  175. package/src/types.ts +32 -0
@@ -0,0 +1,45 @@
1
+ import { BaseRuleVisitor, getTagName } from "./rule-utils.js";
2
+ class NestedLinkVisitor extends BaseRuleVisitor {
3
+ linkStack = [];
4
+ checkNestedLink(openTag) {
5
+ if (this.linkStack.length > 0) {
6
+ this.addOffense("Nested `<a>` elements are not allowed. Links cannot contain other links.", openTag.tag_name.location, "error");
7
+ return true;
8
+ }
9
+ return false;
10
+ }
11
+ visitHTMLElementNode(node) {
12
+ if (!node.open_tag || node.open_tag.type !== "AST_HTML_OPEN_TAG_NODE") {
13
+ super.visitHTMLElementNode(node);
14
+ return;
15
+ }
16
+ const openTag = node.open_tag;
17
+ const tagName = getTagName(openTag);
18
+ if (tagName !== "a") {
19
+ super.visitHTMLElementNode(node);
20
+ return;
21
+ }
22
+ // If we're already inside a link, this is a nested link
23
+ this.checkNestedLink(openTag);
24
+ this.linkStack.push(openTag);
25
+ super.visitHTMLElementNode(node);
26
+ this.linkStack.pop();
27
+ }
28
+ // Handle self-closing <a> tags (though they're not valid HTML, they might exist)
29
+ visitHTMLOpenTagNode(node) {
30
+ const tagName = getTagName(node);
31
+ if (tagName === "a" && node.is_void) {
32
+ this.checkNestedLink(node);
33
+ }
34
+ super.visitHTMLOpenTagNode(node);
35
+ }
36
+ }
37
+ export class HTMLNoNestedLinksRule {
38
+ name = "html-no-nested-links";
39
+ check(node) {
40
+ const visitor = new NestedLinkVisitor(this.name);
41
+ visitor.visit(node);
42
+ return visitor.offenses;
43
+ }
44
+ }
45
+ //# sourceMappingURL=html-no-nested-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-no-nested-links.js","sourceRoot":"","sources":["../../../src/rules/html-no-nested-links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAK7D,MAAM,iBAAkB,SAAQ,eAAe;IACrC,SAAS,GAAsB,EAAE,CAAA;IAEjC,eAAe,CAAC,OAAwB;QAC9C,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,UAAU,CACb,0EAA0E,EAC1E,OAAO,CAAC,QAAS,CAAC,QAAQ,EAC1B,OAAO,CACR,CAAA;YAED,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC;IAED,oBAAoB,CAAC,IAAqB;QACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;YACtE,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;YAChC,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,QAA2B,CAAA;QAChD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;QAEnC,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;YACpB,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;YAChC,OAAM;QACR,CAAC;QAED,wDAAwD;QACxD,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;QAE7B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5B,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAA;IACtB,CAAC;IAED,iFAAiF;IACjF,oBAAoB,CAAC,IAAqB;QACxC,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhC,IAAI,OAAO,KAAK,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,CAAC;QAED,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC;CACF;AAED,MAAM,OAAO,qBAAqB;IAChC,IAAI,GAAG,sBAAsB,CAAA;IAE7B,KAAK,CAAC,IAAU;QACd,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnB,OAAO,OAAO,CAAC,QAAQ,CAAA;IACzB,CAAC;CACF"}
@@ -0,0 +1,39 @@
1
+ import { BaseRuleVisitor } from "./rule-utils.js";
2
+ class TagNameLowercaseVisitor extends BaseRuleVisitor {
3
+ visitHTMLOpenTagNode(node) {
4
+ this.checkTagName(node);
5
+ this.visitChildNodes(node);
6
+ }
7
+ visitHTMLCloseTagNode(node) {
8
+ this.checkTagName(node);
9
+ this.visitChildNodes(node);
10
+ }
11
+ visitHTMLSelfCloseTagNode(node) {
12
+ this.checkTagName(node);
13
+ this.visitChildNodes(node);
14
+ }
15
+ checkTagName(node) {
16
+ const tagName = node.tag_name?.value;
17
+ if (!tagName)
18
+ return;
19
+ if (tagName !== tagName.toLowerCase()) {
20
+ let type = node.type;
21
+ if (node.type == "AST_HTML_OPEN_TAG_NODE")
22
+ type = "Opening";
23
+ if (node.type == "AST_HTML_CLOSE_TAG_NODE")
24
+ type = "Closing";
25
+ if (node.type == "AST_HTML_SELF_CLOSE_TAG_NODE")
26
+ type = "Self-closing";
27
+ this.addOffense(`${type} tag name \`${tagName}\` should be lowercase. Use \`${tagName.toLowerCase()}\` instead.`, node.tag_name.location, "error");
28
+ }
29
+ }
30
+ }
31
+ export class HTMLTagNameLowercaseRule {
32
+ name = "html-tag-name-lowercase";
33
+ check(node) {
34
+ const visitor = new TagNameLowercaseVisitor(this.name);
35
+ visitor.visit(node);
36
+ return visitor.offenses;
37
+ }
38
+ }
39
+ //# sourceMappingURL=html-tag-name-lowercase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-tag-name-lowercase.js","sourceRoot":"","sources":["../../../src/rules/html-tag-name-lowercase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAKjD,MAAM,uBAAwB,SAAQ,eAAe;IACnD,oBAAoB,CAAC,IAAqB;QACxC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,qBAAqB,CAAC,IAAsB;QAC1C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,yBAAyB,CAAC,IAA0B;QAClD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAEO,YAAY,CAAC,IAA+D;QAClF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAA;QACpC,IAAI,CAAC,OAAO;YAAE,OAAM;QAEpB,IAAI,OAAO,KAAK,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YACtC,IAAI,IAAI,GAAW,IAAI,CAAC,IAAI,CAAA;YAE5B,IAAI,IAAI,CAAC,IAAI,IAAI,wBAAwB;gBAAE,IAAI,GAAG,SAAS,CAAA;YAC3D,IAAI,IAAI,CAAC,IAAI,IAAI,yBAAyB;gBAAE,IAAI,GAAG,SAAS,CAAA;YAC5D,IAAI,IAAI,CAAC,IAAI,IAAI,8BAA8B;gBAAE,IAAI,GAAG,cAAc,CAAA;YAEtE,IAAI,CAAC,UAAU,CACb,GAAG,IAAI,eAAe,OAAO,iCAAiC,OAAO,CAAC,WAAW,EAAE,aAAa,EAChG,IAAI,CAAC,QAAS,CAAC,QAAQ,EACvB,OAAO,CACR,CAAA;QACH,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,wBAAwB;IACnC,IAAI,GAAG,yBAAyB,CAAA;IAEhC,KAAK,CAAC,IAAU;QACd,MAAM,OAAO,GAAG,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnB,OAAO,OAAO,CAAC,QAAQ,CAAA;IACzB,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ export * from "./erb-no-empty-tags.js";
2
+ export * from "./erb-no-output-control-flow.js";
3
+ export * from "./html-anchor-require-href.js";
4
+ export * from "./html-attribute-double-quotes.js";
5
+ export * from "./html-attribute-values-require-quotes.js";
6
+ export * from "./html-boolean-attributes-no-value.js";
7
+ export * from "./html-img-require-alt.js";
8
+ export * from "./html-no-block-inside-inline.js";
9
+ export * from "./html-no-duplicate-attributes.js";
10
+ export * from "./html-no-empty-headings.js";
11
+ export * from "./html-no-nested-links.js";
12
+ export * from "./html-tag-name-lowercase.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA;AACtC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,mCAAmC,CAAA;AACjD,cAAc,2CAA2C,CAAA;AACzD,cAAc,uCAAuC,CAAA;AACrD,cAAc,2BAA2B,CAAA;AACzC,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA"}
@@ -0,0 +1,198 @@
1
+ import { Visitor } from "@herb-tools/core";
2
+ /**
3
+ * Base visitor class that provides common functionality for rule visitors
4
+ */
5
+ export class BaseRuleVisitor extends Visitor {
6
+ offenses = [];
7
+ ruleName;
8
+ constructor(ruleName) {
9
+ super();
10
+ this.ruleName = ruleName;
11
+ }
12
+ /**
13
+ * Helper method to create a lint offense
14
+ */
15
+ createOffense(message, location, severity = "error") {
16
+ return {
17
+ rule: this.ruleName,
18
+ code: this.ruleName,
19
+ source: "Herb Linter",
20
+ message,
21
+ location,
22
+ severity,
23
+ };
24
+ }
25
+ /**
26
+ * Helper method to add an offense to the offenses array
27
+ */
28
+ addOffense(message, location, severity = "error") {
29
+ this.offenses.push(this.createOffense(message, location, severity));
30
+ }
31
+ }
32
+ /**
33
+ * Gets attributes from either an HTMLOpenTagNode or HTMLSelfCloseTagNode
34
+ */
35
+ export function getAttributes(node) {
36
+ return node.type === "AST_HTML_SELF_CLOSE_TAG_NODE"
37
+ ? node.attributes
38
+ : node.children;
39
+ }
40
+ /**
41
+ * Gets the tag name from an HTML tag node (lowercased)
42
+ */
43
+ export function getTagName(node) {
44
+ return node.tag_name?.value.toLowerCase() || null;
45
+ }
46
+ /**
47
+ * Gets the attribute name from an HTMLAttributeNode (lowercased)
48
+ */
49
+ export function getAttributeName(attributeNode) {
50
+ if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") {
51
+ const nameNode = attributeNode.name;
52
+ return nameNode.name?.value.toLowerCase() || null;
53
+ }
54
+ return null;
55
+ }
56
+ /**
57
+ * Gets the attribute value content from an HTMLAttributeValueNode
58
+ */
59
+ export function getAttributeValue(attributeNode) {
60
+ if (attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE") {
61
+ const valueNode = attributeNode.value;
62
+ if (valueNode.children && valueNode.children.length > 0) {
63
+ return valueNode.children
64
+ .filter(child => child.type === "AST_LITERAL_NODE")
65
+ .map(child => child.content)
66
+ .join("");
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ /**
72
+ * Checks if an attribute has a value
73
+ */
74
+ export function hasAttributeValue(attributeNode) {
75
+ return attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE";
76
+ }
77
+ /**
78
+ * Gets the quote type used for an attribute value
79
+ */
80
+ export function getAttributeValueQuoteType(attributeNode) {
81
+ if (attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE") {
82
+ const valueNode = attributeNode.value;
83
+ if (valueNode.quoted && valueNode.open_quote) {
84
+ return valueNode.open_quote.value === '"' ? "double" : "single";
85
+ }
86
+ return "none";
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Finds an attribute by name in a list of attributes
92
+ */
93
+ export function findAttributeByName(attributes, attributeName) {
94
+ for (const child of attributes) {
95
+ if (child.type === "AST_HTML_ATTRIBUTE_NODE") {
96
+ const attributeNode = child;
97
+ const name = getAttributeName(attributeNode);
98
+ if (name === attributeName.toLowerCase()) {
99
+ return attributeNode;
100
+ }
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ /**
106
+ * Checks if a tag has a specific attribute
107
+ */
108
+ export function hasAttribute(node, attributeName) {
109
+ const attributes = getAttributes(node);
110
+ return findAttributeByName(attributes, attributeName) !== null;
111
+ }
112
+ /**
113
+ * Common HTML element categorization
114
+ */
115
+ export const HTML_INLINE_ELEMENTS = new Set([
116
+ "a", "abbr", "acronym", "b", "bdo", "big", "br", "button", "cite", "code",
117
+ "dfn", "em", "i", "img", "input", "kbd", "label", "map", "object", "output",
118
+ "q", "samp", "script", "select", "small", "span", "strong", "sub", "sup",
119
+ "textarea", "time", "tt", "var"
120
+ ]);
121
+ export const HTML_BLOCK_ELEMENTS = new Set([
122
+ "address", "article", "aside", "blockquote", "canvas", "dd", "div", "dl",
123
+ "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2",
124
+ "h3", "h4", "h5", "h6", "header", "hr", "li", "main", "nav", "noscript",
125
+ "ol", "p", "pre", "section", "table", "tfoot", "ul", "video"
126
+ ]);
127
+ export const HTML_BOOLEAN_ATTRIBUTES = new Set([
128
+ "autofocus", "autoplay", "checked", "controls", "defer", "disabled", "hidden",
129
+ "loop", "multiple", "muted", "readonly", "required", "reversed", "selected",
130
+ "open", "default", "formnovalidate", "novalidate", "itemscope", "scoped",
131
+ "seamless", "allowfullscreen", "async", "compact", "declare", "nohref",
132
+ "noresize", "noshade", "nowrap", "sortable", "truespeed", "typemustmatch"
133
+ ]);
134
+ export const HEADING_TAGS = new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
135
+ /**
136
+ * Checks if an element is inline
137
+ */
138
+ export function isInlineElement(tagName) {
139
+ return HTML_INLINE_ELEMENTS.has(tagName.toLowerCase());
140
+ }
141
+ /**
142
+ * Checks if an element is block-level
143
+ */
144
+ export function isBlockElement(tagName) {
145
+ return HTML_BLOCK_ELEMENTS.has(tagName.toLowerCase());
146
+ }
147
+ /**
148
+ * Checks if an attribute is a boolean attribute
149
+ */
150
+ export function isBooleanAttribute(attributeName) {
151
+ return HTML_BOOLEAN_ATTRIBUTES.has(attributeName.toLowerCase());
152
+ }
153
+ /**
154
+ * Abstract base class for rules that need to check individual attributes on HTML tags
155
+ * Eliminates duplication of visitHTMLOpenTagNode/visitHTMLSelfCloseTagNode patterns
156
+ * and attribute iteration logic. Provides simplified interface with extracted attribute info.
157
+ */
158
+ export class AttributeVisitorMixin extends BaseRuleVisitor {
159
+ visitHTMLOpenTagNode(node) {
160
+ this.checkAttributesOnNode(node);
161
+ super.visitHTMLOpenTagNode(node);
162
+ }
163
+ visitHTMLSelfCloseTagNode(node) {
164
+ this.checkAttributesOnNode(node);
165
+ super.visitHTMLSelfCloseTagNode(node);
166
+ }
167
+ checkAttributesOnNode(node) {
168
+ forEachAttribute(node, (attributeNode) => {
169
+ const attributeName = getAttributeName(attributeNode);
170
+ const attributeValue = getAttributeValue(attributeNode);
171
+ if (attributeName) {
172
+ this.checkAttribute(attributeName, attributeValue, attributeNode, node);
173
+ }
174
+ });
175
+ }
176
+ }
177
+ /**
178
+ * Checks if an attribute value is quoted
179
+ */
180
+ export function isAttributeValueQuoted(attributeNode) {
181
+ if (attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE") {
182
+ const valueNode = attributeNode.value;
183
+ return !!valueNode.quoted;
184
+ }
185
+ return false;
186
+ }
187
+ /**
188
+ * Iterates over all attributes of a tag node, calling the callback for each attribute
189
+ */
190
+ export function forEachAttribute(node, callback) {
191
+ const attributes = getAttributes(node);
192
+ for (const child of attributes) {
193
+ if (child.type === "AST_HTML_ATTRIBUTE_NODE") {
194
+ callback(child);
195
+ }
196
+ }
197
+ }
198
+ //# sourceMappingURL=rule-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-utils.js","sourceRoot":"","sources":["../../../src/rules/rule-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"root":["../src/cli.ts","../src/default-rules.ts","../src/herb-lint.ts","../src/index.ts","../src/linter.ts","../src/types.ts","../src/cli/argument-parser.ts","../src/cli/file-processor.ts","../src/cli/index.ts","../src/cli/summary-reporter.ts","../src/cli/formatters/base-formatter.ts","../src/cli/formatters/detailed-formatter.ts","../src/cli/formatters/index.ts","../src/cli/formatters/simple-formatter.ts","../src/rules/erb-no-empty-tags.ts","../src/rules/erb-no-output-control-flow.ts","../src/rules/erb-require-whitespace-inside-tags.ts","../src/rules/html-anchor-require-href.ts","../src/rules/html-aria-role-heading-requires-level.ts","../src/rules/html-attribute-double-quotes.ts","../src/rules/html-attribute-values-require-quotes.ts","../src/rules/html-boolean-attributes-no-value.ts","../src/rules/html-img-require-alt.ts","../src/rules/html-no-block-inside-inline.ts","../src/rules/html-no-duplicate-attributes.ts","../src/rules/html-no-empty-headings.ts","../src/rules/html-no-nested-links.ts","../src/rules/html-tag-name-lowercase.ts","../src/rules/index.ts","../src/rules/rule-utils.ts"],"version":"5.8.3"}
@@ -0,0 +1,14 @@
1
+ import type { ThemeInput } from "@herb-tools/highlighter";
2
+ export interface ParsedArguments {
3
+ pattern: string;
4
+ formatOption: 'simple' | 'detailed';
5
+ showTiming: boolean;
6
+ theme: ThemeInput;
7
+ wrapLines: boolean;
8
+ truncateLines: boolean;
9
+ }
10
+ export declare class ArgumentParser {
11
+ private readonly usage;
12
+ parse(argv: string[]): ParsedArguments;
13
+ private getFilePattern;
14
+ }
@@ -0,0 +1,21 @@
1
+ import type { Diagnostic } from "@herb-tools/core";
2
+ export interface ProcessedFile {
3
+ filename: string;
4
+ diagnostic: Diagnostic;
5
+ content: string;
6
+ }
7
+ export interface ProcessingResult {
8
+ totalErrors: number;
9
+ totalWarnings: number;
10
+ filesWithIssues: number;
11
+ ruleCount: number;
12
+ allDiagnostics: ProcessedFile[];
13
+ ruleViolations: Map<string, {
14
+ count: number;
15
+ files: Set<string>;
16
+ }>;
17
+ }
18
+ export declare class FileProcessor {
19
+ private linter;
20
+ processFiles(files: string[]): Promise<ProcessingResult>;
21
+ }
@@ -0,0 +1,6 @@
1
+ import type { Diagnostic } from "@herb-tools/core";
2
+ import type { ProcessedFile } from "../file-processor.js";
3
+ export declare abstract class BaseFormatter {
4
+ abstract format(allDiagnostics: ProcessedFile[], isSingleFile?: boolean): Promise<void>;
5
+ abstract formatFile(filename: string, diagnostics: Diagnostic[]): void;
6
+ }
@@ -0,0 +1,13 @@
1
+ import { type ThemeInput } from "@herb-tools/highlighter";
2
+ import { BaseFormatter } from "./base-formatter.js";
3
+ import type { Diagnostic } from "@herb-tools/core";
4
+ import type { ProcessedFile } from "../file-processor.js";
5
+ export declare class DetailedFormatter extends BaseFormatter {
6
+ private highlighter;
7
+ private theme;
8
+ private wrapLines;
9
+ private truncateLines;
10
+ constructor(theme?: ThemeInput, wrapLines?: boolean, truncateLines?: boolean);
11
+ format(allDiagnostics: ProcessedFile[], isSingleFile?: boolean): Promise<void>;
12
+ formatFile(_filename: string, _diagnostics: Diagnostic[]): void;
13
+ }
@@ -0,0 +1,3 @@
1
+ export { BaseFormatter } from "./base-formatter.js";
2
+ export { SimpleFormatter } from "./simple-formatter.js";
3
+ export { DetailedFormatter } from "./detailed-formatter.js";
@@ -0,0 +1,7 @@
1
+ import { BaseFormatter } from "./base-formatter.js";
2
+ import type { Diagnostic } from "@herb-tools/core";
3
+ import type { ProcessedFile } from "../file-processor.js";
4
+ export declare class SimpleFormatter extends BaseFormatter {
5
+ format(allDiagnostics: ProcessedFile[]): Promise<void>;
6
+ formatFile(filename: string, diagnostics: Diagnostic[]): void;
7
+ }
@@ -0,0 +1,22 @@
1
+ export interface SummaryData {
2
+ files: string[];
3
+ totalErrors: number;
4
+ totalWarnings: number;
5
+ filesWithViolations: number;
6
+ ruleCount: number;
7
+ startTime: number;
8
+ startDate: Date;
9
+ showTiming: boolean;
10
+ ruleViolations: Map<string, {
11
+ count: number;
12
+ files: Set<string>;
13
+ }>;
14
+ }
15
+ export declare class SummaryReporter {
16
+ private pluralize;
17
+ displaySummary(data: SummaryData): void;
18
+ displayMostViolatedRules(ruleViolations: Map<string, {
19
+ count: number;
20
+ files: Set<string>;
21
+ }>, limit?: number): void;
22
+ }
@@ -0,0 +1,6 @@
1
+ export declare class CLI {
2
+ private argumentParser;
3
+ private fileProcessor;
4
+ private summaryReporter;
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { RuleClass } from "./types.js";
2
+ export declare const defaultRules: RuleClass[];
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from "./linter.js";
2
+ export * from "./rules/index.js";
3
+ export * from "./types.js";
@@ -0,0 +1,18 @@
1
+ import type { RuleClass, LintResult } from "./types.js";
2
+ import type { DocumentNode } from "@herb-tools/core";
3
+ export declare class Linter {
4
+ private rules;
5
+ private offenses;
6
+ /**
7
+ * Creates a new Linter instance.
8
+ * @param rules - Array of rule classes (not instances) to use. If not provided, uses default rules.
9
+ */
10
+ constructor(rules?: RuleClass[]);
11
+ /**
12
+ * Returns the default set of rule classes used by the linter.
13
+ * @returns Array of rule classes
14
+ */
15
+ private getDefaultRules;
16
+ getRuleCount(): number;
17
+ lint(document: DocumentNode): LintResult;
18
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class ERBNoEmptyTagsRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Node } from "@herb-tools/core";
2
+ import type { Rule, LintOffense } from "../types.js";
3
+ export declare class ERBNoOutputControlFlowRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Node } from "@herb-tools/core";
2
+ import type { LintOffense, Rule } from "../types.js";
3
+ export declare class ERBRequireWhitespaceRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLAnchorRequireHrefRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLAriaRoleHeadingRequiresLevelRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLAttributeDoubleQuotesRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLAttributeValuesRequireQuotesRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLBooleanAttributesNoValueRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLImgRequireAltRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLNoBlockInsideInlineRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLNoDuplicateAttributesRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLNoEmptyHeadingsRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLNoNestedLinksRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { Rule, LintOffense } from "../types.js";
2
+ import type { Node } from "@herb-tools/core";
3
+ export declare class HTMLTagNameLowercaseRule implements Rule {
4
+ name: string;
5
+ check(node: Node): LintOffense[];
6
+ }
@@ -0,0 +1,12 @@
1
+ export * from "./erb-no-empty-tags.js";
2
+ export * from "./erb-no-output-control-flow.js";
3
+ export * from "./html-anchor-require-href.js";
4
+ export * from "./html-attribute-double-quotes.js";
5
+ export * from "./html-attribute-values-require-quotes.js";
6
+ export * from "./html-boolean-attributes-no-value.js";
7
+ export * from "./html-img-require-alt.js";
8
+ export * from "./html-no-block-inside-inline.js";
9
+ export * from "./html-no-duplicate-attributes.js";
10
+ export * from "./html-no-empty-headings.js";
11
+ export * from "./html-no-nested-links.js";
12
+ export * from "./html-tag-name-lowercase.js";