@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.
- package/lib/constants/node-types.js +0 -5
- package/lib/constants/obsolete-tags.js +1 -1
- package/lib/constants/rule-category.js +0 -5
- package/lib/constants/void-elements.js +1 -1
- package/lib/rules/element-newline.js +29 -9
- package/lib/rules/id-naming-convention.js +14 -7
- package/lib/rules/indent.js +29 -11
- package/lib/rules/index.js +6 -0
- package/lib/rules/no-abstract-roles.js +7 -0
- package/lib/rules/no-accesskey-attrs.js +45 -0
- package/lib/rules/no-aria-hidden-body.js +7 -0
- package/lib/rules/no-duplicate-attrs.js +7 -0
- package/lib/rules/no-duplicate-id.js +7 -0
- package/lib/rules/no-extra-spacing-attrs.js +35 -6
- package/lib/rules/no-inline-styles.js +7 -0
- package/lib/rules/no-multiple-empty-lines.js +90 -0
- package/lib/rules/no-multiple-h1.js +7 -0
- package/lib/rules/no-non-scalable-viewport.js +7 -0
- package/lib/rules/no-obsolete-tags.js +7 -0
- package/lib/rules/no-positive-tabindex.js +7 -0
- package/lib/rules/no-restricted-attrs.js +140 -0
- package/lib/rules/no-skip-heading-levels.js +7 -0
- package/lib/rules/no-target-blank.js +7 -0
- package/lib/rules/quotes.js +28 -0
- package/lib/rules/require-button-type.js +7 -0
- package/lib/rules/require-closing-tags.js +19 -2
- package/lib/rules/require-doctype.js +7 -0
- package/lib/rules/require-frame-title.js +7 -0
- package/lib/rules/require-img-alt.js +14 -1
- package/lib/rules/require-lang.js +7 -1
- package/lib/rules/require-li-container.js +13 -1
- package/lib/rules/require-meta-charset.js +12 -1
- package/lib/rules/require-meta-description.js +11 -0
- package/lib/rules/require-meta-viewport.js +12 -8
- package/lib/rules/require-title.js +9 -3
- package/lib/rules/utils/node-utils.js +27 -5
- package/lib/types.d.ts +105 -33
- package/package.json +8 -4
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../types").
|
|
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(
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
98
|
-
* @param {
|
|
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
|
|
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
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
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(
|
|
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
|
-
:
|
|
55
|
+
: CONVENTIONS.SNAKE_CASE;
|
|
49
56
|
|
|
50
57
|
const checkNaming = CONVENTION_CHECKERS[convention];
|
|
51
58
|
|
package/lib/rules/indent.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
// @ts-check
|
|
2
1
|
/**
|
|
3
|
-
* @typedef {import("../types").
|
|
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 {
|
|
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) =>
|
|
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 {
|
|
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 {
|
|
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 (
|
|
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) {
|
package/lib/rules/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
73
|
+
// skip the attribute on the different line with the start tag
|
|
56
74
|
return;
|
|
57
75
|
}
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
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",
|