@html-eslint/eslint-plugin 0.41.0 → 0.43.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 (195) hide show
  1. package/lib/configs/recommended.js +1 -0
  2. package/lib/data/entities.json +2299 -0
  3. package/lib/languages/html-language.js +3 -3
  4. package/lib/languages/html-source-code.js +9 -11
  5. package/lib/languages/html-traversal-step.js +4 -3
  6. package/lib/rules/attrs-newline.js +3 -4
  7. package/lib/rules/element-newline.js +4 -14
  8. package/lib/rules/id-naming-convention.js +3 -6
  9. package/lib/rules/indent/indent-level.js +1 -1
  10. package/lib/rules/indent/indent.js +18 -23
  11. package/lib/rules/index.js +8 -0
  12. package/lib/rules/lowercase.js +11 -10
  13. package/lib/rules/max-element-depth.js +4 -6
  14. package/lib/rules/no-abstract-roles.js +3 -5
  15. package/lib/rules/no-accesskey-attrs.js +3 -5
  16. package/lib/rules/no-aria-hidden-body.js +2 -2
  17. package/lib/rules/no-aria-hidden-on-focusable.js +118 -0
  18. package/lib/rules/no-duplicate-attrs.js +3 -7
  19. package/lib/rules/no-duplicate-class.js +3 -6
  20. package/lib/rules/no-duplicate-id.js +6 -9
  21. package/lib/rules/no-duplicate-in-head.js +188 -0
  22. package/lib/rules/no-empty-headings.js +121 -0
  23. package/lib/rules/no-extra-spacing-attrs.js +4 -14
  24. package/lib/rules/no-extra-spacing-text.js +4 -10
  25. package/lib/rules/no-heading-inside-button.js +2 -2
  26. package/lib/rules/no-inline-styles.js +2 -2
  27. package/lib/rules/no-invalid-entity.js +107 -0
  28. package/lib/rules/no-invalid-role.js +2 -2
  29. package/lib/rules/no-multiple-empty-lines.js +10 -11
  30. package/lib/rules/no-multiple-h1.js +3 -3
  31. package/lib/rules/no-nested-interactive.js +3 -3
  32. package/lib/rules/no-non-scalable-viewport.js +2 -2
  33. package/lib/rules/no-obsolete-tags.js +2 -2
  34. package/lib/rules/no-positive-tabindex.js +3 -5
  35. package/lib/rules/no-restricted-attr-values.js +4 -6
  36. package/lib/rules/no-restricted-attrs.js +4 -6
  37. package/lib/rules/no-script-style-type.js +3 -5
  38. package/lib/rules/no-skip-heading-levels.js +3 -3
  39. package/lib/rules/no-target-blank.js +2 -2
  40. package/lib/rules/no-trailing-spaces.js +10 -9
  41. package/lib/rules/prefer-https.js +3 -6
  42. package/lib/rules/quotes.js +25 -10
  43. package/lib/rules/require-attrs.js +3 -8
  44. package/lib/rules/require-button-type.js +3 -4
  45. package/lib/rules/require-closing-tags.js +3 -3
  46. package/lib/rules/require-doctype.js +2 -2
  47. package/lib/rules/require-explicit-size.js +2 -5
  48. package/lib/rules/require-form-method.js +2 -2
  49. package/lib/rules/require-frame-title.js +2 -2
  50. package/lib/rules/require-img-alt.js +40 -17
  51. package/lib/rules/require-input-label.js +3 -3
  52. package/lib/rules/require-lang.js +2 -2
  53. package/lib/rules/require-li-container.js +2 -2
  54. package/lib/rules/require-meta-charset.js +3 -4
  55. package/lib/rules/require-meta-description.js +3 -4
  56. package/lib/rules/require-meta-viewport.js +3 -4
  57. package/lib/rules/require-open-graph-protocol.js +3 -6
  58. package/lib/rules/require-title.js +3 -5
  59. package/lib/rules/sort-attrs.js +4 -5
  60. package/lib/rules/use-baseline.js +3 -6
  61. package/lib/rules/utils/baseline.js +7 -4
  62. package/lib/rules/utils/node.js +11 -26
  63. package/lib/rules/utils/settings.js +4 -7
  64. package/lib/rules/utils/source-code.js +2 -2
  65. package/lib/rules/utils/template-literal.js +43 -0
  66. package/lib/rules/utils/visitors.js +6 -7
  67. package/package.json +6 -6
  68. package/types/configs/recommended.d.ts +1 -0
  69. package/types/index.d.ts +2 -0
  70. package/types/index.d.ts.map +1 -1
  71. package/types/languages/html-language.d.ts +0 -5
  72. package/types/languages/html-language.d.ts.map +1 -1
  73. package/types/languages/html-source-code.d.ts +13 -14
  74. package/types/languages/html-source-code.d.ts.map +1 -1
  75. package/types/languages/html-traversal-step.d.ts +5 -5
  76. package/types/languages/html-traversal-step.d.ts.map +1 -1
  77. package/types/rules/attrs-newline.d.ts +3 -4
  78. package/types/rules/attrs-newline.d.ts.map +1 -1
  79. package/types/rules/element-newline.d.ts +5 -13
  80. package/types/rules/element-newline.d.ts.map +1 -1
  81. package/types/rules/id-naming-convention.d.ts +3 -6
  82. package/types/rules/id-naming-convention.d.ts.map +1 -1
  83. package/types/rules/indent/indent-level.d.ts +3 -3
  84. package/types/rules/indent/indent-level.d.ts.map +1 -1
  85. package/types/rules/indent/indent.d.ts +5 -18
  86. package/types/rules/indent/indent.d.ts.map +1 -1
  87. package/types/rules/lowercase.d.ts +2 -8
  88. package/types/rules/lowercase.d.ts.map +1 -1
  89. package/types/rules/max-element-depth.d.ts +3 -6
  90. package/types/rules/max-element-depth.d.ts.map +1 -1
  91. package/types/rules/no-abstract-roles.d.ts +2 -8
  92. package/types/rules/no-abstract-roles.d.ts.map +1 -1
  93. package/types/rules/no-accesskey-attrs.d.ts +2 -8
  94. package/types/rules/no-accesskey-attrs.d.ts.map +1 -1
  95. package/types/rules/no-aria-hidden-body.d.ts +2 -5
  96. package/types/rules/no-aria-hidden-body.d.ts.map +1 -1
  97. package/types/rules/no-aria-hidden-on-focusable.d.ts +4 -0
  98. package/types/rules/no-aria-hidden-on-focusable.d.ts.map +1 -0
  99. package/types/rules/no-duplicate-attrs.d.ts +2 -10
  100. package/types/rules/no-duplicate-attrs.d.ts.map +1 -1
  101. package/types/rules/no-duplicate-class.d.ts +3 -7
  102. package/types/rules/no-duplicate-class.d.ts.map +1 -1
  103. package/types/rules/no-duplicate-id.d.ts +2 -9
  104. package/types/rules/no-duplicate-id.d.ts.map +1 -1
  105. package/types/rules/no-duplicate-in-head.d.ts +4 -0
  106. package/types/rules/no-duplicate-in-head.d.ts.map +1 -0
  107. package/types/rules/no-empty-headings.d.ts +4 -0
  108. package/types/rules/no-empty-headings.d.ts.map +1 -0
  109. package/types/rules/no-extra-spacing-attrs.d.ts +3 -14
  110. package/types/rules/no-extra-spacing-attrs.d.ts.map +1 -1
  111. package/types/rules/no-extra-spacing-text.d.ts +3 -9
  112. package/types/rules/no-extra-spacing-text.d.ts.map +1 -1
  113. package/types/rules/no-heading-inside-button.d.ts +2 -5
  114. package/types/rules/no-heading-inside-button.d.ts.map +1 -1
  115. package/types/rules/no-inline-styles.d.ts +2 -5
  116. package/types/rules/no-inline-styles.d.ts.map +1 -1
  117. package/types/rules/no-invalid-entity.d.ts +11 -0
  118. package/types/rules/no-invalid-entity.d.ts.map +1 -0
  119. package/types/rules/no-invalid-role.d.ts +2 -5
  120. package/types/rules/no-invalid-role.d.ts.map +1 -1
  121. package/types/rules/no-multiple-empty-lines.d.ts +3 -7
  122. package/types/rules/no-multiple-empty-lines.d.ts.map +1 -1
  123. package/types/rules/no-multiple-h1.d.ts +2 -6
  124. package/types/rules/no-multiple-h1.d.ts.map +1 -1
  125. package/types/rules/no-nested-interactive.d.ts +2 -6
  126. package/types/rules/no-nested-interactive.d.ts.map +1 -1
  127. package/types/rules/no-non-scalable-viewport.d.ts +2 -5
  128. package/types/rules/no-non-scalable-viewport.d.ts.map +1 -1
  129. package/types/rules/no-obsolete-tags.d.ts +2 -5
  130. package/types/rules/no-obsolete-tags.d.ts.map +1 -1
  131. package/types/rules/no-positive-tabindex.d.ts +2 -8
  132. package/types/rules/no-positive-tabindex.d.ts.map +1 -1
  133. package/types/rules/no-restricted-attr-values.d.ts +3 -7
  134. package/types/rules/no-restricted-attr-values.d.ts.map +1 -1
  135. package/types/rules/no-restricted-attrs.d.ts +3 -7
  136. package/types/rules/no-restricted-attrs.d.ts.map +1 -1
  137. package/types/rules/no-script-style-type.d.ts +2 -8
  138. package/types/rules/no-script-style-type.d.ts.map +1 -1
  139. package/types/rules/no-skip-heading-levels.d.ts +2 -6
  140. package/types/rules/no-skip-heading-levels.d.ts.map +1 -1
  141. package/types/rules/no-target-blank.d.ts +2 -5
  142. package/types/rules/no-target-blank.d.ts.map +1 -1
  143. package/types/rules/no-trailing-spaces.d.ts +2 -7
  144. package/types/rules/no-trailing-spaces.d.ts.map +1 -1
  145. package/types/rules/prefer-https.d.ts +2 -9
  146. package/types/rules/prefer-https.d.ts.map +1 -1
  147. package/types/rules/quotes.d.ts +7 -9
  148. package/types/rules/quotes.d.ts.map +1 -1
  149. package/types/rules/require-attrs.d.ts +3 -8
  150. package/types/rules/require-attrs.d.ts.map +1 -1
  151. package/types/rules/require-button-type.d.ts +2 -7
  152. package/types/rules/require-button-type.d.ts.map +1 -1
  153. package/types/rules/require-closing-tags.d.ts +3 -4
  154. package/types/rules/require-closing-tags.d.ts.map +1 -1
  155. package/types/rules/require-doctype.d.ts +2 -5
  156. package/types/rules/require-doctype.d.ts.map +1 -1
  157. package/types/rules/require-explicit-size.d.ts +3 -5
  158. package/types/rules/require-explicit-size.d.ts.map +1 -1
  159. package/types/rules/require-form-method.d.ts +2 -5
  160. package/types/rules/require-form-method.d.ts.map +1 -1
  161. package/types/rules/require-frame-title.d.ts +2 -5
  162. package/types/rules/require-frame-title.d.ts.map +1 -1
  163. package/types/rules/require-img-alt.d.ts +3 -4
  164. package/types/rules/require-img-alt.d.ts.map +1 -1
  165. package/types/rules/require-input-label.d.ts +2 -6
  166. package/types/rules/require-input-label.d.ts.map +1 -1
  167. package/types/rules/require-lang.d.ts +2 -5
  168. package/types/rules/require-lang.d.ts.map +1 -1
  169. package/types/rules/require-li-container.d.ts +2 -5
  170. package/types/rules/require-li-container.d.ts.map +1 -1
  171. package/types/rules/require-meta-charset.d.ts +2 -7
  172. package/types/rules/require-meta-charset.d.ts.map +1 -1
  173. package/types/rules/require-meta-description.d.ts +2 -7
  174. package/types/rules/require-meta-description.d.ts.map +1 -1
  175. package/types/rules/require-meta-viewport.d.ts +2 -7
  176. package/types/rules/require-meta-viewport.d.ts.map +1 -1
  177. package/types/rules/require-open-graph-protocol.d.ts +3 -5
  178. package/types/rules/require-open-graph-protocol.d.ts.map +1 -1
  179. package/types/rules/require-title.d.ts +2 -8
  180. package/types/rules/require-title.d.ts.map +1 -1
  181. package/types/rules/sort-attrs.d.ts +3 -6
  182. package/types/rules/sort-attrs.d.ts.map +1 -1
  183. package/types/rules/use-baseline.d.ts +3 -7
  184. package/types/rules/use-baseline.d.ts.map +1 -1
  185. package/types/rules/utils/baseline.d.ts.map +1 -1
  186. package/types/rules/utils/node.d.ts +25 -29
  187. package/types/rules/utils/node.d.ts.map +1 -1
  188. package/types/rules/utils/settings.d.ts +7 -9
  189. package/types/rules/utils/settings.d.ts.map +1 -1
  190. package/types/rules/utils/source-code.d.ts +4 -4
  191. package/types/rules/utils/source-code.d.ts.map +1 -1
  192. package/types/rules/utils/template-literal.d.ts +18 -0
  193. package/types/rules/utils/template-literal.d.ts.map +1 -0
  194. package/types/rules/utils/visitors.d.ts +4 -4
  195. package/types/rules/utils/visitors.d.ts.map +1 -1
@@ -1,12 +1,12 @@
1
- const { visitorKeys, parseForESLint } = require("@html-eslint/parser");
2
- const { HTMLSourceCode } = require("./html-source-code");
3
-
4
1
  /**
5
2
  * @import { Language, ParseResult, File, FileError, OkParseResult } from "@eslint/core";
6
3
  * @import { ParserOptions } from "@html-eslint/parser";
7
4
  * @import { AST } from "eslint";
8
5
  */
9
6
 
7
+ const { visitorKeys, parseForESLint } = require("@html-eslint/parser");
8
+ const { HTMLSourceCode } = require("./html-source-code");
9
+
10
10
  /**
11
11
  * @implements {Language<{ LangOptions: ParserOptions; Code: HTMLSourceCode; RootNode: AST.Program; Node: {}}>}
12
12
  */
@@ -1,13 +1,11 @@
1
1
  /**
2
- * @typedef {import("eslint").AST.Program} Program
3
- * @typedef {import("@eslint/plugin-kit").SourceLocation} SourceLocation
4
- * @typedef {import("@eslint/plugin-kit").DirectiveType} DirectiveType
5
- * @typedef {import("@eslint/core").TraversalStep} TraversalStep
6
- * @typedef {import("@html-eslint/types").CommentContent} CommentContent
7
- * @typedef {import("@html-eslint/types").AnyHTMLNode} AnyHTMLNode
8
- * @typedef {import("@eslint/core").Position} Position
9
- * @typedef {import("../types").BaseNode} BaseNode
2
+ * @import {AST} from 'eslint';
3
+ * @import {SourceLocation, DirectiveType} from '@eslint/plugin-kit';
4
+ * @import {TraversalStep, Position} from '@eslint/core';
5
+ * @import {CommentContent, AnyHTMLNode} from '@html-eslint/types';
6
+ * @import {BaseNode} from '../types';
10
7
  */
8
+
11
9
  const {
12
10
  TextSourceCodeBase,
13
11
  ConfigCommentParser,
@@ -29,7 +27,7 @@ const commentParser = new ConfigCommentParser();
29
27
 
30
28
  class HTMLSourceCode extends TextSourceCodeBase {
31
29
  /**
32
- * @param {{ast: Program, text: string, comments: CommentContent[]}} config
30
+ * @param {{ast: AST.Program, text: string, comments: CommentContent[]}} config
33
31
  */
34
32
  constructor({ ast, text, comments }) {
35
33
  super({ ast, text });
@@ -219,8 +217,8 @@ class HTMLSourceCode extends TextSourceCodeBase {
219
217
 
220
218
  /**
221
219
  *
222
- * @param {AnyHTMLNode | Program} node
223
- * @param {AnyHTMLNode | Program | null} parent
220
+ * @param {AnyHTMLNode | AST.Program} node
221
+ * @param {AnyHTMLNode | AST.Program | null} parent
224
222
  */
225
223
  const visit = (node, parent) => {
226
224
  this.parentsMap.set(node, parent);
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @typedef {import("eslint").AST.Program} Program
3
- * @typedef {import('@html-eslint/types').AnyHTMLNode} AnyHTMLNode
2
+ * @import {AST} from 'eslint';
3
+ * @import {AnyHTMLNode} from '@html-eslint/types';
4
4
  */
5
+
5
6
  const { VisitNodeStep } = require("@eslint/plugin-kit");
6
7
 
7
8
  const STEP_PHASE = {
@@ -18,7 +19,7 @@ const STEP_PHASE = {
18
19
  class HTMLTraversalStep extends VisitNodeStep {
19
20
  /**
20
21
  * @param {Object} options
21
- * @param {AnyHTMLNode | Program} options.target
22
+ * @param {AnyHTMLNode | AST.Program} options.target
22
23
  * @param {1|2} options.phase
23
24
  * @param {Array<any>} options.args
24
25
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef { import("../types").RuleFixer } RuleFixer
2
+ * @import { RuleFixer, RuleModule } from '../types';
3
3
  *
4
4
  * @typedef {Object} MessageId
5
5
  * @property {"closeStyleWrong"} CLOSE_STYLE_WRONG
@@ -8,8 +8,6 @@
8
8
  * @typedef {Object} Option
9
9
  * @property {"sameline" | "newline"} [option.closeStyle]
10
10
  * @property {number} [options.ifAttrsMoreThan]
11
- *
12
- * @typedef { import("../types").RuleModule<[Option]> } RuleModule
13
11
  */
14
12
 
15
13
  const { RULE_CATEGORY } = require("../constants");
@@ -25,7 +23,7 @@ const MESSAGE_ID = {
25
23
  };
26
24
 
27
25
  /**
28
- * @type {RuleModule}
26
+ * @type {RuleModule<[Option]>}
29
27
  */
30
28
  module.exports = {
31
29
  meta: {
@@ -50,6 +48,7 @@ module.exports = {
50
48
  type: "integer",
51
49
  },
52
50
  },
51
+ additionalProperties: false,
53
52
  },
54
53
  ],
55
54
  messages: {
@@ -1,21 +1,10 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").Comment } Comment
4
- * @typedef { import("@html-eslint/types").Doctype } Doctype
5
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
6
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
7
- * @typedef { import("@html-eslint/types").Text } Text
8
- * @typedef { import("@html-eslint/types").AnyNode } AnyNode
9
- * @typedef { import("@html-eslint/types").OpenTagEnd } OpenTagEnd
10
- * @typedef { import("@html-eslint/types").CloseTag } CloseTag
11
- * @typedef { import("../types").Line } Line
2
+ * @import { Text, AnyNode, OpenTagEnd, CloseTag } from '@html-eslint/types';
3
+ * @import { Line, RuleModule } from '../types';
12
4
  * @typedef { AnyNode | Line } AnyNodeOrLine
13
- *
14
5
  * @typedef {Object} Option
15
6
  * @property {string[]} [Option.skip]
16
7
  * @property {string[]} [Option.inline]
17
- *
18
- * @typedef { import("../types").RuleModule<[Option]> } RuleModule
19
8
  */
20
9
 
21
10
  const { RULE_CATEGORY } = require("../constants");
@@ -76,7 +65,7 @@ wbr
76
65
  };
77
66
 
78
67
  /**
79
- * @type {RuleModule}
68
+ * @type {RuleModule<[Option]>}
80
69
  */
81
70
  module.exports = {
82
71
  meta: {
@@ -108,6 +97,7 @@ module.exports = {
108
97
  },
109
98
  },
110
99
  },
100
+ additionalProperties: false,
111
101
  },
112
102
  ],
113
103
  messages: {
@@ -1,14 +1,11 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
4
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
2
+ * @import {Tag, ScriptTag, StyleTag} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
5
4
  *
6
5
  * @typedef {"camelCase" | "snake_case" | "PascalCase" | "kebab-case" | "regex"} Option1
7
6
  * @typedef {Object} Option2
8
7
  * @property {string} pattern
9
8
  * @property {string} [flags]
10
- *
11
- * @typedef { import("../types").RuleModule<[Option1, Option2]> } RuleModule
12
9
  */
13
10
 
14
11
  const { RULE_CATEGORY } = require("../constants");
@@ -42,7 +39,7 @@ const CONVENTION_CHECKERS = {
42
39
  };
43
40
 
44
41
  /**
45
- * @type {RuleModule}
42
+ * @type {RuleModule<[Option1, Option2]>}
46
43
  */
47
44
  module.exports = {
48
45
  meta: {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import("@html-eslint/types").AnyNode} AnyNode
2
+ * @import {AnyNode} from "@html-eslint/types";
3
3
  * @typedef {{ [key in AnyNode['type']]?: number}} IncLevelOptions
4
4
  * @typedef {(node: AnyNode) => number} GetIncreasingLevel
5
5
  */
@@ -1,19 +1,7 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").AnyNode } AnyNode
3
- * @typedef { import("../../types").Line } Line
4
- * @typedef { import("@html-eslint/types").Tag } Tag
5
- * @typedef { import("../../types").RuleListener } RuleListener
6
- * @typedef { import("../../types").Context<any[]> } Context
7
- * @typedef { import("@html-eslint/types").TemplateText } TemplateText
8
- * @typedef { import("eslint").AST.Token } Token
9
- * @typedef { import("eslint").SourceCode } SourceCode
10
- * @typedef { import("eslint").AST.Range } Range
11
- * @typedef { import("eslint").AST.SourceLocation } SourceLocation
12
- * @typedef { import("@html-eslint/types").TemplateLiteral } TemplateLiteral
13
- * @typedef { import("@html-eslint/types").OpenTemplate } OpenTemplate
14
- * @typedef { import("@html-eslint/types").CloseTemplate } CloseTemplate
15
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
16
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
2
+ * @import {AnyNode, Tag, TemplateText, TemplateLiteral, OpenTemplate, CloseTemplate, ScriptTag, StyleTag} from "@html-eslint/types";
3
+ * @import {Line, RuleListener, Context, RuleModule} from "../../types";
4
+ * @import {AST} from "eslint";
17
5
  *
18
6
  * @typedef {AnyNode | Line} AnyNodeOrLine
19
7
  * @typedef {Object} IndentType
@@ -30,11 +18,9 @@
30
18
  * @typedef {Object} Option2
31
19
  * @property {number} [Option2.Attribute]
32
20
  * @property {Record<string, number>} [Option2.tagChildrenIndent]
33
- *
34
- * @typedef { import("../../types").RuleModule<[Option1, Option2]> } RuleModule
35
21
  */
36
22
 
37
- const { parse } = require("@html-eslint/template-parser");
23
+ const { parseTemplateLiteral } = require("../utils/template-literal");
38
24
  const { NODE_TYPES } = require("@html-eslint/parser");
39
25
  const { RULE_CATEGORY } = require("../../constants");
40
26
  const {
@@ -66,7 +52,7 @@ const INDENT_TYPES = {
66
52
  const IGNORING_NODES = ["pre", "xmp"];
67
53
 
68
54
  /**
69
- * @type {RuleModule}
55
+ * @type {RuleModule<[Option1, Option2]>}
70
56
  */
71
57
  module.exports = {
72
58
  meta: {
@@ -111,6 +97,7 @@ module.exports = {
111
97
  additionalProperties: false,
112
98
  },
113
99
  },
100
+ additionalProperties: false,
114
101
  },
115
102
  ],
116
103
  messages: {
@@ -399,13 +386,21 @@ module.exports = {
399
386
  TaggedTemplateExpression(node) {
400
387
  if (shouldCheckTaggedTemplateExpression(node, context)) {
401
388
  const base = getTemplateLiteralBaseIndentLevel(node.quasi);
402
- parse(node.quasi, getSourceCode(context), createIndentVisitor(base));
389
+ parseTemplateLiteral(
390
+ node.quasi,
391
+ getSourceCode(context),
392
+ createIndentVisitor(base)
393
+ );
403
394
  }
404
395
  },
405
396
  TemplateLiteral(node) {
406
397
  if (shouldCheckTemplateLiteral(node, context)) {
407
398
  const base = getTemplateLiteralBaseIndentLevel(node);
408
- parse(node, getSourceCode(context), createIndentVisitor(base));
399
+ parseTemplateLiteral(
400
+ node,
401
+ getSourceCode(context),
402
+ createIndentVisitor(base)
403
+ );
409
404
  }
410
405
  },
411
406
  };
@@ -415,7 +410,7 @@ module.exports = {
415
410
  /**
416
411
  * @param {AnyNodeOrLine | TemplateText | OpenTemplate | CloseTemplate} node
417
412
  * @param {string} actualIndent
418
- * @return {{range: Range; loc: SourceLocation}}
413
+ * @return {{range: AST.Range; loc: AST.SourceLocation}}
419
414
  */
420
415
  function getIndentNodeToReport(node, actualIndent) {
421
416
  let rangeStart = node.range[0];
@@ -448,7 +443,7 @@ function countLeftPadding(str) {
448
443
  }
449
444
 
450
445
  /**
451
- * @param {Context} context
446
+ * @param {Context<any[]>} context
452
447
  * @return {IndentOptionInfo}
453
448
  */
454
449
  function getIndentOptionInfo(context) {
@@ -27,6 +27,7 @@ const noDuplicateAttrs = require("./no-duplicate-attrs");
27
27
  const noAbstractRoles = require("./no-abstract-roles");
28
28
  const requireButtonType = require("./require-button-type");
29
29
  const noAriaHiddenBody = require("./no-aria-hidden-body");
30
+ const noAriaHiddenOnFocusable = require("./no-aria-hidden-on-focusable");
30
31
  const noMultipleEmptyLines = require("./no-multiple-empty-lines");
31
32
  const noAccesskeyAttrs = require("./no-accesskey-attrs");
32
33
  const noRestrictedAttrs = require("./no-restricted-attrs");
@@ -47,6 +48,9 @@ const maxElementDepth = require("./max-element-depth");
47
48
  const requireExplicitSize = require("./require-explicit-size");
48
49
  const useBaseLine = require("./use-baseline");
49
50
  const noDuplicateClass = require("./no-duplicate-class");
51
+ const noEmptyHeadings = require("./no-empty-headings");
52
+ const noInvalidEntity = require("./no-invalid-entity");
53
+ const noDuplicateInHead = require("./no-duplicate-in-head");
50
54
  // import new rule here ↑
51
55
  // DO NOT REMOVE THIS COMMENT
52
56
 
@@ -81,6 +85,7 @@ const rules = {
81
85
  "no-abstract-roles": noAbstractRoles,
82
86
  "require-button-type": requireButtonType,
83
87
  "no-aria-hidden-body": noAriaHiddenBody,
88
+ "no-aria-hidden-on-focusable": noAriaHiddenOnFocusable,
84
89
  "no-multiple-empty-lines": noMultipleEmptyLines,
85
90
  "no-accesskey-attrs": noAccesskeyAttrs,
86
91
  "no-restricted-attrs": noRestrictedAttrs,
@@ -100,6 +105,9 @@ const rules = {
100
105
  "require-explicit-size": requireExplicitSize,
101
106
  "use-baseline": useBaseLine,
102
107
  "no-duplicate-class": noDuplicateClass,
108
+ "no-empty-headings": noEmptyHeadings,
109
+ "no-invalid-entity": noInvalidEntity,
110
+ "no-duplicate-in-head": noDuplicateInHead,
103
111
  // export new rule here ↑
104
112
  // DO NOT REMOVE THIS COMMENT
105
113
  };
@@ -1,15 +1,12 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
- * @typedef { import("../types").RuleModule<[]> } RuleModule
2
+ * @import {Tag, StyleTag, ScriptTag} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
6
4
  */
7
5
 
8
- const { NODE_TYPES } = require("@html-eslint/parser");
9
6
  const { RULE_CATEGORY } = require("../constants");
10
7
  const SVG_CAMEL_CASE_ATTRIBUTES = require("../constants/svg-camel-case-attributes");
11
8
  const { createVisitors } = require("./utils/visitors");
12
- const { hasTemplate } = require("./utils/node");
9
+ const { hasTemplate, isScript, isStyle } = require("./utils/node");
13
10
  const { getRuleUrl } = require("./utils/rule");
14
11
 
15
12
  const MESSAGE_IDS = {
@@ -17,7 +14,7 @@ const MESSAGE_IDS = {
17
14
  };
18
15
 
19
16
  /**
20
- * @type {RuleModule}
17
+ * @type {RuleModule<[]>}
21
18
  */
22
19
  module.exports = {
23
20
  meta: {
@@ -67,8 +64,8 @@ module.exports = {
67
64
  * @param {Tag | StyleTag | ScriptTag} node
68
65
  */
69
66
  function nameOf(node) {
70
- if (node.type === NODE_TYPES.ScriptTag) return "script";
71
- if (node.type === NODE_TYPES.StyleTag) return "style";
67
+ if (isScript(node)) return "script";
68
+ if (isStyle(node)) return "style";
72
69
  return node.name;
73
70
  }
74
71
 
@@ -77,7 +74,11 @@ module.exports = {
77
74
  */
78
75
  function check(node) {
79
76
  const raw = node.openStart.value.slice(1);
80
- if (nameOf(node) !== raw) {
77
+ const name = nameOf(node);
78
+ if (
79
+ name !== raw &&
80
+ (svgStack.length === 0 || name.toLowerCase() === "svg")
81
+ ) {
81
82
  context.report({
82
83
  node: node.openStart,
83
84
  messageId: MESSAGE_IDS.UNEXPECTED,
@@ -1,11 +1,9 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
2
+ * @import {Tag, StyleTag, ScriptTag} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
5
4
  *
6
5
  * @typedef {Object} Option
7
6
  * @property {number} [Option.max]
8
- * @typedef { import("../types").RuleModule<[Option]> } RuleModule
9
7
  */
10
8
 
11
9
  const { RULE_CATEGORY } = require("../constants");
@@ -17,7 +15,7 @@ const MESSAGE_IDS = {
17
15
  };
18
16
 
19
17
  /**
20
- * @type {RuleModule}
18
+ * @type {RuleModule<[Option]>}
21
19
  */
22
20
  module.exports = {
23
21
  meta: {
@@ -25,7 +23,7 @@ module.exports = {
25
23
 
26
24
  docs: {
27
25
  description: "Enforce element maximum depth",
28
- category: RULE_CATEGORY.STYLE,
26
+ category: RULE_CATEGORY.BEST_PRACTICE,
29
27
  recommended: false,
30
28
  url: getRuleUrl("max-element-depth"),
31
29
  },
@@ -1,8 +1,6 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
- * @typedef { import("../types").RuleModule<[]> } RuleModule
2
+ * @import {Tag, StyleTag, ScriptTag} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
6
4
  */
7
5
 
8
6
  const { RULE_CATEGORY } = require("../constants");
@@ -30,7 +28,7 @@ const ABSTRACT_ROLE_SET = new Set([
30
28
  ]);
31
29
 
32
30
  /**
33
- * @type {RuleModule}
31
+ * @type {RuleModule<[]>}
34
32
  */
35
33
  module.exports = {
36
34
  meta: {
@@ -1,8 +1,6 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
- * @typedef { import("../types").RuleModule<[]> } RuleModule
2
+ * @import {Tag, StyleTag, ScriptTag} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
6
4
  */
7
5
 
8
6
  const { RULE_CATEGORY } = require("../constants");
@@ -15,7 +13,7 @@ const MESSAGE_IDS = {
15
13
  };
16
14
 
17
15
  /**
18
- * @type {RuleModule}
16
+ * @type {RuleModule<[]>}
19
17
  */
20
18
  module.exports = {
21
19
  meta: {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule<[]> } RuleModule
2
+ * @import {RuleModule} from "../types";
3
3
  */
4
4
 
5
5
  const { RULE_CATEGORY } = require("../constants");
@@ -12,7 +12,7 @@ const MESSAGE_IDS = {
12
12
  };
13
13
 
14
14
  /**
15
- * @type {RuleModule}
15
+ * @type {RuleModule<[]>}
16
16
  */
17
17
  module.exports = {
18
18
  meta: {
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @import {Tag} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
4
+ */
5
+
6
+ const { RULE_CATEGORY } = require("../constants");
7
+ const { findAttr } = require("./utils/node");
8
+ const { createVisitors } = require("./utils/visitors");
9
+ const { getRuleUrl } = require("./utils/rule");
10
+
11
+ const MESSAGE_IDS = {
12
+ UNEXPECTED: "unexpected",
13
+ };
14
+
15
+ // List of elements that are inherently focusable
16
+ const FOCUSABLE_ELEMENTS = new Set([
17
+ "a", // if href is present
18
+ "button",
19
+ "input",
20
+ "select",
21
+ "textarea",
22
+ "video", // if controls is present
23
+ "audio", // if controls is present
24
+ "details",
25
+ "embed",
26
+ "iframe",
27
+ "summary",
28
+ ]);
29
+
30
+ /**
31
+ * @type {RuleModule<[]>}
32
+ */
33
+ module.exports = {
34
+ meta: {
35
+ type: "code",
36
+
37
+ docs: {
38
+ description: 'Disallow aria-hidden="true" on focusable elements',
39
+ category: RULE_CATEGORY.ACCESSIBILITY,
40
+ recommended: false,
41
+ url: getRuleUrl("no-aria-hidden-on-focusable"),
42
+ },
43
+
44
+ fixable: null,
45
+ schema: [],
46
+ messages: {
47
+ [MESSAGE_IDS.UNEXPECTED]:
48
+ 'Unexpected aria-hidden="true" on focusable element.',
49
+ },
50
+ },
51
+
52
+ create(context) {
53
+ /**
54
+ * Checks if an element is focusable
55
+ * @param {Tag} node
56
+ * @returns {boolean}
57
+ */
58
+ function isFocusable(node) {
59
+ const tagName = node.name.toLowerCase();
60
+
61
+ const contentEditableAttr = findAttr(node, "contenteditable");
62
+ if (contentEditableAttr) {
63
+ const value = contentEditableAttr.value
64
+ ? contentEditableAttr.value.value.toLowerCase()
65
+ : "";
66
+ if (value === "" || value === "true" || value === "plaintext-only") {
67
+ return true;
68
+ }
69
+ }
70
+
71
+ // Check for tabindex attribute
72
+ const tabIndexAttr = findAttr(node, "tabindex");
73
+ if (tabIndexAttr && tabIndexAttr.value) {
74
+ const tabIndexValue = tabIndexAttr.value.value;
75
+ // If tabindex is -1, the element is not focusable
76
+ if (tabIndexValue === "-1") {
77
+ return false;
78
+ }
79
+ // If tabindex is present and not -1, the element is focusable
80
+ return true;
81
+ }
82
+
83
+ // Special cases for elements that are only focusable with certain attributes
84
+ if (tagName === "a") {
85
+ return !!findAttr(node, "href");
86
+ }
87
+
88
+ if (tagName === "audio" || tagName === "video") {
89
+ return !!findAttr(node, "controls");
90
+ }
91
+
92
+ // Check if element is inherently focusable
93
+ return FOCUSABLE_ELEMENTS.has(tagName);
94
+ }
95
+
96
+ return createVisitors(context, {
97
+ Tag(node) {
98
+ const ariaHiddenAttr = findAttr(node, "aria-hidden");
99
+ if (!ariaHiddenAttr || !ariaHiddenAttr.value) {
100
+ return;
101
+ }
102
+
103
+ // Only check for aria-hidden="true"
104
+ if (ariaHiddenAttr.value.value !== "true") {
105
+ return;
106
+ }
107
+
108
+ // Check if the element is focusable
109
+ if (isFocusable(node)) {
110
+ context.report({
111
+ node: ariaHiddenAttr,
112
+ messageId: MESSAGE_IDS.UNEXPECTED,
113
+ });
114
+ }
115
+ },
116
+ });
117
+ },
118
+ };
@@ -1,10 +1,6 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
- * @typedef { import("../types").RuleModule<[]> } RuleModule
6
- * @typedef { import("@html-eslint/types").Attribute } Attribute
7
- * @typedef { import("../types").SuggestionReportDescriptor } SuggestionReportDescriptor
2
+ * @import {Tag, StyleTag, ScriptTag, Attribute} from "@html-eslint/types";
3
+ * @import {RuleModule, SuggestionReportDescriptor} from "../types";
8
4
  */
9
5
 
10
6
  const { RULE_CATEGORY } = require("../constants");
@@ -17,7 +13,7 @@ const MESSAGE_IDS = {
17
13
  };
18
14
 
19
15
  /**
20
- * @type {RuleModule}
16
+ * @type {RuleModule<[]>}
21
17
  */
22
18
  module.exports = {
23
19
  meta: {
@@ -1,9 +1,6 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
- * @typedef { import("@html-eslint/types").AttributeValue } AttributeValue
6
- * @typedef { import("../types").RuleModule<[]> } RuleModule
2
+ * @import {AttributeValue} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
7
4
  * @typedef {Object} ClassInfo
8
5
  * @property {string} name
9
6
  * @property {import("@html-eslint/types").AnyNode['loc']} loc
@@ -20,7 +17,7 @@ const MESSAGE_IDS = {
20
17
  };
21
18
 
22
19
  /**
23
- * @type {RuleModule}
20
+ * @type {RuleModule<[]>}
24
21
  */
25
22
  module.exports = {
26
23
  meta: {
@@ -1,12 +1,9 @@
1
1
  /**
2
- * @typedef { import("@html-eslint/types").Tag } Tag
3
- * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
- * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
- * @typedef { import("@html-eslint/types").AttributeValue } AttributeValue
6
- * @typedef { import("../types").RuleModule<[]> } RuleModule
2
+ * @import {Tag, AttributeValue} from "@html-eslint/types";
3
+ * @import {RuleModule} from "../types";
7
4
  */
8
5
 
9
- const { parse } = require("@html-eslint/template-parser");
6
+ const { parseTemplateLiteral } = require("./utils/template-literal");
10
7
  const { RULE_CATEGORY } = require("../constants");
11
8
  const { findAttr } = require("./utils/node");
12
9
  const {
@@ -21,7 +18,7 @@ const MESSAGE_IDS = {
21
18
  };
22
19
 
23
20
  /**
24
- * @type {RuleModule}
21
+ * @type {RuleModule<[]>}
25
22
  */
26
23
  module.exports = {
27
24
  meta: {
@@ -93,7 +90,7 @@ module.exports = {
93
90
  TaggedTemplateExpression(node) {
94
91
  const idAttrsMap = new Map();
95
92
  if (shouldCheckTaggedTemplateExpression(node, context)) {
96
- parse(node.quasi, getSourceCode(context), {
93
+ parseTemplateLiteral(node.quasi, getSourceCode(context), {
97
94
  Tag: createTagVisitor(idAttrsMap),
98
95
  });
99
96
  }
@@ -102,7 +99,7 @@ module.exports = {
102
99
  TemplateLiteral(node) {
103
100
  const idAttrsMap = new Map();
104
101
  if (shouldCheckTemplateLiteral(node, context)) {
105
- parse(node, getSourceCode(context), {
102
+ parseTemplateLiteral(node, getSourceCode(context), {
106
103
  Tag: createTagVisitor(idAttrsMap),
107
104
  });
108
105
  }