@html-eslint/eslint-plugin 0.10.0 → 0.13.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 (38) hide show
  1. package/lib/constants/node-types.js +0 -5
  2. package/lib/constants/obsolete-tags.js +1 -1
  3. package/lib/constants/rule-category.js +0 -5
  4. package/lib/constants/void-elements.js +1 -1
  5. package/lib/rules/element-newline.js +29 -9
  6. package/lib/rules/id-naming-convention.js +14 -7
  7. package/lib/rules/indent.js +29 -11
  8. package/lib/rules/index.js +6 -0
  9. package/lib/rules/no-abstract-roles.js +7 -0
  10. package/lib/rules/no-accesskey-attrs.js +45 -0
  11. package/lib/rules/no-aria-hidden-body.js +7 -0
  12. package/lib/rules/no-duplicate-attrs.js +7 -0
  13. package/lib/rules/no-duplicate-id.js +7 -0
  14. package/lib/rules/no-extra-spacing-attrs.js +35 -6
  15. package/lib/rules/no-inline-styles.js +7 -0
  16. package/lib/rules/no-multiple-empty-lines.js +90 -0
  17. package/lib/rules/no-multiple-h1.js +7 -0
  18. package/lib/rules/no-non-scalable-viewport.js +7 -0
  19. package/lib/rules/no-obsolete-tags.js +7 -0
  20. package/lib/rules/no-positive-tabindex.js +7 -0
  21. package/lib/rules/no-restricted-attrs.js +140 -0
  22. package/lib/rules/no-skip-heading-levels.js +7 -0
  23. package/lib/rules/no-target-blank.js +7 -0
  24. package/lib/rules/quotes.js +28 -0
  25. package/lib/rules/require-button-type.js +7 -0
  26. package/lib/rules/require-closing-tags.js +19 -2
  27. package/lib/rules/require-doctype.js +7 -0
  28. package/lib/rules/require-frame-title.js +7 -0
  29. package/lib/rules/require-img-alt.js +14 -1
  30. package/lib/rules/require-lang.js +7 -1
  31. package/lib/rules/require-li-container.js +13 -1
  32. package/lib/rules/require-meta-charset.js +12 -1
  33. package/lib/rules/require-meta-description.js +11 -0
  34. package/lib/rules/require-meta-viewport.js +12 -8
  35. package/lib/rules/require-title.js +9 -3
  36. package/lib/rules/utils/node-utils.js +27 -5
  37. package/lib/types.d.ts +105 -33
  38. package/package.json +8 -4
@@ -1,8 +1,3 @@
1
- /**
2
- * @typedef {import("../types").NodeTypes} NodeTypes
3
- */
4
-
5
- /** @type {NodeTypes} */
6
1
  module.exports = {
7
2
  PROGRAM: "Program",
8
3
  TEXT: "text",
@@ -1,5 +1,5 @@
1
1
  // https://html.spec.whatwg.org/#non-conforming-features
2
- /** @type {string[] */
2
+ /** @type {string[]} */
3
3
  module.exports = [
4
4
  "applet",
5
5
  "acronym",
@@ -1,8 +1,3 @@
1
- /**
2
- * @typedef {import("../types").RuleCategory} RuleCategory
3
- */
4
-
5
- /** @type {RuleCategory} */
6
1
  module.exports = {
7
2
  BEST_PRACTICE: "Best Practice",
8
3
  SEO: "SEO",
@@ -1,4 +1,4 @@
1
- /** @type {string[] */
1
+ /** @type {string[]} */
2
2
  module.exports = [
3
3
  "area",
4
4
  "base",
@@ -1,5 +1,7 @@
1
1
  /**
2
- * @typedef {import("../types").HTMLNode} HTMLNode
2
+ * @typedef {import("../types").ElementNode} ElementNode
3
+ * @typedef {import("../types").AnyNode} AnyNode
4
+ * @typedef {import("../types").Context} Context
3
5
  */
4
6
 
5
7
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
@@ -29,9 +31,12 @@ module.exports = {
29
31
  },
30
32
  },
31
33
 
34
+ /**
35
+ * @param {Context} context
36
+ */
32
37
  create(context) {
33
- function checkSiblings(sibilings) {
34
- sibilings
38
+ function checkSiblings(siblings) {
39
+ siblings
35
40
  .filter((node) => node.type !== NODE_TYPES.TEXT && node.range[0])
36
41
  .forEach((current, index, arr) => {
37
42
  const after = arr[index + 1];
@@ -50,6 +55,10 @@ module.exports = {
50
55
  });
51
56
  }
52
57
 
58
+ /**
59
+ *
60
+ * @param {ElementNode['childNodes'][number]} node
61
+ */
53
62
  function checkChild(node) {
54
63
  const children = (node.childNodes || []).filter(
55
64
  (n) => !!n.range[0] && n.type !== NODE_TYPES.TEXT
@@ -57,31 +66,40 @@ module.exports = {
57
66
  const first = children[0];
58
67
  const last = children[children.length - 1];
59
68
  if (first) {
60
- if (isOnTheSameLine(node.startTag, first)) {
69
+ if (node.startTag && isOnTheSameLine(node.startTag, first)) {
61
70
  context.report({
62
71
  node: node.startTag,
63
72
  messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
64
73
  data: { tag: `<${node.tagName}>` },
65
74
  fix(fixer) {
66
- return fixer.insertTextAfter(node.startTag, "\n");
75
+ if (node.startTag) {
76
+ return fixer.insertTextAfter(node.startTag, "\n");
77
+ }
78
+ return null;
67
79
  },
68
80
  });
69
81
  }
70
82
  }
71
83
  if (last) {
72
- if (isOnTheSameLine(node.endTag, last)) {
84
+ if (node.endTag && isOnTheSameLine(node.endTag, last)) {
73
85
  context.report({
74
86
  node: node.endTag,
75
87
  messageId: MESSAGE_IDS.EXPECT_NEW_LINE_BEFORE,
76
88
  data: { tag: `</${node.tagName}>` },
77
89
  fix(fixer) {
78
- return fixer.insertTextBefore(node.endTag, "\n");
90
+ if (node.endTag) {
91
+ return fixer.insertTextBefore(node.endTag, "\n");
92
+ }
93
+ return null;
79
94
  },
80
95
  });
81
96
  }
82
97
  }
83
98
  }
84
99
  return {
100
+ /**
101
+ * @param {ElementNode} node
102
+ */
85
103
  "*"(node) {
86
104
  if (node.type !== NODE_TYPES.TEXT) {
87
105
  checkSiblings(node.childNodes || []);
@@ -94,13 +112,15 @@ module.exports = {
94
112
 
95
113
  /**
96
114
  * Checks whether two nodes are on the same line or not.
97
- * @param {HTMLNode} nodeBefore A node before
98
- * @param {HTMLNode} nodeAfter A node after
115
+ * @param {AnyNode} nodeBefore A node before
116
+ * @param {AnyNode} nodeAfter A node after
99
117
  * @returns {boolean} `true` if two nodes are on the same line, otherwise `false`.
100
118
  */
101
119
  function isOnTheSameLine(nodeBefore, nodeAfter) {
102
120
  if (nodeBefore && nodeAfter) {
121
+ // @ts-ignore
103
122
  if (nodeBefore.endTag) {
123
+ // @ts-ignore
104
124
  return nodeBefore.endTag.loc.end.line === nodeAfter.loc.start.line;
105
125
  }
106
126
  return nodeBefore.loc.start.line === nodeAfter.loc.start.line;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NamingUtils, NodeUtils } = require("./utils");
3
7
 
@@ -5,7 +9,7 @@ const MESSAGE_IDS = {
5
9
  WRONG: "wrong",
6
10
  };
7
11
 
8
- const CONEVNTIONS = {
12
+ const CONVENTIONS = {
9
13
  CAMEL_CASE: "camelCase",
10
14
  SNAKE_CASE: "snake_case",
11
15
  PASCAL_CASE: "PascalCase",
@@ -13,12 +17,15 @@ const CONEVNTIONS = {
13
17
  };
14
18
 
15
19
  const CONVENTION_CHECKERS = {
16
- [CONEVNTIONS.CAMEL_CASE]: NamingUtils.isCamelCase,
17
- [CONEVNTIONS.SNAKE_CASE]: NamingUtils.isSnakeCase,
18
- [CONEVNTIONS.PASCAL_CASE]: NamingUtils.isPascalCase,
19
- [CONEVNTIONS.KEBAB_CASE]: NamingUtils.isKebabCase,
20
+ [CONVENTIONS.CAMEL_CASE]: NamingUtils.isCamelCase,
21
+ [CONVENTIONS.SNAKE_CASE]: NamingUtils.isSnakeCase,
22
+ [CONVENTIONS.PASCAL_CASE]: NamingUtils.isPascalCase,
23
+ [CONVENTIONS.KEBAB_CASE]: NamingUtils.isKebabCase,
20
24
  };
21
25
 
26
+ /**
27
+ * @type {Rule}
28
+ */
22
29
  module.exports = {
23
30
  meta: {
24
31
  type: "code",
@@ -32,7 +39,7 @@ module.exports = {
32
39
  fixable: null,
33
40
  schema: [
34
41
  {
35
- enum: Object.values(CONEVNTIONS),
42
+ enum: Object.values(CONVENTIONS),
36
43
  },
37
44
  ],
38
45
  messages: {
@@ -45,7 +52,7 @@ module.exports = {
45
52
  const convention =
46
53
  context.options && context.options.length
47
54
  ? context.options[0]
48
- : CONEVNTIONS.SNAKE_CASE;
55
+ : CONVENTIONS.SNAKE_CASE;
49
56
 
50
57
  const checkNaming = CONVENTION_CHECKERS[convention];
51
58
 
@@ -1,7 +1,9 @@
1
- // @ts-check
2
1
  /**
3
- * @typedef {import("../types").HTMLNode} HTMLNode
2
+ * @typedef {import("../types").Rule} Rule
3
+ * @typedef {import("../types").ElementNode} ElementNode
4
4
  * @typedef {import("../types").AttrNode} AttrNode
5
+ * @typedef {import("../types").TagNode} TagNode
6
+ * @typedef {import("../types").AnyNode} AnyNode
5
7
  * @typedef {import("../types").BaseNode} BaseNode
6
8
  * @typedef {Object} IndentType
7
9
  * @property {"tab"} TAB
@@ -12,6 +14,7 @@
12
14
  */
13
15
 
14
16
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
17
+ const { NodeUtils } = require("./utils");
15
18
 
16
19
  /** @type {MessageId} */
17
20
  const MESSAGE_ID = {
@@ -31,6 +34,9 @@ const IGNORING_NODES = [
31
34
  NODE_TYPES.XMP,
32
35
  ];
33
36
 
37
+ /**
38
+ * @type {Rule}
39
+ */
34
40
  module.exports = {
35
41
  meta: {
36
42
  type: "code",
@@ -60,7 +66,6 @@ module.exports = {
60
66
  "Expected indentation of {{expected}} but found {{actual}}.",
61
67
  },
62
68
  },
63
-
64
69
  create(context) {
65
70
  const sourceCode = context.getSourceCode();
66
71
  const indentLevel = new IndentLevel();
@@ -79,7 +84,7 @@ module.exports = {
79
84
 
80
85
  /**
81
86
  * @param {BaseNode} node
82
- * @param {HTMLNode} [nodeToReport]
87
+ * @param {BaseNode} [nodeToReport]
83
88
  */
84
89
  function checkIndent(node, nodeToReport) {
85
90
  const codeBefore = getLineCodeBefore(node);
@@ -126,14 +131,19 @@ module.exports = {
126
131
  }
127
132
 
128
133
  /**
134
+ * @param {AnyNode} startTag
129
135
  * @param {AttrNode[]} attrs
130
136
  */
131
- function checkAttrsIndent(attrs) {
132
- attrs.forEach((attr) => checkIndent(attr));
137
+ function checkAttrsIndent(startTag, attrs) {
138
+ attrs.forEach((attr) => {
139
+ if (attr.loc.start.line !== startTag.loc.start.line) {
140
+ checkIndent(attr);
141
+ }
142
+ });
133
143
  }
134
144
 
135
145
  /**
136
- * @param {BaseNode} startTag
146
+ * @param {AnyNode} startTag
137
147
  */
138
148
  function checkEndOfStartTag(startTag) {
139
149
  const start = startTag.range[1] - 1;
@@ -141,6 +151,7 @@ module.exports = {
141
151
  const line = startTag.loc.end.line;
142
152
  const endCol = startTag.loc.end.column;
143
153
  const startCol = startTag.loc.end.column - 1;
154
+
144
155
  checkIndent({
145
156
  range: [start, end],
146
157
  start,
@@ -160,7 +171,7 @@ module.exports = {
160
171
  let nodesToIgnoreChildren = [];
161
172
  return {
162
173
  /**
163
- * @param {HTMLNode} node
174
+ * @param {ElementNode} node
164
175
  */
165
176
  "*"(node) {
166
177
  if (IGNORING_NODES.includes(node.type)) {
@@ -173,8 +184,8 @@ module.exports = {
173
184
 
174
185
  indentLevel.up();
175
186
 
176
- if (Array.isArray(node.attrs)) {
177
- checkAttrsIndent(node.attrs);
187
+ if (node.startTag && Array.isArray(node.attrs)) {
188
+ checkAttrsIndent(node.startTag, node.attrs);
178
189
  }
179
190
 
180
191
  (node.childNodes || []).forEach((current) => {
@@ -186,7 +197,11 @@ module.exports = {
186
197
  }
187
198
  });
188
199
 
189
- if (node.lineNodes && node.lineNodes.length) {
200
+ if (
201
+ (NodeUtils.isTextNode(node) || NodeUtils.isCommentNode(node)) &&
202
+ node.lineNodes &&
203
+ node.lineNodes.length
204
+ ) {
190
205
  if (!node.startTag) {
191
206
  indentLevel.down();
192
207
  }
@@ -219,6 +234,9 @@ module.exports = {
219
234
  };
220
235
 
221
236
  function getIndentTypeAndSize(options) {
237
+ /**
238
+ * @type {IndentType['SPACE'] | IndentType['TAB']}
239
+ */
222
240
  let indentType = INDENT_TYPES.SPACE;
223
241
  let indentSize = 4;
224
242
  if (options.length) {
@@ -25,6 +25,9 @@ const noDuplicateAttrs = require("./no-duplicate-attrs");
25
25
  const noAbstractRoles = require("./no-abstract-roles");
26
26
  const requireButtonType = require("./require-button-type");
27
27
  const noAriaHiddenBody = require("./no-aria-hidden-body");
28
+ const noMultipleEmptyLines = require("./no-multiple-empty-lines");
29
+ const noAccesskeyAttrs = require("./no-accesskey-attrs");
30
+ const noRestrictedAttrs = require("./no-restricted-attrs");
28
31
 
29
32
  module.exports = {
30
33
  "require-lang": requireLang,
@@ -54,4 +57,7 @@ module.exports = {
54
57
  "no-abstract-roles": noAbstractRoles,
55
58
  "require-button-type": requireButtonType,
56
59
  "no-aria-hidden-body": noAriaHiddenBody,
60
+ "no-multiple-empty-lines": noMultipleEmptyLines,
61
+ "no-accesskey-attrs": noAccesskeyAttrs,
62
+ "no-restricted-attrs": noRestrictedAttrs,
57
63
  };
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -20,6 +24,9 @@ const ABSTRACT_ROLE_SET = new Set([
20
24
  "window",
21
25
  ]);
22
26
 
27
+ /**
28
+ * @type {Rule}
29
+ */
23
30
  module.exports = {
24
31
  meta: {
25
32
  type: "code",
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
5
+ const { RULE_CATEGORY } = require("../constants");
6
+ const { NodeUtils } = require("./utils");
7
+
8
+ const MESSAGE_IDS = {
9
+ UNEXPECTED: "unexpected",
10
+ };
11
+
12
+ /**
13
+ * @type {Rule}
14
+ */
15
+ module.exports = {
16
+ meta: {
17
+ type: "code",
18
+
19
+ docs: {
20
+ description: "Disallow to use of accesskey attribute",
21
+ category: RULE_CATEGORY.ACCESSIBILITY,
22
+ recommended: false,
23
+ },
24
+
25
+ fixable: null,
26
+ schema: [],
27
+ messages: {
28
+ [MESSAGE_IDS.UNEXPECTED]: "Unexpected use of accesskey attribute.",
29
+ },
30
+ },
31
+
32
+ create(context) {
33
+ return {
34
+ "*"(node) {
35
+ const accessKeyAttr = NodeUtils.findAttr(node, "accesskey");
36
+ if (accessKeyAttr) {
37
+ context.report({
38
+ node: accessKeyAttr,
39
+ messageId: MESSAGE_IDS.UNEXPECTED,
40
+ });
41
+ }
42
+ },
43
+ };
44
+ },
45
+ };
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -5,6 +9,9 @@ const MESSAGE_IDS = {
5
9
  UNEXPECTED: "unexpected",
6
10
  };
7
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
8
15
  module.exports = {
9
16
  meta: {
10
17
  type: "code",
@@ -1,9 +1,16 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
 
3
7
  const MESSAGE_IDS = {
4
8
  DUPLICATE_ATTRS: "duplicateAttrs",
5
9
  };
6
10
 
11
+ /**
12
+ * @type {Rule}
13
+ */
7
14
  module.exports = {
8
15
  meta: {
9
16
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -5,6 +9,9 @@ const MESSAGE_IDS = {
5
9
  DUPLICATE_ID: "duplicateId",
6
10
  };
7
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
8
15
  module.exports = {
9
16
  meta: {
10
17
  type: "code",
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ * @typedef {import("../types").AttrNode} AttrNode
4
+ * @typedef {import("../types").TagNode} TagNode
5
+ * @typedef {import("../types").ElementNode} ElementNode
6
+ */
7
+
1
8
  const { RULE_CATEGORY } = require("../constants");
2
9
 
3
10
  const MESSAGE_IDS = {
@@ -6,6 +13,9 @@ const MESSAGE_IDS = {
6
13
  EXTRA_BEFORE: "unexpectedBefore",
7
14
  };
8
15
 
16
+ /**
17
+ * @type {Rule}
18
+ */
9
19
  module.exports = {
10
20
  meta: {
11
21
  type: "code",
@@ -25,6 +35,9 @@ module.exports = {
25
35
  },
26
36
  },
27
37
  create(context) {
38
+ /**
39
+ * @param {AttrNode[]} attrs
40
+ */
28
41
  function checkExtraSpacesBetweenAttrs(attrs) {
29
42
  attrs.forEach((current, index, attrs) => {
30
43
  if (index >= attrs.length - 1) {
@@ -50,12 +63,21 @@ module.exports = {
50
63
  }
51
64
  });
52
65
  }
53
- function checkExtraSpaceAfter(startTag, lastAttr) {
66
+ /**
67
+ * @param {TagNode} startTag
68
+ * @param {AttrNode} lastAttr
69
+ * @param {boolean} isSelfClosed
70
+ */
71
+ function checkExtraSpaceAfter(startTag, lastAttr, isSelfClosed) {
54
72
  if (startTag.loc.end.line !== lastAttr.loc.end.line) {
55
- // skip the attribute on the diffrent line with the start tag
73
+ // skip the attribute on the different line with the start tag
56
74
  return;
57
75
  }
58
- const spacesBetween = startTag.loc.end.column - lastAttr.loc.end.column;
76
+ let spacesBetween = startTag.loc.end.column - lastAttr.loc.end.column;
77
+ if (isSelfClosed) {
78
+ spacesBetween--;
79
+ }
80
+
59
81
  if (spacesBetween > 1) {
60
82
  context.report({
61
83
  loc: {
@@ -66,15 +88,20 @@ module.exports = {
66
88
  fix(fixer) {
67
89
  return fixer.removeRange([
68
90
  lastAttr.range[1],
69
- startTag.range[1] - 1,
91
+ lastAttr.range[1] + spacesBetween - 1,
70
92
  ]);
71
93
  },
72
94
  });
73
95
  }
74
96
  }
97
+
98
+ /**
99
+ * @param {ElementNode} node
100
+ * @param {AttrNode} firstAttr
101
+ */
75
102
  function checkExtraSpaceBefore(node, firstAttr) {
76
103
  if (node.loc.start.line !== firstAttr.loc.start.line) {
77
- // skip the attribute on the diffrent line with the start tag
104
+ // skip the attribute on the different line with the start tag
78
105
  return;
79
106
  }
80
107
  const nodeLength = node.tagName.length;
@@ -103,9 +130,11 @@ module.exports = {
103
130
  checkExtraSpaceBefore(node, node.attrs[0]);
104
131
  }
105
132
  if (node.startTag && node.attrs && node.attrs.length > 0) {
133
+ const isSelfClosed = !node.endTag;
106
134
  checkExtraSpaceAfter(
107
135
  node.startTag,
108
- node.attrs[node.attrs.length - 1]
136
+ node.attrs[node.attrs.length - 1],
137
+ isSelfClosed
109
138
  );
110
139
  }
111
140
  checkExtraSpacesBetweenAttrs(node.attrs || []);
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -5,6 +9,9 @@ const MESSAGE_IDS = {
5
9
  INLINE_STYLE: "unexpectedInlineStyle",
6
10
  };
7
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
8
15
  module.exports = {
9
16
  meta: {
10
17
  type: "code",
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
5
+ const { RULE_CATEGORY } = require("../constants");
6
+
7
+ const MESSAGE_IDS = {
8
+ UNEXPECTED: "unexpected",
9
+ };
10
+
11
+ /**
12
+ * @type {Rule}
13
+ */
14
+ module.exports = {
15
+ meta: {
16
+ type: "code",
17
+
18
+ docs: {
19
+ description: "Disallow multiple empty lines",
20
+ category: RULE_CATEGORY.STYLE,
21
+ recommended: false,
22
+ },
23
+
24
+ fixable: "whitespace",
25
+ schema: [
26
+ {
27
+ type: "object",
28
+ properties: {
29
+ max: {
30
+ type: "integer",
31
+ minimum: 0,
32
+ },
33
+ },
34
+ required: ["max"],
35
+ additionalProperties: false,
36
+ },
37
+ ],
38
+ messages: {
39
+ [MESSAGE_IDS.UNEXPECTED]: "More than {{max}} blank lines not allowed.",
40
+ },
41
+ },
42
+
43
+ create(context) {
44
+ const sourceCode = context.getSourceCode();
45
+ const lines = sourceCode.lines;
46
+ const max = context.options.length ? context.options[0].max : 2;
47
+ return {
48
+ "Program:exit"(node) {
49
+ /** @type {number[]} */
50
+ const nonEmptyLineNumbers = [];
51
+
52
+ lines.forEach((line, index) => {
53
+ if (line.trim().length > 0) {
54
+ nonEmptyLineNumbers.push(index + 1);
55
+ }
56
+ });
57
+
58
+ nonEmptyLineNumbers.forEach((current, index, arr) => {
59
+ const before = arr[index - 1];
60
+ if (typeof before === "number") {
61
+ if (current - before - 1 > max) {
62
+ context.report({
63
+ node,
64
+ loc: {
65
+ start: { line: before, column: 0 },
66
+ end: { line: current, column: 0 },
67
+ },
68
+ messageId: MESSAGE_IDS.UNEXPECTED,
69
+ data: {
70
+ max,
71
+ },
72
+ fix(fixer) {
73
+ const start = sourceCode.getIndexFromLoc({
74
+ line: before + 1,
75
+ column: 0,
76
+ });
77
+ const end = sourceCode.getIndexFromLoc({
78
+ line: current - max,
79
+ column: 0,
80
+ });
81
+ return fixer.removeRange([start, end]);
82
+ },
83
+ });
84
+ }
85
+ }
86
+ });
87
+ },
88
+ };
89
+ },
90
+ };
@@ -1,9 +1,16 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
 
3
7
  const MESSAGE_IDS = {
4
8
  MULTIPLE_H1: "unexpectedMultiH1",
5
9
  };
6
10
 
11
+ /**
12
+ * @type {Rule}
13
+ */
7
14
  module.exports = {
8
15
  meta: {
9
16
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -5,6 +9,9 @@ const MESSAGE_IDS = {
5
9
  UNEXPECTED: "unexpected",
6
10
  };
7
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
8
15
  module.exports = {
9
16
  meta: {
10
17
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
2
6
  const { OBSOLETE_TAGS } = require("../constants");
3
7
 
@@ -7,6 +11,9 @@ const MESSAGE_IDS = {
7
11
  UNEXPECTED: "unexpected",
8
12
  };
9
13
 
14
+ /**
15
+ * @type {Rule}
16
+ */
10
17
  module.exports = {
11
18
  meta: {
12
19
  type: "code",