@html-eslint/eslint-plugin 0.28.0-alpha.0 → 0.28.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 (128) hide show
  1. package/lib/rules/attrs-newline.js +4 -5
  2. package/lib/rules/element-newline.js +22 -25
  3. package/lib/rules/id-naming-convention.js +63 -22
  4. package/lib/rules/indent.js +11 -4
  5. package/lib/rules/lowercase.js +4 -5
  6. package/lib/rules/no-abstract-roles.js +23 -19
  7. package/lib/rules/no-accesskey-attrs.js +18 -14
  8. package/lib/rules/no-aria-hidden-body.js +18 -12
  9. package/lib/rules/no-duplicate-attrs.js +27 -23
  10. package/lib/rules/no-duplicate-id.js +62 -21
  11. package/lib/rules/no-extra-spacing-attrs.js +104 -100
  12. package/lib/rules/no-extra-spacing-text.js +66 -26
  13. package/lib/rules/no-inline-styles.js +3 -25
  14. package/lib/rules/no-multiple-empty-lines.js +94 -42
  15. package/lib/rules/no-multiple-h1.js +1 -1
  16. package/lib/rules/no-obsolete-tags.js +3 -2
  17. package/lib/rules/no-positive-tabindex.js +24 -18
  18. package/lib/rules/no-restricted-attr-values.js +51 -47
  19. package/lib/rules/no-restricted-attrs.js +50 -45
  20. package/lib/rules/no-script-style-type.js +3 -2
  21. package/lib/rules/no-skip-heading-levels.js +1 -1
  22. package/lib/rules/no-target-blank.js +7 -2
  23. package/lib/rules/no-trailing-spaces.js +95 -39
  24. package/lib/rules/quotes.js +12 -8
  25. package/lib/rules/require-attrs.js +28 -20
  26. package/lib/rules/require-button-type.js +7 -3
  27. package/lib/rules/require-closing-tags.js +3 -2
  28. package/lib/rules/require-frame-title.js +3 -2
  29. package/lib/rules/require-img-alt.js +3 -2
  30. package/lib/rules/require-lang.js +3 -2
  31. package/lib/rules/require-li-container.js +1 -1
  32. package/lib/rules/require-meta-charset.js +5 -9
  33. package/lib/rules/require-meta-description.js +5 -5
  34. package/lib/rules/require-meta-viewport.js +5 -5
  35. package/lib/rules/require-open-graph-protocol.js +5 -5
  36. package/lib/rules/require-title.js +8 -7
  37. package/lib/rules/sort-attrs.js +5 -3
  38. package/lib/rules/utils/node.js +226 -68
  39. package/lib/rules/utils/visitors.js +52 -0
  40. package/lib/types.d.ts +15 -19
  41. package/package.json +9 -6
  42. package/types/configs/recommended.d.ts +1 -1
  43. package/types/constants/rule-category.d.ts +4 -4
  44. package/types/index.d.ts.map +1 -1
  45. package/types/rules/attrs-newline.d.ts +7 -4
  46. package/types/rules/attrs-newline.d.ts.map +1 -1
  47. package/types/rules/element-newline.d.ts +13 -11
  48. package/types/rules/element-newline.d.ts.map +1 -1
  49. package/types/rules/id-naming-convention.d.ts +7 -4
  50. package/types/rules/id-naming-convention.d.ts.map +1 -1
  51. package/types/rules/indent.d.ts +10 -7
  52. package/types/rules/indent.d.ts.map +1 -1
  53. package/types/rules/lowercase.d.ts +8 -4
  54. package/types/rules/lowercase.d.ts.map +1 -1
  55. package/types/rules/no-abstract-roles.d.ts +7 -4
  56. package/types/rules/no-abstract-roles.d.ts.map +1 -1
  57. package/types/rules/no-accesskey-attrs.d.ts +7 -4
  58. package/types/rules/no-accesskey-attrs.d.ts.map +1 -1
  59. package/types/rules/no-aria-hidden-body.d.ts +4 -1
  60. package/types/rules/no-aria-hidden-body.d.ts.map +1 -1
  61. package/types/rules/no-duplicate-attrs.d.ts +7 -4
  62. package/types/rules/no-duplicate-attrs.d.ts.map +1 -1
  63. package/types/rules/no-duplicate-id.d.ts +8 -4
  64. package/types/rules/no-duplicate-id.d.ts.map +1 -1
  65. package/types/rules/no-extra-spacing-attrs.d.ts +15 -12
  66. package/types/rules/no-extra-spacing-attrs.d.ts.map +1 -1
  67. package/types/rules/no-extra-spacing-text.d.ts +11 -5
  68. package/types/rules/no-extra-spacing-text.d.ts.map +1 -1
  69. package/types/rules/no-inline-styles.d.ts +5 -2
  70. package/types/rules/no-inline-styles.d.ts.map +1 -1
  71. package/types/rules/no-multiple-empty-lines.d.ts +8 -2
  72. package/types/rules/no-multiple-empty-lines.d.ts.map +1 -1
  73. package/types/rules/no-multiple-h1.d.ts +5 -2
  74. package/types/rules/no-multiple-h1.d.ts.map +1 -1
  75. package/types/rules/no-non-scalable-viewport.d.ts +4 -1
  76. package/types/rules/no-non-scalable-viewport.d.ts.map +1 -1
  77. package/types/rules/no-obsolete-tags.d.ts +4 -1
  78. package/types/rules/no-obsolete-tags.d.ts.map +1 -1
  79. package/types/rules/no-positive-tabindex.d.ts +7 -4
  80. package/types/rules/no-positive-tabindex.d.ts.map +1 -1
  81. package/types/rules/no-restricted-attr-values.d.ts +9 -6
  82. package/types/rules/no-restricted-attr-values.d.ts.map +1 -1
  83. package/types/rules/no-restricted-attrs.d.ts +9 -6
  84. package/types/rules/no-restricted-attrs.d.ts.map +1 -1
  85. package/types/rules/no-script-style-type.d.ts +7 -4
  86. package/types/rules/no-script-style-type.d.ts.map +1 -1
  87. package/types/rules/no-skip-heading-levels.d.ts +5 -2
  88. package/types/rules/no-skip-heading-levels.d.ts.map +1 -1
  89. package/types/rules/no-target-blank.d.ts +4 -1
  90. package/types/rules/no-target-blank.d.ts.map +1 -1
  91. package/types/rules/no-trailing-spaces.d.ts +6 -1
  92. package/types/rules/no-trailing-spaces.d.ts.map +1 -1
  93. package/types/rules/quotes.d.ts +9 -6
  94. package/types/rules/quotes.d.ts.map +1 -1
  95. package/types/rules/require-attrs.d.ts +7 -4
  96. package/types/rules/require-attrs.d.ts.map +1 -1
  97. package/types/rules/require-button-type.d.ts +4 -1
  98. package/types/rules/require-button-type.d.ts.map +1 -1
  99. package/types/rules/require-closing-tags.d.ts +5 -2
  100. package/types/rules/require-closing-tags.d.ts.map +1 -1
  101. package/types/rules/require-doctype.d.ts +4 -1
  102. package/types/rules/require-doctype.d.ts.map +1 -1
  103. package/types/rules/require-frame-title.d.ts +4 -1
  104. package/types/rules/require-frame-title.d.ts.map +1 -1
  105. package/types/rules/require-img-alt.d.ts +5 -2
  106. package/types/rules/require-img-alt.d.ts.map +1 -1
  107. package/types/rules/require-lang.d.ts +4 -1
  108. package/types/rules/require-lang.d.ts.map +1 -1
  109. package/types/rules/require-li-container.d.ts +4 -1
  110. package/types/rules/require-li-container.d.ts.map +1 -1
  111. package/types/rules/require-meta-charset.d.ts +6 -2
  112. package/types/rules/require-meta-charset.d.ts.map +1 -1
  113. package/types/rules/require-meta-description.d.ts +6 -2
  114. package/types/rules/require-meta-description.d.ts.map +1 -1
  115. package/types/rules/require-meta-viewport.d.ts +6 -2
  116. package/types/rules/require-meta-viewport.d.ts.map +1 -1
  117. package/types/rules/require-open-graph-protocol.d.ts +6 -2
  118. package/types/rules/require-open-graph-protocol.d.ts.map +1 -1
  119. package/types/rules/require-title.d.ts +7 -3
  120. package/types/rules/require-title.d.ts.map +1 -1
  121. package/types/rules/sort-attrs.d.ts +7 -4
  122. package/types/rules/sort-attrs.d.ts.map +1 -1
  123. package/types/rules/utils/array.d.ts.map +1 -1
  124. package/types/rules/utils/naming.d.ts.map +1 -1
  125. package/types/rules/utils/node.d.ts +65 -12
  126. package/types/rules/utils/node.d.ts.map +1 -1
  127. package/types/rules/utils/visitors.d.ts +10 -0
  128. package/types/rules/utils/visitors.d.ts.map +1 -0
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  const { RULE_CATEGORY } = require("../constants");
11
+ const { createVisitors } = require("./utils/visitors");
11
12
 
12
13
  const MESSAGE_IDS = {
13
14
  RESTRICTED: "restricted",
@@ -64,54 +65,57 @@ module.exports = {
64
65
  */
65
66
  const options = context.options;
66
67
  const checkers = options.map((option) => new PatternChecker(option));
67
-
68
- return {
69
- /**
70
- * @param {TagNode | StyleTagNode | ScriptTagNode} node
71
- */
72
- [["Tag", "StyleTag", "ScriptTag"].join(",")](node) {
73
- node.attributes.forEach((attr) => {
74
- if (
75
- !attr.key ||
76
- !attr.key.value ||
77
- !attr.value ||
78
- typeof attr.value.value !== "string"
79
- ) {
80
- return;
81
- }
82
-
83
- const matched = checkers.find(
84
- (checker) =>
85
- attr.value && checker.test(attr.key.value, attr.value.value)
86
- );
87
-
88
- if (!matched) {
89
- return;
90
- }
91
-
92
- /**
93
- * @type {{node: AttributeNode, message: string, messageId?: string}}
94
- */
95
- const result = {
96
- node: attr,
97
- message: "",
98
- };
99
-
100
- const customMessage = matched.getMessage();
101
-
102
- if (customMessage) {
103
- result.message = customMessage;
104
- } else {
105
- result.messageId = MESSAGE_IDS.RESTRICTED;
106
- }
107
-
108
- context.report({
109
- ...result,
110
- data: { attrValuePatterns: attr.value.value },
111
- });
68
+ /**
69
+ * @param {TagNode | StyleTagNode | ScriptTagNode} node
70
+ */
71
+ function check(node) {
72
+ node.attributes.forEach((attr) => {
73
+ if (
74
+ !attr.key ||
75
+ !attr.key.value ||
76
+ !attr.value ||
77
+ typeof attr.value.value !== "string"
78
+ ) {
79
+ return;
80
+ }
81
+
82
+ const matched = checkers.find(
83
+ (checker) =>
84
+ attr.value && checker.test(attr.key.value, attr.value.value)
85
+ );
86
+
87
+ if (!matched) {
88
+ return;
89
+ }
90
+
91
+ /**
92
+ * @type {{node: AttributeNode, message: string, messageId?: string}}
93
+ */
94
+ const result = {
95
+ node: attr,
96
+ message: "",
97
+ };
98
+
99
+ const customMessage = matched.getMessage();
100
+
101
+ if (customMessage) {
102
+ result.message = customMessage;
103
+ } else {
104
+ result.messageId = MESSAGE_IDS.RESTRICTED;
105
+ }
106
+
107
+ context.report({
108
+ ...result,
109
+ data: { attrValuePatterns: attr.value.value },
112
110
  });
113
- },
114
- };
111
+ });
112
+ }
113
+
114
+ return createVisitors(context, {
115
+ Tag: check,
116
+ StyleTag: check,
117
+ ScriptTag: check,
118
+ });
115
119
  },
116
120
  };
117
121
 
@@ -9,6 +9,7 @@
9
9
 
10
10
  const { NODE_TYPES } = require("@html-eslint/parser");
11
11
  const { RULE_CATEGORY } = require("../constants");
12
+ const { createVisitors } = require("./utils/visitors");
12
13
 
13
14
  const MESSAGE_IDS = {
14
15
  RESTRICTED: "restricted",
@@ -65,52 +66,56 @@ module.exports = {
65
66
  const options = context.options;
66
67
  const checkers = options.map((option) => new PatternChecker(option));
67
68
 
68
- return {
69
- /**
70
- * @param {TagNode | StyleTagNode | ScriptTagNode} node
71
- */
72
- [["Tag", "StyleTag", "ScriptTag"].join(",")](node) {
73
- const tagName =
74
- node.type === NODE_TYPES.Tag
75
- ? node.name
76
- : node.type === NODE_TYPES.ScriptTag
77
- ? "script"
78
- : "style";
79
- node.attributes.forEach((attr) => {
80
- if (!attr.key || !attr.key.value) {
81
- return;
82
- }
83
- const matched = checkers.find((checker) =>
84
- checker.test(tagName, attr.key.value)
85
- );
86
-
87
- if (!matched) {
88
- return;
89
- }
90
-
91
- /**
92
- * @type {{node: AttributeNode, message: string, messageId?: string}}
93
- */
94
- const result = {
95
- node: attr,
96
- message: "",
97
- };
98
-
99
- const customMessage = matched.getMessage();
100
-
101
- if (customMessage) {
102
- result.message = customMessage;
103
- } else {
104
- result.messageId = MESSAGE_IDS.RESTRICTED;
105
- }
106
-
107
- context.report({
108
- ...result,
109
- data: { attr: attr.key.value },
110
- });
69
+ /**
70
+ * @param {TagNode | StyleTagNode | ScriptTagNode} node
71
+ */
72
+ function check(node) {
73
+ const tagName =
74
+ node.type === NODE_TYPES.Tag
75
+ ? node.name
76
+ : node.type === NODE_TYPES.ScriptTag
77
+ ? "script"
78
+ : "style";
79
+ node.attributes.forEach((attr) => {
80
+ if (!attr.key || !attr.key.value) {
81
+ return;
82
+ }
83
+ const matched = checkers.find((checker) =>
84
+ checker.test(tagName, attr.key.value)
85
+ );
86
+
87
+ if (!matched) {
88
+ return;
89
+ }
90
+
91
+ /**
92
+ * @type {{node: AttributeNode, message: string, messageId?: string}}
93
+ */
94
+ const result = {
95
+ node: attr,
96
+ message: "",
97
+ };
98
+
99
+ const customMessage = matched.getMessage();
100
+
101
+ if (customMessage) {
102
+ result.message = customMessage;
103
+ } else {
104
+ result.messageId = MESSAGE_IDS.RESTRICTED;
105
+ }
106
+
107
+ context.report({
108
+ ...result,
109
+ data: { attr: attr.key.value },
111
110
  });
112
- },
113
- };
111
+ });
112
+ }
113
+
114
+ return createVisitors(context, {
115
+ Tag: check,
116
+ StyleTag: check,
117
+ ScriptTag: check,
118
+ });
114
119
  },
115
120
  };
116
121
 
@@ -7,6 +7,7 @@
7
7
 
8
8
  const { RULE_CATEGORY } = require("../constants");
9
9
  const { findAttr } = require("./utils/node");
10
+ const { createVisitors } = require("./utils/visitors");
10
11
 
11
12
  const MESSAGE_IDS = {
12
13
  UNNECESSARY: "unnecessary",
@@ -56,7 +57,7 @@ module.exports = {
56
57
  });
57
58
  }
58
59
  }
59
- return {
60
+ return createVisitors(context, {
60
61
  ScriptTag(node) {
61
62
  check(node, "text/javascript");
62
63
  },
@@ -71,6 +72,6 @@ module.exports = {
71
72
  }
72
73
  }
73
74
  },
74
- };
75
+ });
75
76
  },
76
77
  };
@@ -46,7 +46,7 @@ module.exports = {
46
46
  level: parseInt(node.name.replace("h", ""), 10),
47
47
  });
48
48
  },
49
- "Program:exit"() {
49
+ "Document:exit"() {
50
50
  if (headings.length <= 1) {
51
51
  return;
52
52
  }
@@ -4,6 +4,7 @@
4
4
 
5
5
  const { RULE_CATEGORY } = require("../constants");
6
6
  const { findAttr } = require("./utils/node");
7
+ const { createVisitors } = require("./utils/visitors");
7
8
 
8
9
  const MESSAGE_IDS = {
9
10
  MISSING: "missing",
@@ -38,7 +39,7 @@ module.exports = {
38
39
  function isExternalLink(link) {
39
40
  return /^(?:\w+:|\/\/)/.test(link);
40
41
  }
41
- return {
42
+ return createVisitors(context, {
42
43
  Tag(node) {
43
44
  if (node.name !== "a") {
44
45
  return;
@@ -49,6 +50,10 @@ module.exports = {
49
50
  const href = findAttr(node, "href");
50
51
  if (href && href.value && isExternalLink(href.value.value)) {
51
52
  const rel = findAttr(node, "rel");
53
+ if (rel && rel.value && rel.value.templates.length) {
54
+ return;
55
+ }
56
+
52
57
  if (!rel || !rel.value || !rel.value.value.includes("noreferrer")) {
53
58
  context.report({
54
59
  node: target,
@@ -58,6 +63,6 @@ module.exports = {
58
63
  }
59
64
  }
60
65
  },
61
- };
66
+ });
62
67
  },
63
68
  };
@@ -1,8 +1,21 @@
1
1
  /**
2
2
  * @typedef { import("../types").RuleModule } RuleModule
3
+ * @typedef { import("es-html-parser").CommentContentNode } CommentContentNode
4
+ * @typedef { import("es-html-parser").TextNode } TextNode
3
5
  */
4
6
 
7
+ const { parse } = require("@html-eslint/template-parser");
5
8
  const { RULE_CATEGORY } = require("../constants");
9
+ const {
10
+ getTemplateTokens,
11
+ codeToLines,
12
+ isRangesOverlap,
13
+ } = require("./utils/node");
14
+ const {
15
+ shouldCheckTaggedTemplateExpression,
16
+ shouldCheckTemplateLiteral,
17
+ } = require("./utils/settings");
18
+ const { getSourceCode } = require("./utils/source-code");
6
19
 
7
20
  const MESSAGE_IDS = {
8
21
  TRAILING_SPACE: "trailingSpace",
@@ -27,48 +40,91 @@ module.exports = {
27
40
  },
28
41
 
29
42
  create(context) {
30
- const sourceCode = context.getSourceCode();
31
- const lineBreaks = sourceCode.getText().match(/\r\n|[\r\n\u2028\u2029]/gu);
43
+ const sourceCode = getSourceCode(context);
44
+ /**
45
+ * @param {string} source
46
+ * @param {string[]} lines
47
+ * @param {number} rangeOffset
48
+ * @param {((CommentContentNode | TextNode)['templates'][number])[]} tokens
49
+ */
50
+ function check(source, lines, rangeOffset, tokens) {
51
+ let rangeIndex = rangeOffset;
52
+ const lineBreaks = source.match(/\r\n|[\r\n\u2028\u2029]/gu);
53
+ lines.forEach((line, index) => {
54
+ const lineNumber = index + 1;
55
+ const match = line.match(/[ \t\u00a0\u2000-\u200b\u3000]+$/);
56
+ const lineBreakLength =
57
+ lineBreaks && lineBreaks[index] ? lineBreaks[index].length : 1;
58
+ const lineLength = line.length + lineBreakLength;
32
59
 
33
- return {
34
- Program() {
35
- const lines = sourceCode.lines;
36
- let rangeIndex = 0;
37
-
38
- lines.forEach((line, index) => {
39
- const lineNumber = index + 1;
40
- const match = line.match(/[ \t\u00a0\u2000-\u200b\u3000]+$/);
41
- const lineBreakLength =
42
- lineBreaks && lineBreaks[index] ? lineBreaks[index].length : 1;
43
- const lineLength = line.length + lineBreakLength;
44
-
45
- if (match) {
46
- if (typeof match.index === "number" && match.index > 0) {
47
- const loc = {
48
- start: {
49
- line: lineNumber,
50
- column: match.index,
51
- },
52
- end: {
53
- line: lineNumber,
54
- column: lineLength - lineBreakLength,
55
- },
56
- };
57
-
58
- context.report({
59
- messageId: MESSAGE_IDS.TRAILING_SPACE,
60
- loc,
61
- fix(fixer) {
62
- return fixer.removeRange([
63
- rangeIndex + loc.start.column,
64
- rangeIndex + loc.end.column,
65
- ]);
66
- },
67
- });
60
+ if (match) {
61
+ if (typeof match.index === "number" && match.index > 0) {
62
+ const loc = {
63
+ start: {
64
+ line: lineNumber,
65
+ column: match.index,
66
+ },
67
+ end: {
68
+ line: lineNumber,
69
+ column: lineLength - lineBreakLength,
70
+ },
71
+ };
72
+ const start = sourceCode.getIndexFromLoc(loc.start);
73
+ const end = sourceCode.getIndexFromLoc(loc.end);
74
+ if (
75
+ tokens.some((token) => isRangesOverlap(token.range, [start, end]))
76
+ ) {
77
+ return;
68
78
  }
79
+ context.report({
80
+ messageId: MESSAGE_IDS.TRAILING_SPACE,
81
+ loc,
82
+ fix(fixer) {
83
+ return fixer.removeRange([
84
+ rangeIndex + loc.start.column,
85
+ rangeIndex + loc.end.column,
86
+ ]);
87
+ },
88
+ });
69
89
  }
70
- rangeIndex += lineLength;
71
- });
90
+ }
91
+ rangeIndex += lineLength;
92
+ });
93
+ }
94
+
95
+ return {
96
+ Document() {
97
+ check(sourceCode.getText(), sourceCode.getLines(), 0, []);
98
+ },
99
+ TaggedTemplateExpression(node) {
100
+ if (shouldCheckTaggedTemplateExpression(node, context)) {
101
+ const { html, tokens } = parse(
102
+ node.quasi,
103
+ getSourceCode(context),
104
+ {}
105
+ );
106
+ const lines = codeToLines(html);
107
+ check(
108
+ html,
109
+ lines,
110
+ // @ts-ignore
111
+ node.quasi.range[0] + 1,
112
+ getTemplateTokens(tokens)
113
+ );
114
+ }
115
+ },
116
+ TemplateLiteral(node) {
117
+ if (shouldCheckTemplateLiteral(node, context)) {
118
+ const { html, tokens } = parse(node, getSourceCode(context), {});
119
+ const lines = codeToLines(html);
120
+ check(
121
+ html,
122
+ lines,
123
+ // @ts-ignore
124
+ node.range[0] + 1,
125
+ getTemplateTokens(tokens)
126
+ );
127
+ }
72
128
  },
73
129
  };
74
130
  },
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  const { RULE_CATEGORY } = require("../constants");
11
+ const { createVisitors } = require("./utils/visitors");
11
12
 
12
13
  const MESSAGE_IDS = {
13
14
  UNEXPECTED: "unexpected",
@@ -131,14 +132,17 @@ module.exports = {
131
132
  });
132
133
  }
133
134
  }
135
+ /**
136
+ * @param {TagNode | ScriptTagNode | StyleTagNode} node
137
+ */
138
+ function check(node) {
139
+ node.attributes.forEach((attr) => checkQuotes(attr));
140
+ }
134
141
 
135
- return {
136
- /**
137
- * @param {TagNode | ScriptTagNode | StyleTagNode} node
138
- */
139
- [["Tag", "ScriptTag", "StyleTag"].join(",")](node) {
140
- node.attributes.forEach((attr) => checkQuotes(attr));
141
- },
142
- };
142
+ return createVisitors(context, {
143
+ Tag: check,
144
+ ScriptTag: check,
145
+ StyleTag: check,
146
+ });
143
147
  },
144
148
  };
@@ -7,6 +7,7 @@
7
7
 
8
8
  const { NODE_TYPES } = require("@html-eslint/parser");
9
9
  const { RULE_CATEGORY } = require("../constants");
10
+ const { createVisitors } = require("./utils/visitors");
10
11
 
11
12
  const MESSAGE_IDS = {
12
13
  MISSING: "missing",
@@ -103,25 +104,32 @@ module.exports = {
103
104
  });
104
105
  }
105
106
 
106
- return {
107
- /**
108
- * @param {StyleTagNode | ScriptTagNode} node
109
- * @returns
110
- */
111
- [["StyleTag", "ScriptTag"].join(",")](node) {
112
- const tagName = node.type === NODE_TYPES.StyleTag ? "style" : "script";
113
- if (!tagOptionsMap.has(tagName)) {
114
- return;
115
- }
116
- check(node, tagName);
117
- },
118
- Tag(node) {
119
- const tagName = node.name.toLowerCase();
120
- if (!tagOptionsMap.has(tagName)) {
121
- return;
122
- }
123
- check(node, tagName);
124
- },
125
- };
107
+ /**
108
+ * @param {StyleTagNode | ScriptTagNode} node
109
+ */
110
+ function checkStyleOrScript(node) {
111
+ const tagName = node.type === NODE_TYPES.StyleTag ? "style" : "script";
112
+ if (!tagOptionsMap.has(tagName)) {
113
+ return;
114
+ }
115
+ check(node, tagName);
116
+ }
117
+
118
+ /**
119
+ * @param {TagNode} node
120
+ */
121
+ function checkTag(node) {
122
+ const tagName = node.name.toLowerCase();
123
+ if (!tagOptionsMap.has(tagName)) {
124
+ return;
125
+ }
126
+ check(node, tagName);
127
+ }
128
+
129
+ return createVisitors(context, {
130
+ StyleTag: checkStyleOrScript,
131
+ ScriptTag: checkStyleOrScript,
132
+ Tag: checkTag,
133
+ });
126
134
  },
127
135
  };
@@ -4,6 +4,7 @@
4
4
 
5
5
  const { RULE_CATEGORY } = require("../constants");
6
6
  const { findAttr } = require("./utils/node");
7
+ const { createVisitors } = require("./utils/visitors");
7
8
 
8
9
  const MESSAGE_IDS = {
9
10
  MISSING: "missing",
@@ -35,7 +36,7 @@ module.exports = {
35
36
  },
36
37
 
37
38
  create(context) {
38
- return {
39
+ return createVisitors(context, {
39
40
  Tag(node) {
40
41
  if (node.name !== "button") {
41
42
  return;
@@ -46,7 +47,10 @@ module.exports = {
46
47
  node: node.openStart,
47
48
  messageId: MESSAGE_IDS.MISSING,
48
49
  });
49
- } else if (!VALID_BUTTON_TYPES_SET.has(typeAttr.value.value)) {
50
+ } else if (
51
+ !VALID_BUTTON_TYPES_SET.has(typeAttr.value.value) &&
52
+ !typeAttr.value.templates.length
53
+ ) {
50
54
  context.report({
51
55
  node: typeAttr,
52
56
  messageId: MESSAGE_IDS.INVALID,
@@ -56,6 +60,6 @@ module.exports = {
56
60
  });
57
61
  }
58
62
  },
59
- };
63
+ });
60
64
  },
61
65
  };
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  const { RULE_CATEGORY, VOID_ELEMENTS } = require("../constants");
7
+ const { createVisitors } = require("./utils/visitors");
7
8
 
8
9
  const VOID_ELEMENTS_SET = new Set(VOID_ELEMENTS);
9
10
 
@@ -125,7 +126,7 @@ module.exports = {
125
126
  }
126
127
  }
127
128
 
128
- return {
129
+ return createVisitors(context, {
129
130
  Tag(node) {
130
131
  const isVoidElement = VOID_ELEMENTS_SET.has(node.name);
131
132
  const isSelfClosingCustomElement = !!selfClosingCustomPatterns.some(
@@ -156,6 +157,6 @@ module.exports = {
156
157
  foreignContext.pop();
157
158
  }
158
159
  },
159
- };
160
+ });
160
161
  },
161
162
  };
@@ -4,6 +4,7 @@
4
4
 
5
5
  const { RULE_CATEGORY } = require("../constants");
6
6
  const { findAttr } = require("./utils/node");
7
+ const { createVisitors } = require("./utils/visitors");
7
8
 
8
9
  const MESSAGE_IDS = {
9
10
  MISSING: "missing",
@@ -32,7 +33,7 @@ module.exports = {
32
33
  },
33
34
 
34
35
  create(context) {
35
- return {
36
+ return createVisitors(context, {
36
37
  Tag(node) {
37
38
  if (node.name !== "frame" && node.name !== "iframe") {
38
39
  return;
@@ -51,6 +52,6 @@ module.exports = {
51
52
  });
52
53
  }
53
54
  },
54
- };
55
+ });
55
56
  },
56
57
  };
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  const { RULE_CATEGORY } = require("../constants");
7
+ const { createVisitors } = require("./utils/visitors");
7
8
 
8
9
  const MESSAGE_IDS = {
9
10
  MISSING_ALT: "missingAlt",
@@ -48,7 +49,7 @@ module.exports = {
48
49
  context.options[0].substitute) ||
49
50
  [];
50
51
 
51
- return {
52
+ return createVisitors(context, {
52
53
  Tag(node) {
53
54
  if (node.name !== "img") {
54
55
  return;
@@ -66,7 +67,7 @@ module.exports = {
66
67
  });
67
68
  }
68
69
  },
69
- };
70
+ });
70
71
  },
71
72
  };
72
73
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  const { RULE_CATEGORY } = require("../constants");
6
6
  const { findAttr } = require("./utils/node");
7
+ const { createVisitors } = require("./utils/visitors");
7
8
 
8
9
  const MESSAGE_IDS = {
9
10
  MISSING: "missing",
@@ -32,7 +33,7 @@ module.exports = {
32
33
  },
33
34
 
34
35
  create(context) {
35
- return {
36
+ return createVisitors(context, {
36
37
  Tag(node) {
37
38
  if (node.name !== "html") {
38
39
  return;
@@ -56,6 +57,6 @@ module.exports = {
56
57
  });
57
58
  }
58
59
  },
59
- };
60
+ });
60
61
  },
61
62
  };