@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
@@ -15,6 +15,7 @@
15
15
 
16
16
  const { RULE_CATEGORY } = require("../constants");
17
17
  const { getLocBetween } = require("./utils/node");
18
+ const { createVisitors } = require("./utils/visitors");
18
19
 
19
20
  const MESSAGE_IDS = {
20
21
  EXTRA_BETWEEN: "unexpectedBetween",
@@ -182,125 +183,128 @@ module.exports = {
182
183
  }
183
184
  }
184
185
  }
186
+ /**
187
+ * @param {TagNode | StyleTagNode | ScriptTagNode} node
188
+ * @returns
189
+ */
190
+ function check(node) {
191
+ if (!node.attributes) {
192
+ return;
193
+ }
185
194
 
186
- return {
187
- /**
188
- * @param {TagNode | StyleTagNode | ScriptTagNode} node
189
- * @returns
190
- */
191
- [["Tag", "StyleTag", "ScriptTag"].join(",")](node) {
192
- if (!node.attributes) {
193
- return;
194
- }
195
-
196
- if (node.attributes.length) {
197
- checkExtraSpaceBefore(node.openStart, node.attributes[0]);
195
+ if (node.attributes.length) {
196
+ checkExtraSpaceBefore(node.openStart, node.attributes[0]);
198
197
 
199
- for (const attr of node.attributes) {
200
- if (attr.startWrapper && attr.value) {
201
- if (
202
- disallowInAssignment &&
203
- attr.startWrapper.loc.start.column - attr.key.loc.end.column > 1
204
- ) {
205
- const start = attr.key.range[1];
206
- const end = attr.startWrapper.range[0];
207
- context.report({
208
- node: attr,
209
- messageId: MESSAGE_IDS.EXTRA_IN_ASSIGNMENT,
210
- fix(fixer) {
211
- return fixer.replaceTextRange([start, end], `=`);
212
- },
213
- });
214
- }
198
+ for (const attr of node.attributes) {
199
+ if (attr.startWrapper && attr.value) {
200
+ if (
201
+ disallowInAssignment &&
202
+ attr.startWrapper.loc.start.column - attr.key.loc.end.column > 1
203
+ ) {
204
+ const start = attr.key.range[1];
205
+ const end = attr.startWrapper.range[0];
206
+ context.report({
207
+ node: attr,
208
+ messageId: MESSAGE_IDS.EXTRA_IN_ASSIGNMENT,
209
+ fix(fixer) {
210
+ return fixer.replaceTextRange([start, end], `=`);
211
+ },
212
+ });
215
213
  }
216
214
  }
217
215
  }
216
+ }
218
217
 
219
- if (node.openEnd) {
220
- checkExtraSpacesBetweenAttrs(node.attributes);
218
+ if (node.openEnd) {
219
+ checkExtraSpacesBetweenAttrs(node.attributes);
221
220
 
222
- const lastAttr = node.attributes[node.attributes.length - 1];
223
- const nodeBeforeEnd =
224
- node.attributes.length === 0 ? node.openStart : lastAttr;
221
+ const lastAttr = node.attributes[node.attributes.length - 1];
222
+ const nodeBeforeEnd =
223
+ node.attributes.length === 0 ? node.openStart : lastAttr;
225
224
 
226
- if (nodeBeforeEnd.loc.end.line !== node.openEnd.loc.start.line) {
227
- return;
228
- }
225
+ if (nodeBeforeEnd.loc.end.line !== node.openEnd.loc.start.line) {
226
+ return;
227
+ }
229
228
 
230
- const isSelfClosing = node.openEnd.value === "/>";
229
+ const isSelfClosing = node.openEnd.value === "/>";
231
230
 
232
- const spacesBetween =
233
- node.openEnd.loc.start.column - nodeBeforeEnd.loc.end.column;
234
- const locBetween = getLocBetween(nodeBeforeEnd, node.openEnd);
231
+ const spacesBetween =
232
+ node.openEnd.loc.start.column - nodeBeforeEnd.loc.end.column;
233
+ const locBetween = getLocBetween(nodeBeforeEnd, node.openEnd);
235
234
 
236
- if (isSelfClosing && enforceBeforeSelfClose) {
237
- if (spacesBetween < 1) {
238
- context.report({
239
- loc: locBetween,
240
- messageId: MESSAGE_IDS.MISSING_BEFORE_SELF_CLOSE,
241
- fix(fixer) {
242
- return fixer.insertTextAfter(nodeBeforeEnd, " ");
243
- },
244
- });
245
- } else if (spacesBetween === 1) {
246
- if (
247
- disallowTabs &&
248
- sourceCode[node.openEnd.range[0] - 1] === `\t`
249
- ) {
250
- context.report({
251
- loc: node.openEnd.loc,
252
- messageId: MESSAGE_IDS.EXTRA_TAB_BEFORE_SELF_CLOSE,
253
- fix(fixer) {
254
- return fixer.replaceTextRange(
255
- [node.openEnd.range[0] - 1, node.openEnd.range[0]],
256
- ` `
257
- );
258
- },
259
- });
260
- }
261
- } else {
235
+ if (isSelfClosing && enforceBeforeSelfClose) {
236
+ if (spacesBetween < 1) {
237
+ context.report({
238
+ loc: locBetween,
239
+ messageId: MESSAGE_IDS.MISSING_BEFORE_SELF_CLOSE,
240
+ fix(fixer) {
241
+ return fixer.insertTextAfter(nodeBeforeEnd, " ");
242
+ },
243
+ });
244
+ } else if (spacesBetween === 1) {
245
+ if (
246
+ disallowTabs &&
247
+ sourceCode[node.openEnd.range[0] - 1] === `\t`
248
+ ) {
262
249
  context.report({
263
- loc: locBetween,
264
- messageId: MESSAGE_IDS.EXTRA_BEFORE_SELF_CLOSE,
250
+ loc: node.openEnd.loc,
251
+ messageId: MESSAGE_IDS.EXTRA_TAB_BEFORE_SELF_CLOSE,
265
252
  fix(fixer) {
266
- return fixer.removeRange([
267
- nodeBeforeEnd.range[1] + 1,
268
- node.openEnd.range[0],
269
- ]);
253
+ return fixer.replaceTextRange(
254
+ [node.openEnd.range[0] - 1, node.openEnd.range[0]],
255
+ ` `
256
+ );
270
257
  },
271
258
  });
272
259
  }
273
-
274
- return;
260
+ } else {
261
+ context.report({
262
+ loc: locBetween,
263
+ messageId: MESSAGE_IDS.EXTRA_BEFORE_SELF_CLOSE,
264
+ fix(fixer) {
265
+ return fixer.removeRange([
266
+ nodeBeforeEnd.range[1] + 1,
267
+ node.openEnd.range[0],
268
+ ]);
269
+ },
270
+ });
275
271
  }
276
272
 
277
- if (spacesBetween > 0) {
278
- if (node.attributes.length > 0) {
279
- context.report({
280
- loc: locBetween,
281
- messageId: MESSAGE_IDS.EXTRA_AFTER,
282
- fix(fixer) {
283
- return fixer.removeRange([
284
- lastAttr.range[1],
285
- node.openEnd.range[0],
286
- ]);
287
- },
288
- });
289
- } else {
290
- context.report({
291
- loc: locBetween,
292
- messageId: MESSAGE_IDS.EXTRA_BEFORE_CLOSE,
293
- fix(fixer) {
294
- return fixer.removeRange([
295
- node.openStart.range[1],
296
- node.openEnd.range[0],
297
- ]);
298
- },
299
- });
300
- }
273
+ return;
274
+ }
275
+
276
+ if (spacesBetween > 0) {
277
+ if (node.attributes.length > 0) {
278
+ context.report({
279
+ loc: locBetween,
280
+ messageId: MESSAGE_IDS.EXTRA_AFTER,
281
+ fix(fixer) {
282
+ return fixer.removeRange([
283
+ lastAttr.range[1],
284
+ node.openEnd.range[0],
285
+ ]);
286
+ },
287
+ });
288
+ } else {
289
+ context.report({
290
+ loc: locBetween,
291
+ messageId: MESSAGE_IDS.EXTRA_BEFORE_CLOSE,
292
+ fix(fixer) {
293
+ return fixer.removeRange([
294
+ node.openStart.range[1],
295
+ node.openEnd.range[0],
296
+ ]);
297
+ },
298
+ });
301
299
  }
302
300
  }
303
- },
304
- };
301
+ }
302
+ }
303
+
304
+ return createVisitors(context, {
305
+ Tag: check,
306
+ StyleTag: check,
307
+ ScriptTag: check,
308
+ });
305
309
  },
306
310
  };
@@ -1,12 +1,18 @@
1
1
  /**
2
2
  * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").ProgramNode } ProgramNode
4
3
  * @typedef { import("es-html-parser").CommentContentNode } CommentContentNode
4
+ * @typedef { import("es-html-parser").TagNode } TagNode
5
+ * @typedef { import("es-html-parser").CommentNode } CommentNode
5
6
  * @typedef { import("../types").ContentNode } ContentNode
6
- * @typedef { import("../types").TextNode } TextNode
7
+ * @typedef { import("es-html-parser").TextNode } TextNode
8
+ * @typedef { import("../types").LineNode } LineNode
9
+ * @typedef { import("../types").Range } Range
7
10
  */
8
11
 
9
12
  const { RULE_CATEGORY } = require("../constants");
13
+ const { isTag, isOverlapWithTemplates } = require("./utils/node");
14
+ const { getSourceCode } = require("./utils/source-code");
15
+ const { createVisitors } = require("./utils/visitors");
10
16
 
11
17
  const MESSAGE_IDS = {
12
18
  UNEXPECTED: "unexpected",
@@ -48,39 +54,36 @@ module.exports = {
48
54
 
49
55
  create(context) {
50
56
  const options = context.options[0] || {};
57
+ /**
58
+ * @type {string[]}
59
+ */
51
60
  const skipTags = options.skip || [];
52
- const sourceCode = context.getSourceCode();
61
+ const sourceCode = getSourceCode(context);
62
+ /**
63
+ * @type {TagNode[]}
64
+ */
65
+ const tagStack = [];
53
66
 
54
67
  /**
55
- * @param {Array<ContentNode>} siblings
68
+ * @param {CommentNode | TextNode} node
69
+ * @returns {boolean}
56
70
  */
57
- function checkSiblings(siblings) {
58
- for (
59
- let length = siblings.length, index = 0;
60
- index < length;
61
- index += 1
71
+ function hasSkipTagOnParent(node) {
72
+ // @ts-ignore
73
+ const parent = node.parent;
74
+ if (
75
+ parent &&
76
+ // @ts-ignore
77
+ isTag(parent) &&
78
+ skipTags.some((skipTag) => skipTag === parent.name)
62
79
  ) {
63
- const node = siblings[index];
64
-
65
- if (node.type === `Tag` && skipTags.includes(node.name) === false) {
66
- checkSiblings(node.children);
67
- } else if (node.type === `Text`) {
68
- stripConsecutiveSpaces(node);
69
- } else if (node.type === `Comment`) {
70
- stripConsecutiveSpaces(node.value);
71
- }
80
+ return true;
72
81
  }
82
+ return false;
73
83
  }
74
84
 
75
- return {
76
- Program(node) {
77
- // @ts-ignore
78
- checkSiblings(node.body);
79
- },
80
- };
81
-
82
85
  /**
83
- * @param {TextNode | CommentContentNode} node
86
+ * @param {CommentContentNode | TextNode} node
84
87
  */
85
88
  function stripConsecutiveSpaces(node) {
86
89
  const text = node.value;
@@ -97,6 +100,15 @@ module.exports = {
97
100
  const indexStart = node.range[0] + matcher.lastIndex - space.length;
98
101
  const indexEnd = indexStart + space.length;
99
102
 
103
+ const hasOverlap = isOverlapWithTemplates(node.templates, [
104
+ indexStart,
105
+ indexEnd,
106
+ ]);
107
+
108
+ if (hasOverlap) {
109
+ return;
110
+ }
111
+
100
112
  context.report({
101
113
  node: node,
102
114
  loc: {
@@ -113,5 +125,33 @@ module.exports = {
113
125
  });
114
126
  }
115
127
  }
128
+
129
+ return createVisitors(context, {
130
+ Comment(node) {
131
+ if (hasSkipTagOnParent(node)) {
132
+ return;
133
+ }
134
+ stripConsecutiveSpaces(node.value);
135
+ },
136
+ Text(node) {
137
+ if (hasSkipTagOnParent(node)) {
138
+ return;
139
+ }
140
+ stripConsecutiveSpaces(node);
141
+ },
142
+ Tag(node) {
143
+ tagStack.push(node);
144
+ if (
145
+ skipTags.some((skipTag) =>
146
+ tagStack.some((tag) => tag.name === skipTag)
147
+ )
148
+ ) {
149
+ return;
150
+ }
151
+ },
152
+ "Tag:exit"() {
153
+ tagStack.pop();
154
+ },
155
+ });
116
156
  },
117
157
  };
@@ -5,12 +5,7 @@
5
5
 
6
6
  const { RULE_CATEGORY } = require("../constants");
7
7
  const { findAttr } = require("./utils/node");
8
- const { parse } = require("@html-eslint/template-parser");
9
- const {
10
- shouldCheckTaggedTemplateExpression,
11
- shouldCheckTemplateLiteral,
12
- } = require("./utils/settings");
13
- const { getSourceCode } = require("./utils/source-code");
8
+ const { createVisitors } = require("./utils/visitors");
14
9
  const MESSAGE_IDS = {
15
10
  INLINE_STYLE: "unexpectedInlineStyle",
16
11
  };
@@ -36,10 +31,7 @@ module.exports = {
36
31
  },
37
32
 
38
33
  create(context) {
39
- /**
40
- * @type {RuleListener}
41
- */
42
- const visitors = {
34
+ return createVisitors(context, {
43
35
  Tag(node) {
44
36
  const styleAttr = findAttr(node, "style");
45
37
  if (styleAttr) {
@@ -49,20 +41,6 @@ module.exports = {
49
41
  });
50
42
  }
51
43
  },
52
- };
53
-
54
- return {
55
- ...visitors,
56
- TaggedTemplateExpression(node) {
57
- if (shouldCheckTaggedTemplateExpression(node, context)) {
58
- parse(node.quasi, getSourceCode(context), visitors);
59
- }
60
- },
61
- TemplateLiteral(node) {
62
- if (shouldCheckTemplateLiteral(node, context)) {
63
- parse(node, getSourceCode(context), visitors);
64
- }
65
- },
66
- };
44
+ });
67
45
  },
68
46
  };
@@ -1,9 +1,23 @@
1
1
  /**
2
2
  * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").ProgramNode } ProgramNode
3
+ * @typedef { import("es-html-parser").DocumentNode } DocumentNode
4
+ * @typedef { import("es-html-parser").AnyToken } AnyToken
5
+ * @typedef { import("es-html-parser").CommentContentNode } CommentContentNode
6
+ * @typedef { import("es-html-parser").TextNode } TextNode
4
7
  */
5
8
 
9
+ const { parse } = require("@html-eslint/template-parser");
6
10
  const { RULE_CATEGORY } = require("../constants");
11
+ const {
12
+ shouldCheckTaggedTemplateExpression,
13
+ shouldCheckTemplateLiteral,
14
+ } = require("./utils/settings");
15
+ const { getSourceCode } = require("./utils/source-code");
16
+ const {
17
+ codeToLines,
18
+ isRangesOverlap,
19
+ getTemplateTokens,
20
+ } = require("./utils/node");
7
21
 
8
22
  const MESSAGE_IDS = {
9
23
  UNEXPECTED: "unexpected",
@@ -42,52 +56,90 @@ module.exports = {
42
56
  },
43
57
 
44
58
  create(context) {
45
- const sourceCode = context.getSourceCode();
46
- const lines = sourceCode.lines;
47
59
  const max = context.options.length ? context.options[0].max : 2;
48
- return {
49
- /**
50
- * @param {ProgramNode} node
51
- */
52
- "Program:exit"(node) {
53
- /** @type {number[]} */
54
- const nonEmptyLineNumbers = [];
60
+ const sourceCode = getSourceCode(context);
55
61
 
56
- lines.forEach((line, index) => {
57
- if (line.trim().length > 0) {
58
- nonEmptyLineNumbers.push(index + 1);
59
- }
60
- });
62
+ /**
63
+ * @param {string[]} lines
64
+ * @param {number} lineOffset
65
+ * @param {((CommentContentNode | TextNode)['templates'][number])[]} tokens
66
+ */
67
+ function check(lines, lineOffset, tokens) {
68
+ /** @type {number[]} */
69
+ const nonEmptyLineNumbers = [];
61
70
 
62
- nonEmptyLineNumbers.forEach((current, index, arr) => {
63
- const before = arr[index - 1];
64
- if (typeof before === "number") {
65
- if (current - before - 1 > max) {
66
- context.report({
67
- node,
68
- loc: {
69
- start: { line: before, column: 0 },
70
- end: { line: current, column: 0 },
71
- },
72
- messageId: MESSAGE_IDS.UNEXPECTED,
73
- data: {
74
- max,
75
- },
76
- fix(fixer) {
77
- const start = sourceCode.getIndexFromLoc({
78
- line: before + 1,
79
- column: 0,
80
- });
81
- const end = sourceCode.getIndexFromLoc({
82
- line: current - max,
83
- column: 0,
84
- });
85
- return fixer.removeRange([start, end]);
86
- },
87
- });
71
+ lines.forEach((line, index) => {
72
+ if (line.trim().length > 0) {
73
+ nonEmptyLineNumbers.push(index + lineOffset);
74
+ }
75
+ });
76
+
77
+ nonEmptyLineNumbers.forEach((current, index, arr) => {
78
+ const before = arr[index - 1];
79
+ if (typeof before === "number") {
80
+ if (current - before - 1 > max) {
81
+ const start = sourceCode.getIndexFromLoc({
82
+ line: before + 1,
83
+ column: 0,
84
+ });
85
+ const end = sourceCode.getIndexFromLoc({
86
+ line: current - max,
87
+ column: 0,
88
+ });
89
+ if (
90
+ tokens.some((token) => isRangesOverlap(token.range, [start, end]))
91
+ ) {
92
+ return;
88
93
  }
94
+
95
+ context.report({
96
+ loc: {
97
+ start: { line: before, column: 0 },
98
+ end: { line: current, column: 0 },
99
+ },
100
+ messageId: MESSAGE_IDS.UNEXPECTED,
101
+ data: {
102
+ max,
103
+ },
104
+ fix(fixer) {
105
+ return fixer.removeRange([start, end]);
106
+ },
107
+ });
89
108
  }
90
- });
109
+ }
110
+ });
111
+ }
112
+ return {
113
+ "Document:exit"() {
114
+ check(sourceCode.lines, 1, []);
115
+ },
116
+ TaggedTemplateExpression(node) {
117
+ if (shouldCheckTaggedTemplateExpression(node, context)) {
118
+ const { html, tokens } = parse(
119
+ node.quasi,
120
+ getSourceCode(context),
121
+ {}
122
+ );
123
+ const lines = codeToLines(html);
124
+ check(
125
+ lines,
126
+ // @ts-ignore
127
+ node.quasi.loc.start.line,
128
+ getTemplateTokens(tokens)
129
+ );
130
+ }
131
+ },
132
+ TemplateLiteral(node) {
133
+ if (shouldCheckTemplateLiteral(node, context)) {
134
+ const { html, tokens } = parse(node, getSourceCode(context), {});
135
+ const lines = codeToLines(html);
136
+ check(
137
+ lines,
138
+ // @ts-ignore
139
+ node.loc.start.line,
140
+ getTemplateTokens(tokens)
141
+ );
142
+ }
91
143
  },
92
144
  };
93
145
  },
@@ -41,7 +41,7 @@ module.exports = {
41
41
  h1s.push(node);
42
42
  }
43
43
  },
44
- "Program:exit"() {
44
+ "Document:exit"() {
45
45
  if (h1s.length > 1) {
46
46
  h1s.forEach((h1) => {
47
47
  context.report({
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  const { RULE_CATEGORY, OBSOLETE_TAGS } = require("../constants");
6
+ const { createVisitors } = require("./utils/visitors");
6
7
 
7
8
  const OBSOLETE_TAGS_SET = new Set(OBSOLETE_TAGS);
8
9
 
@@ -31,7 +32,7 @@ module.exports = {
31
32
  },
32
33
 
33
34
  create(context) {
34
- return {
35
+ return createVisitors(context, {
35
36
  Tag(node) {
36
37
  if (OBSOLETE_TAGS_SET.has(node.name)) {
37
38
  context.report({
@@ -43,6 +44,6 @@ module.exports = {
43
44
  });
44
45
  }
45
46
  },
46
- };
47
+ });
47
48
  },
48
49
  };
@@ -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
  UNEXPECTED: "unexpected",
@@ -33,23 +34,28 @@ module.exports = {
33
34
  },
34
35
 
35
36
  create(context) {
36
- return {
37
- /**
38
- * @param {TagNode | StyleTagNode | ScriptTagNode} node
39
- */
40
- [["Tag", "StyleTag", "ScriptTag"].join(",")](node) {
41
- const tabIndexAttr = findAttr(node, "tabindex");
42
- if (
43
- tabIndexAttr &&
44
- tabIndexAttr.value &&
45
- parseInt(tabIndexAttr.value.value, 10) > 0
46
- ) {
47
- context.report({
48
- node: tabIndexAttr,
49
- messageId: MESSAGE_IDS.UNEXPECTED,
50
- });
51
- }
52
- },
53
- };
37
+ /**
38
+ * @param {TagNode | StyleTagNode | ScriptTagNode} node
39
+ */
40
+ function check(node) {
41
+ const tabIndexAttr = findAttr(node, "tabindex");
42
+ if (
43
+ tabIndexAttr &&
44
+ tabIndexAttr.value &&
45
+ !tabIndexAttr.value.templates.length &&
46
+ parseInt(tabIndexAttr.value.value, 10) > 0
47
+ ) {
48
+ context.report({
49
+ node: tabIndexAttr,
50
+ messageId: MESSAGE_IDS.UNEXPECTED,
51
+ });
52
+ }
53
+ }
54
+
55
+ return createVisitors(context, {
56
+ Tag: check,
57
+ StyleTag: check,
58
+ ScriptTag: check,
59
+ });
54
60
  },
55
61
  };