@herb-tools/linter 0.5.0 → 0.6.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 (139) hide show
  1. package/dist/herb-lint.js +5131 -1647
  2. package/dist/herb-lint.js.map +1 -1
  3. package/dist/index.cjs +662 -145
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +654 -147
  6. package/dist/index.js.map +1 -1
  7. package/dist/package.json +4 -4
  8. package/dist/src/cli/argument-parser.js +0 -4
  9. package/dist/src/cli/argument-parser.js.map +1 -1
  10. package/dist/src/default-rules.js +20 -0
  11. package/dist/src/default-rules.js.map +1 -1
  12. package/dist/src/linter.js +29 -4
  13. package/dist/src/linter.js.map +1 -1
  14. package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js +26 -0
  15. package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js.map +1 -0
  16. package/dist/src/rules/erb-prefer-image-tag-helper.js +0 -4
  17. package/dist/src/rules/erb-prefer-image-tag-helper.js.map +1 -1
  18. package/dist/src/rules/html-aria-attribute-must-be-valid.js +11 -10
  19. package/dist/src/rules/html-aria-attribute-must-be-valid.js.map +1 -1
  20. package/dist/src/rules/html-aria-label-is-well-formatted.js +33 -0
  21. package/dist/src/rules/html-aria-label-is-well-formatted.js.map +1 -0
  22. package/dist/src/rules/html-aria-level-must-be-valid.js +26 -4
  23. package/dist/src/rules/html-aria-level-must-be-valid.js.map +1 -1
  24. package/dist/src/rules/html-aria-role-heading-requires-level.js +7 -13
  25. package/dist/src/rules/html-aria-role-heading-requires-level.js.map +1 -1
  26. package/dist/src/rules/html-aria-role-must-be-valid.js +3 -3
  27. package/dist/src/rules/html-aria-role-must-be-valid.js.map +1 -1
  28. package/dist/src/rules/html-attribute-double-quotes.js +14 -4
  29. package/dist/src/rules/html-attribute-double-quotes.js.map +1 -1
  30. package/dist/src/rules/html-attribute-equals-spacing.js +24 -0
  31. package/dist/src/rules/html-attribute-equals-spacing.js.map +1 -0
  32. package/dist/src/rules/html-attribute-values-require-quotes.js +19 -8
  33. package/dist/src/rules/html-attribute-values-require-quotes.js.map +1 -1
  34. package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js +47 -0
  35. package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js.map +1 -0
  36. package/dist/src/rules/html-boolean-attributes-no-value.js +9 -2
  37. package/dist/src/rules/html-boolean-attributes-no-value.js.map +1 -1
  38. package/dist/src/rules/html-iframe-has-title.js +39 -0
  39. package/dist/src/rules/html-iframe-has-title.js.map +1 -0
  40. package/dist/src/rules/html-img-require-alt.js +0 -4
  41. package/dist/src/rules/html-img-require-alt.js.map +1 -1
  42. package/dist/src/rules/html-navigation-has-label.js +43 -0
  43. package/dist/src/rules/html-navigation-has-label.js.map +1 -0
  44. package/dist/src/rules/html-no-aria-hidden-on-focusable.js +67 -0
  45. package/dist/src/rules/html-no-aria-hidden-on-focusable.js.map +1 -0
  46. package/dist/src/rules/html-no-duplicate-attributes.js +22 -25
  47. package/dist/src/rules/html-no-duplicate-attributes.js.map +1 -1
  48. package/dist/src/rules/html-no-duplicate-ids.js +2 -2
  49. package/dist/src/rules/html-no-duplicate-ids.js.map +1 -1
  50. package/dist/src/rules/html-no-empty-headings.js +0 -21
  51. package/dist/src/rules/html-no-empty-headings.js.map +1 -1
  52. package/dist/src/rules/html-no-positive-tab-index.js +21 -0
  53. package/dist/src/rules/html-no-positive-tab-index.js.map +1 -0
  54. package/dist/src/rules/html-no-self-closing.js +22 -0
  55. package/dist/src/rules/html-no-self-closing.js.map +1 -0
  56. package/dist/src/rules/html-no-title-attribute.js +27 -0
  57. package/dist/src/rules/html-no-title-attribute.js.map +1 -0
  58. package/dist/src/rules/html-tag-name-lowercase.js +35 -23
  59. package/dist/src/rules/html-tag-name-lowercase.js.map +1 -1
  60. package/dist/src/rules/index.js +10 -0
  61. package/dist/src/rules/index.js.map +1 -1
  62. package/dist/src/rules/rule-utils.js +176 -22
  63. package/dist/src/rules/rule-utils.js.map +1 -1
  64. package/dist/src/rules/svg-tag-name-capitalization.js +0 -8
  65. package/dist/src/rules/svg-tag-name-capitalization.js.map +1 -1
  66. package/dist/src/types.js.map +1 -1
  67. package/dist/tsconfig.tsbuildinfo +1 -1
  68. package/dist/types/cli/index.d.ts +4 -0
  69. package/dist/types/rules/erb-no-silent-tag-in-attribute-name.d.ts +7 -0
  70. package/dist/types/rules/html-aria-label-is-well-formatted.d.ts +7 -0
  71. package/dist/types/rules/html-attribute-equals-spacing.d.ts +7 -0
  72. package/dist/types/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +7 -0
  73. package/dist/types/rules/html-iframe-has-title.d.ts +7 -0
  74. package/dist/types/rules/html-navigation-has-label.d.ts +7 -0
  75. package/dist/types/rules/html-no-aria-hidden-on-focusable.d.ts +7 -0
  76. package/dist/types/rules/html-no-positive-tab-index.d.ts +7 -0
  77. package/dist/types/rules/html-no-self-closing.d.ts +7 -0
  78. package/dist/types/rules/html-no-title-attribute.d.ts +7 -0
  79. package/dist/types/rules/html-tag-name-lowercase.d.ts +2 -1
  80. package/dist/types/rules/index.d.ts +10 -0
  81. package/dist/types/rules/rule-utils.d.ts +107 -13
  82. package/dist/types/src/rules/erb-no-silent-tag-in-attribute-name.d.ts +7 -0
  83. package/dist/types/src/rules/html-aria-label-is-well-formatted.d.ts +7 -0
  84. package/dist/types/src/rules/html-attribute-equals-spacing.d.ts +7 -0
  85. package/dist/types/src/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +7 -0
  86. package/dist/types/src/rules/html-iframe-has-title.d.ts +7 -0
  87. package/dist/types/src/rules/html-navigation-has-label.d.ts +7 -0
  88. package/dist/types/src/rules/html-no-aria-hidden-on-focusable.d.ts +7 -0
  89. package/dist/types/src/rules/html-no-positive-tab-index.d.ts +7 -0
  90. package/dist/types/src/rules/html-no-self-closing.d.ts +7 -0
  91. package/dist/types/src/rules/html-no-title-attribute.d.ts +7 -0
  92. package/dist/types/src/rules/html-tag-name-lowercase.d.ts +2 -1
  93. package/dist/types/src/rules/index.d.ts +10 -0
  94. package/dist/types/src/rules/rule-utils.d.ts +107 -13
  95. package/dist/types/src/types.d.ts +24 -0
  96. package/dist/types/types.d.ts +24 -0
  97. package/docs/rules/README.md +12 -2
  98. package/docs/rules/erb-no-silent-tag-in-attribute-name.md +34 -0
  99. package/docs/rules/html-aria-label-is-well-formatted.md +49 -0
  100. package/docs/rules/html-attribute-equals-spacing.md +35 -0
  101. package/docs/rules/html-avoid-both-disabled-and-aria-disabled.md +48 -0
  102. package/docs/rules/html-iframe-has-title.md +43 -0
  103. package/docs/rules/html-navigation-has-label.md +61 -0
  104. package/docs/rules/html-no-aria-hidden-on-focusable.md +54 -0
  105. package/docs/rules/html-no-positive-tab-index.md +55 -0
  106. package/docs/rules/html-no-self-closing.md +65 -0
  107. package/docs/rules/html-no-title-attribute.md +69 -0
  108. package/docs/rules/html-tag-name-lowercase.md +16 -3
  109. package/package.json +4 -4
  110. package/src/cli/argument-parser.ts +0 -5
  111. package/src/default-rules.ts +20 -0
  112. package/src/linter.ts +30 -4
  113. package/src/rules/erb-no-silent-tag-in-attribute-name.ts +40 -0
  114. package/src/rules/erb-prefer-image-tag-helper.ts +2 -7
  115. package/src/rules/html-aria-attribute-must-be-valid.ts +28 -32
  116. package/src/rules/html-aria-label-is-well-formatted.ts +59 -0
  117. package/src/rules/html-aria-level-must-be-valid.ts +38 -5
  118. package/src/rules/html-aria-role-heading-requires-level.ts +16 -28
  119. package/src/rules/html-aria-role-must-be-valid.ts +5 -5
  120. package/src/rules/html-attribute-double-quotes.ts +21 -6
  121. package/src/rules/html-attribute-equals-spacing.ts +41 -0
  122. package/src/rules/html-attribute-values-require-quotes.ts +29 -9
  123. package/src/rules/html-avoid-both-disabled-and-aria-disabled.ts +66 -0
  124. package/src/rules/html-boolean-attributes-no-value.ts +17 -4
  125. package/src/rules/html-iframe-has-title.ts +62 -0
  126. package/src/rules/html-img-require-alt.ts +2 -7
  127. package/src/rules/html-navigation-has-label.ts +64 -0
  128. package/src/rules/html-no-aria-hidden-on-focusable.ts +90 -0
  129. package/src/rules/html-no-duplicate-attributes.ts +28 -28
  130. package/src/rules/html-no-duplicate-ids.ts +4 -3
  131. package/src/rules/html-no-empty-headings.ts +2 -31
  132. package/src/rules/html-no-positive-tab-index.ts +33 -0
  133. package/src/rules/html-no-self-closing.ts +36 -0
  134. package/src/rules/html-no-title-attribute.ts +42 -0
  135. package/src/rules/html-tag-name-lowercase.ts +42 -29
  136. package/src/rules/index.ts +10 -0
  137. package/src/rules/rule-utils.ts +260 -39
  138. package/src/rules/svg-tag-name-capitalization.ts +2 -9
  139. package/src/types.ts +27 -0
@@ -1,12 +1,13 @@
1
- import { AttributeVisitorMixin } from "./rule-utils"
2
1
  import { ParserRule } from "../types"
3
- import type { ParseResult, Node } from "@herb-tools/core"
2
+ import { AttributeVisitorMixin, StaticAttributeStaticValueParams } from "./rule-utils"
3
+
4
+ import type { ParseResult } from "@herb-tools/core"
4
5
  import type { LintOffense, LintContext } from "../types"
5
6
 
6
7
  class NoDuplicateIdsVisitor extends AttributeVisitorMixin {
7
8
  private documentIds: Set<string> = new Set<string>()
8
9
 
9
- protected checkAttribute(attributeName: string, attributeValue: string | null, attributeNode: Node): void {
10
+ protected checkStaticAttributeStaticValue({ attributeName, attributeValue, attributeNode }: StaticAttributeStaticValueParams): void {
10
11
  if (attributeName.toLowerCase() !== "id") return
11
12
  if (!attributeValue) return
12
13
 
@@ -2,7 +2,7 @@ import { BaseRuleVisitor, getTagName, getAttributes, findAttributeByName, getAtt
2
2
 
3
3
  import { ParserRule } from "../types.js"
4
4
  import type { LintOffense, LintContext } from "../types.js"
5
- import type { HTMLElementNode, HTMLOpenTagNode, HTMLSelfCloseTagNode, ParseResult, LiteralNode, HTMLTextNode } from "@herb-tools/core"
5
+ import type { HTMLElementNode, HTMLOpenTagNode, ParseResult, LiteralNode, HTMLTextNode } from "@herb-tools/core"
6
6
 
7
7
  class NoEmptyHeadingsVisitor extends BaseRuleVisitor {
8
8
  visitHTMLElementNode(node: HTMLElementNode): void {
@@ -10,10 +10,6 @@ class NoEmptyHeadingsVisitor extends BaseRuleVisitor {
10
10
  super.visitHTMLElementNode(node)
11
11
  }
12
12
 
13
- visitHTMLSelfCloseTagNode(node: HTMLSelfCloseTagNode): void {
14
- this.checkSelfClosingHeading(node)
15
- super.visitHTMLSelfCloseTagNode(node)
16
- }
17
13
 
18
14
  private checkHeadingElement(node: HTMLElementNode): void {
19
15
  if (!node.open_tag || node.open_tag.type !== "AST_HTML_OPEN_TAG_NODE") {
@@ -47,31 +43,6 @@ class NoEmptyHeadingsVisitor extends BaseRuleVisitor {
47
43
  }
48
44
  }
49
45
 
50
- private checkSelfClosingHeading(node: HTMLSelfCloseTagNode): void {
51
- const tagName = getTagName(node)
52
- if (!tagName) {
53
- return
54
- }
55
-
56
- // Check if it's a standard heading tag (h1-h6) or has role="heading"
57
- const isStandardHeading = HEADING_TAGS.has(tagName)
58
- const isAriaHeading = this.hasHeadingRole(node)
59
-
60
- if (!isStandardHeading && !isAriaHeading) {
61
- return
62
- }
63
-
64
- // Self-closing headings are always empty
65
- const elementDescription = isStandardHeading
66
- ? `\`<${tagName}>\``
67
- : `\`<${tagName} role="heading">\``
68
-
69
- this.addOffense(
70
- `Heading element ${elementDescription} must not be empty. Provide accessible text content for screen readers and SEO.`,
71
- node.tag_name!.location,
72
- "error"
73
- )
74
- }
75
46
 
76
47
  private isEmptyHeading(node: HTMLElementNode): boolean {
77
48
  if (!node.body || node.body.length === 0) {
@@ -114,7 +85,7 @@ class NoEmptyHeadingsVisitor extends BaseRuleVisitor {
114
85
  return !hasAccessibleContent
115
86
  }
116
87
 
117
- private hasHeadingRole(node: HTMLOpenTagNode | HTMLSelfCloseTagNode): boolean {
88
+ private hasHeadingRole(node: HTMLOpenTagNode): boolean {
118
89
  const attributes = getAttributes(node)
119
90
  const roleAttribute = findAttributeByName(attributes, "role")
120
91
 
@@ -0,0 +1,33 @@
1
+ import { ParserRule } from "../types.js"
2
+ import { AttributeVisitorMixin, StaticAttributeStaticValueParams } from "./rule-utils.js"
3
+
4
+ import type { LintOffense, LintContext } from "../types.js"
5
+ import type { ParseResult } from "@herb-tools/core"
6
+
7
+ class NoPositiveTabIndexVisitor extends AttributeVisitorMixin {
8
+ protected checkStaticAttributeStaticValue({ attributeName, attributeValue, attributeNode }: StaticAttributeStaticValueParams): void {
9
+ if (attributeName !== "tabindex") return
10
+
11
+ const tabIndexValue = parseInt(attributeValue, 10)
12
+
13
+ if (!isNaN(tabIndexValue) && tabIndexValue > 0) {
14
+ this.addOffense(
15
+ `Do not use positive \`tabindex\` values as they are error prone and can severely disrupt navigation experience for keyboard users. Use \`tabindex="0"\` to make an element focusable or \`tabindex=\"-1\"\` to remove it from the tab sequence.`,
16
+ attributeNode.location,
17
+ "error"
18
+ )
19
+ }
20
+ }
21
+ }
22
+
23
+ export class HTMLNoPositiveTabIndexRule extends ParserRule {
24
+ name = "html-no-positive-tab-index"
25
+
26
+ check(result: ParseResult, context?: Partial<LintContext>): LintOffense[] {
27
+ const visitor = new NoPositiveTabIndexVisitor(this.name, context)
28
+
29
+ visitor.visit(result.value)
30
+
31
+ return visitor.offenses
32
+ }
33
+ }
@@ -0,0 +1,36 @@
1
+ import { ParserRule } from "../types.js"
2
+ import { BaseRuleVisitor, getTagName, isVoidElement } from "./rule-utils.js"
3
+
4
+ import type { LintContext, LintOffense } from "../types.js"
5
+ import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core"
6
+
7
+ class NoSelfClosingVisitor extends BaseRuleVisitor {
8
+ visitHTMLOpenTagNode(node: HTMLOpenTagNode): void {
9
+ if (node.tag_closing?.value === "/>") {
10
+ const tagName = getTagName(node)
11
+
12
+ const shouldBeVoid = tagName ? isVoidElement(tagName) : false
13
+ const instead = shouldBeVoid ? `Use \`<${tagName}>\` instead.` : `Use \`<${tagName}></${tagName}>\` instead.`
14
+
15
+ this.addOffense(
16
+ `Self-closing syntax \`<${tagName} />\` is not allowed in HTML. ${instead}`,
17
+ node.location,
18
+ "error"
19
+ )
20
+ }
21
+
22
+ super.visitHTMLOpenTagNode(node)
23
+ }
24
+ }
25
+
26
+ export class HTMLNoSelfClosingRule extends ParserRule {
27
+ name = "html-no-self-closing"
28
+
29
+ check(result: ParseResult, context?: Partial<LintContext>): LintOffense[] {
30
+ const visitor = new NoSelfClosingVisitor(this.name, context)
31
+
32
+ visitor.visit(result.value)
33
+
34
+ return visitor.offenses
35
+ }
36
+ }
@@ -0,0 +1,42 @@
1
+ import { ParserRule } from "../types.js"
2
+ import { BaseRuleVisitor, getTagName, hasAttribute } from "./rule-utils.js"
3
+
4
+ import type { LintOffense, LintContext } from "../types.js"
5
+ import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core"
6
+
7
+ class NoTitleAttributeVisitor extends BaseRuleVisitor {
8
+ ALLOWED_ELEMENTS_WITH_TITLE = new Set(["iframe", "link"])
9
+
10
+ visitHTMLOpenTagNode(node: HTMLOpenTagNode): void {
11
+ this.checkTitleAttribute(node)
12
+ super.visitHTMLOpenTagNode(node)
13
+ }
14
+
15
+ private checkTitleAttribute(node: HTMLOpenTagNode): void {
16
+ const tagName = getTagName(node)
17
+
18
+ if (!tagName || this.ALLOWED_ELEMENTS_WITH_TITLE.has(tagName)) {
19
+ return
20
+ }
21
+
22
+ if (hasAttribute(node, "title")) {
23
+ this.addOffense(
24
+ "The `title` attribute should never be used as it is inaccessible for several groups of users. Use `aria-label` or `aria-describedby` instead. Exceptions are provided for `<iframe>` and `<link>` elements.",
25
+ node.tag_name!.location,
26
+ "error"
27
+ )
28
+ }
29
+ }
30
+ }
31
+
32
+ export class HTMLNoTitleAttributeRule extends ParserRule {
33
+ name = "html-no-title-attribute"
34
+
35
+ check(result: ParseResult, context?: Partial<LintContext>): LintOffense[] {
36
+ const visitor = new NoTitleAttributeVisitor(this.name, context)
37
+
38
+ visitor.visit(result.value)
39
+
40
+ return visitor.offenses
41
+ }
42
+ }
@@ -1,53 +1,56 @@
1
+ import { ParserRule } from "../types.js"
1
2
  import { BaseRuleVisitor } from "./rule-utils.js"
2
3
 
3
- import { ParserRule } from "../types.js"
4
+ import { isNode, getTagName, HTMLElementNode, HTMLOpenTagNode, HTMLCloseTagNode, ParseResult, XMLDeclarationNode, Node } from "@herb-tools/core"
5
+
4
6
  import type { LintOffense, LintContext } from "../types.js"
5
- import type { HTMLElementNode, HTMLOpenTagNode, HTMLCloseTagNode, HTMLSelfCloseTagNode, ParseResult } from "@herb-tools/core"
6
7
 
7
- class TagNameLowercaseVisitor extends BaseRuleVisitor {
8
- visitHTMLElementNode(node: HTMLElementNode): void {
9
- const tagName = node.tag_name?.value
8
+ class XMLDeclarationChecker extends BaseRuleVisitor {
9
+ hasXMLDeclaration: boolean = false
10
10
 
11
- if (node.open_tag) {
12
- this.checkTagName(node.open_tag as HTMLOpenTagNode)
13
- }
11
+ visitXMLDeclarationNode(_node: XMLDeclarationNode): void {
12
+ this.hasXMLDeclaration = true
13
+ }
14
14
 
15
- if (tagName && ["svg"].includes(tagName.toLowerCase())) {
16
- if (node.close_tag) {
17
- this.checkTagName(node.close_tag as HTMLCloseTagNode)
18
- }
15
+ visitChildNodes(node: Node): void {
16
+ if (this.hasXMLDeclaration) return
17
+ super.visitChildNodes(node)
18
+ }
19
+ }
19
20
 
20
- return
21
+ class TagNameLowercaseVisitor extends BaseRuleVisitor {
22
+ visitHTMLElementNode(node: HTMLElementNode): void {
23
+ if (getTagName(node).toLowerCase() === "svg") {
24
+ this.checkTagName(node.open_tag)
25
+ this.checkTagName(node.close_tag)
26
+ } else {
27
+ super.visitHTMLElementNode(node)
21
28
  }
29
+ }
22
30
 
23
- this.visitChildNodes(node)
24
-
25
- if (node.close_tag) {
26
- this.checkTagName(node.close_tag as HTMLCloseTagNode)
27
- }
31
+ visitHTMLOpenTagNode(node: HTMLOpenTagNode) {
32
+ this.checkTagName(node)
28
33
  }
29
34
 
30
- visitHTMLSelfCloseTagNode(node: HTMLSelfCloseTagNode): void {
35
+ visitHTMLCloseTagNode(node: HTMLCloseTagNode) {
31
36
  this.checkTagName(node)
32
- this.visitChildNodes(node)
33
37
  }
34
38
 
35
- private checkTagName(node: HTMLOpenTagNode | HTMLCloseTagNode | HTMLSelfCloseTagNode): void {
36
- const tagName = node.tag_name?.value
39
+ private checkTagName(node: HTMLOpenTagNode | HTMLCloseTagNode | null): void {
40
+ if (!node) return
41
+
42
+ const tagName = getTagName(node)
37
43
 
38
44
  if (!tagName) return
39
45
 
40
46
  const lowercaseTagName = tagName.toLowerCase()
41
47
 
42
- if (tagName !== lowercaseTagName) {
43
- let type: string = node.type
44
-
45
- if (node.type == "AST_HTML_OPEN_TAG_NODE") type = "Opening"
46
- if (node.type == "AST_HTML_CLOSE_TAG_NODE") type = "Closing"
47
- if (node.type == "AST_HTML_SELF_CLOSE_TAG_NODE") type = "Self-closing"
48
+ const type = isNode(node, HTMLOpenTagNode) ? "Opening" : "Closing"
49
+ const open = isNode(node, HTMLOpenTagNode) ? "<" : "</"
48
50
 
51
+ if (tagName !== lowercaseTagName) {
49
52
  this.addOffense(
50
- `${type} tag name \`${tagName}\` should be lowercase. Use \`${lowercaseTagName}\` instead.`,
53
+ `${type} tag name \`${open}${tagName}>\` should be lowercase. Use \`${open}${lowercaseTagName}>\` instead.`,
51
54
  node.tag_name!.location,
52
55
  "error"
53
56
  )
@@ -58,6 +61,16 @@ class TagNameLowercaseVisitor extends BaseRuleVisitor {
58
61
  export class HTMLTagNameLowercaseRule extends ParserRule {
59
62
  name = "html-tag-name-lowercase"
60
63
 
64
+ isEnabled(result: ParseResult, context?: Partial<LintContext>): boolean {
65
+ if (context?.fileName?.endsWith(".xml") || context?.fileName?.endsWith(".xml.erb")) {
66
+ return false
67
+ }
68
+
69
+ const checker = new XMLDeclarationChecker(this.name)
70
+ checker.visit(result.value)
71
+ return !checker.hasXMLDeclaration
72
+ }
73
+
61
74
  check(result: ParseResult, context?: Partial<LintContext>): LintOffense[] {
62
75
  const visitor = new TagNameLowercaseVisitor(this.name, context)
63
76
  visitor.visit(result.value)
@@ -1,19 +1,29 @@
1
1
  export * from "./erb-no-empty-tags.js"
2
2
  export * from "./erb-no-output-control-flow.js"
3
+ export * from "./erb-no-silent-tag-in-attribute-name.js"
3
4
  export * from "./erb-prefer-image-tag-helper.js"
4
5
  export * from "./erb-requires-trailing-newline.js"
5
6
  export * from "./html-anchor-require-href.js"
7
+ export * from "./html-aria-label-is-well-formatted.js"
6
8
  export * from "./html-aria-level-must-be-valid.js"
7
9
  export * from "./html-aria-role-heading-requires-level.js"
8
10
  export * from "./html-aria-role-must-be-valid.js"
9
11
  export * from "./html-attribute-double-quotes.js"
12
+ export * from "./html-attribute-equals-spacing.js"
10
13
  export * from "./html-attribute-values-require-quotes.js"
14
+ export * from "./html-avoid-both-disabled-and-aria-disabled.js"
11
15
  export * from "./html-boolean-attributes-no-value.js"
16
+ export * from "./html-iframe-has-title.js"
12
17
  export * from "./html-img-require-alt.js"
18
+ export * from "./html-navigation-has-label.js"
19
+ export * from "./html-no-aria-hidden-on-focusable.js"
13
20
  export * from "./html-no-block-inside-inline.js"
14
21
  export * from "./html-no-duplicate-attributes.js"
15
22
  export * from "./html-no-duplicate-ids.js"
16
23
  export * from "./html-no-empty-headings.js"
17
24
  export * from "./html-no-nested-links.js"
25
+ export * from "./html-no-positive-tab-index.js"
26
+ export * from "./html-no-self-closing.js"
27
+ export * from "./html-no-title-attribute.js"
18
28
  export * from "./html-tag-name-lowercase.js"
19
29
  export * from "./svg-tag-name-capitalization.js"