@html-eslint/eslint-plugin 0.20.0 → 0.22.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/index.js +0 -2
- package/lib/rules/element-newline.js +30 -11
- package/lib/rules/id-naming-convention.js +30 -12
- package/lib/rules/indent.js +7 -10
- package/lib/rules/index.js +8 -0
- package/lib/rules/lowercase.js +93 -0
- package/lib/rules/no-abstract-roles.js +15 -13
- package/lib/rules/no-accesskey-attrs.js +5 -6
- package/lib/rules/no-aria-hidden-body.js +2 -6
- package/lib/rules/no-duplicate-attrs.js +3 -4
- package/lib/rules/no-duplicate-id.js +7 -7
- package/lib/rules/no-extra-spacing-attrs.js +29 -9
- package/lib/rules/no-inline-styles.js +5 -2
- package/lib/rules/no-multiple-empty-lines.js +3 -4
- package/lib/rules/no-multiple-h1.js +3 -4
- package/lib/rules/no-non-scalable-viewport.js +3 -7
- package/lib/rules/no-obsolete-tags.js +4 -2
- package/lib/rules/no-positive-tabindex.js +5 -6
- package/lib/rules/no-restricted-attr-values.js +9 -3
- package/lib/rules/no-restricted-attrs.js +13 -2
- package/lib/rules/no-script-style-type.js +69 -0
- package/lib/rules/no-skip-heading-levels.js +4 -5
- package/lib/rules/no-target-blank.js +5 -9
- package/lib/rules/no-trailing-spaces.js +0 -4
- package/lib/rules/quotes.js +24 -1
- package/lib/rules/require-attrs.js +18 -7
- package/lib/rules/require-button-type.js +2 -6
- package/lib/rules/require-closing-tags.js +8 -5
- package/lib/rules/require-doctype.js +4 -5
- package/lib/rules/require-frame-title.js +5 -2
- package/lib/rules/require-img-alt.js +10 -0
- package/lib/rules/require-lang.js +5 -6
- package/lib/rules/require-li-container.js +9 -2
- package/lib/rules/require-meta-charset.js +19 -13
- package/lib/rules/require-meta-description.js +9 -12
- package/lib/rules/require-meta-viewport.js +19 -20
- package/lib/rules/require-open-graph-protocol.js +135 -0
- package/lib/rules/require-title.js +19 -25
- package/lib/rules/sort-attrs.js +158 -0
- package/lib/rules/utils/array.js +26 -0
- package/lib/rules/utils/node.js +70 -0
- package/lib/types.d.ts +262 -246
- package/package.json +4 -4
- package/lib/constants/node-types.js +0 -13
- package/lib/rules/utils/index.js +0 -7
- package/lib/rules/utils/node-utils.js +0 -112
- /package/lib/rules/utils/{naming-utils.js → naming.js} +0 -0
package/lib/constants/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
const NODE_TYPES = require("./node-types");
|
|
2
1
|
const RULE_CATEGORY = require("./rule-category");
|
|
3
2
|
const OBSOLETE_TAGS = require("./obsolete-tags");
|
|
4
3
|
const VOID_ELEMENTS = require("./void-elements");
|
|
5
4
|
|
|
6
5
|
module.exports = {
|
|
7
|
-
NODE_TYPES,
|
|
8
6
|
RULE_CATEGORY,
|
|
9
7
|
OBSOLETE_TAGS,
|
|
10
8
|
VOID_ELEMENTS,
|
|
@@ -5,6 +5,9 @@ const MESSAGE_IDS = {
|
|
|
5
5
|
EXPECT_NEW_LINE_BEFORE: "expectBefore",
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @type {Rule}
|
|
10
|
+
*/
|
|
8
11
|
module.exports = {
|
|
9
12
|
meta: {
|
|
10
13
|
type: "code",
|
|
@@ -40,9 +43,10 @@ module.exports = {
|
|
|
40
43
|
create(context) {
|
|
41
44
|
const option = context.options[0] || { skip: [] };
|
|
42
45
|
const skipTags = option.skip;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
let skipTagCount = 0;
|
|
47
|
+
/**
|
|
48
|
+
* @param {ChildType<TagNode | ProgramNode>[]} siblings
|
|
49
|
+
*/
|
|
46
50
|
function checkSiblings(siblings) {
|
|
47
51
|
siblings
|
|
48
52
|
.filter((node) => node.type !== "Text")
|
|
@@ -53,6 +57,7 @@ module.exports = {
|
|
|
53
57
|
context.report({
|
|
54
58
|
node: current,
|
|
55
59
|
messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
|
|
60
|
+
// @ts-ignore
|
|
56
61
|
data: { tag: `<${current.name}>` },
|
|
57
62
|
fix(fixer) {
|
|
58
63
|
return fixer.insertTextAfter(current, "\n");
|
|
@@ -63,6 +68,10 @@ module.exports = {
|
|
|
63
68
|
});
|
|
64
69
|
}
|
|
65
70
|
|
|
71
|
+
/**
|
|
72
|
+
* @param {TagNode} node
|
|
73
|
+
* @param {ChildType<TagNode>[]} children
|
|
74
|
+
*/
|
|
66
75
|
function checkChild(node, children) {
|
|
67
76
|
const targetChildren = children.filter((n) => n.type !== "Text");
|
|
68
77
|
const first = targetChildren[0];
|
|
@@ -94,22 +103,27 @@ module.exports = {
|
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
return {
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
Program(node) {
|
|
107
|
+
checkSiblings(node.body);
|
|
108
|
+
},
|
|
109
|
+
Tag(node) {
|
|
110
|
+
if (skipTagCount > 0) {
|
|
99
111
|
return;
|
|
100
112
|
}
|
|
101
|
-
|
|
102
|
-
const children = node.type === "Program" ? node.body : node.children;
|
|
103
|
-
checkSiblings(children);
|
|
104
113
|
if (skipTags.includes(node.name)) {
|
|
105
|
-
|
|
114
|
+
skipTagCount++;
|
|
106
115
|
return;
|
|
107
116
|
}
|
|
108
|
-
|
|
117
|
+
checkSiblings(node.children);
|
|
118
|
+
checkChild(node, node.children);
|
|
109
119
|
},
|
|
120
|
+
/**
|
|
121
|
+
* @param {TagNode} node
|
|
122
|
+
* @returns
|
|
123
|
+
*/
|
|
110
124
|
"Tag:exit"(node) {
|
|
111
125
|
if (skipTags.includes(node.name)) {
|
|
112
|
-
|
|
126
|
+
skipTagCount--;
|
|
113
127
|
return;
|
|
114
128
|
}
|
|
115
129
|
},
|
|
@@ -117,6 +131,11 @@ module.exports = {
|
|
|
117
131
|
},
|
|
118
132
|
};
|
|
119
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @param {BaseNode} nodeBefore
|
|
136
|
+
* @param {BaseNode} nodeAfter
|
|
137
|
+
* @returns
|
|
138
|
+
*/
|
|
120
139
|
function isOnTheSameLine(nodeBefore, nodeAfter) {
|
|
121
140
|
if (nodeBefore && nodeAfter) {
|
|
122
141
|
return nodeBefore.loc.end.line === nodeAfter.loc.start.line;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
isCamelCase,
|
|
4
|
+
isSnakeCase,
|
|
5
|
+
isPascalCase,
|
|
6
|
+
isKebabCase,
|
|
7
|
+
} = require("./utils/naming");
|
|
8
|
+
const { findAttr } = require("./utils/node");
|
|
7
9
|
|
|
8
10
|
const MESSAGE_IDS = {
|
|
9
11
|
WRONG: "wrong",
|
|
@@ -14,13 +16,14 @@ const CONVENTIONS = {
|
|
|
14
16
|
SNAKE_CASE: "snake_case",
|
|
15
17
|
PASCAL_CASE: "PascalCase",
|
|
16
18
|
KEBAB_CASE: "kebab-case",
|
|
19
|
+
REGEX: "regex",
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
const CONVENTION_CHECKERS = {
|
|
20
|
-
[CONVENTIONS.CAMEL_CASE]:
|
|
21
|
-
[CONVENTIONS.SNAKE_CASE]:
|
|
22
|
-
[CONVENTIONS.PASCAL_CASE]:
|
|
23
|
-
[CONVENTIONS.KEBAB_CASE]:
|
|
23
|
+
[CONVENTIONS.CAMEL_CASE]: isCamelCase,
|
|
24
|
+
[CONVENTIONS.SNAKE_CASE]: isSnakeCase,
|
|
25
|
+
[CONVENTIONS.PASCAL_CASE]: isPascalCase,
|
|
26
|
+
[CONVENTIONS.KEBAB_CASE]: isKebabCase,
|
|
24
27
|
};
|
|
25
28
|
|
|
26
29
|
/**
|
|
@@ -41,6 +44,13 @@ module.exports = {
|
|
|
41
44
|
{
|
|
42
45
|
enum: Object.values(CONVENTIONS),
|
|
43
46
|
},
|
|
47
|
+
{
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
pattern: { type: "string" },
|
|
51
|
+
},
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
},
|
|
44
54
|
],
|
|
45
55
|
messages: {
|
|
46
56
|
[MESSAGE_IDS.WRONG]:
|
|
@@ -54,14 +64,22 @@ module.exports = {
|
|
|
54
64
|
? context.options[0]
|
|
55
65
|
: CONVENTIONS.SNAKE_CASE;
|
|
56
66
|
|
|
57
|
-
const checkNaming =
|
|
67
|
+
const checkNaming =
|
|
68
|
+
convention === CONVENTIONS.REGEX
|
|
69
|
+
? (/** @type string */ name) =>
|
|
70
|
+
new RegExp(context.options[1].pattern).test(name)
|
|
71
|
+
: CONVENTION_CHECKERS[convention];
|
|
58
72
|
|
|
59
73
|
return {
|
|
60
|
-
|
|
74
|
+
/**
|
|
75
|
+
* @param {TagNode | ScriptTagNode | StyleTagNode} node
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
[["Tag", "ScriptTag", "StyleTag"].join(",")](node) {
|
|
61
79
|
if (!node.attributes || node.attributes.length <= 0) {
|
|
62
80
|
return;
|
|
63
81
|
}
|
|
64
|
-
const idAttr =
|
|
82
|
+
const idAttr = findAttr(node, "id");
|
|
65
83
|
if (idAttr && idAttr.value && !checkNaming(idAttr.value.value)) {
|
|
66
84
|
context.report({
|
|
67
85
|
node: idAttr,
|
package/lib/rules/indent.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
* @typedef {import("../types").TagNode} TagNode
|
|
4
|
-
* @typedef {import("../types").BaseNode} BaseNode
|
|
5
|
-
* @typedef {import("../types").OpenTagStartNode} OpenTagStartNode
|
|
6
|
-
* @typedef {import("../types").CloseTagNode} CloseTagNode
|
|
7
|
-
* @typedef {import("../types").LineNode} LineNode
|
|
8
|
-
*@typedef {import("../types").AnyNode} AnyNode
|
|
9
2
|
* @typedef {Object} IndentType
|
|
10
3
|
* @property {"tab"} TAB
|
|
11
4
|
* @property {"space"} SPACE
|
|
@@ -13,8 +6,9 @@
|
|
|
13
6
|
* @typedef {Object} MessageId
|
|
14
7
|
* @property {"wrongIndent"} WRONG_INDENT
|
|
15
8
|
*/
|
|
9
|
+
|
|
16
10
|
const { RULE_CATEGORY } = require("../constants");
|
|
17
|
-
const {
|
|
11
|
+
const { splitToLineNodes } = require("./utils/node");
|
|
18
12
|
|
|
19
13
|
/** @type {MessageId} */
|
|
20
14
|
const MESSAGE_ID = {
|
|
@@ -234,6 +228,9 @@ module.exports = {
|
|
|
234
228
|
OpenTagStart: checkIndent,
|
|
235
229
|
OpenTagEnd: checkIndent,
|
|
236
230
|
CloseTag: checkIndent,
|
|
231
|
+
/**
|
|
232
|
+
* @param {TagNode} node
|
|
233
|
+
*/
|
|
237
234
|
"Tag:exit"(node) {
|
|
238
235
|
if (IGNORING_NODES.includes(node.name)) {
|
|
239
236
|
parentIgnoringChildCount--;
|
|
@@ -250,7 +247,7 @@ module.exports = {
|
|
|
250
247
|
// Text
|
|
251
248
|
Text(node) {
|
|
252
249
|
indent();
|
|
253
|
-
const lineNodes =
|
|
250
|
+
const lineNodes = splitToLineNodes(node);
|
|
254
251
|
lineNodes.forEach((lineNode) => {
|
|
255
252
|
if (lineNode.value.trim().length) {
|
|
256
253
|
checkIndent(lineNode);
|
|
@@ -264,7 +261,7 @@ module.exports = {
|
|
|
264
261
|
CommentOpen: checkIndent,
|
|
265
262
|
CommentContent(node) {
|
|
266
263
|
indent();
|
|
267
|
-
const lineNodes =
|
|
264
|
+
const lineNodes = splitToLineNodes(node);
|
|
268
265
|
lineNodes.forEach((lineNode) => {
|
|
269
266
|
if (lineNode.value.trim().length) {
|
|
270
267
|
checkIndent(lineNode);
|
package/lib/rules/index.js
CHANGED
|
@@ -31,6 +31,10 @@ const noRestrictedAttrs = require("./no-restricted-attrs");
|
|
|
31
31
|
const noTrailingSpaces = require("./no-trailing-spaces");
|
|
32
32
|
const requireAttrs = require("./require-attrs");
|
|
33
33
|
const noRestrictedAttrValues = require("./no-restricted-attr-values");
|
|
34
|
+
const noScriptStyleType = require("./no-script-style-type");
|
|
35
|
+
const lowercase = require("./lowercase");
|
|
36
|
+
const requireOpenGraphProtocol = require("./require-open-graph-protocol");
|
|
37
|
+
const sortAttrs = require("./sort-attrs");
|
|
34
38
|
|
|
35
39
|
module.exports = {
|
|
36
40
|
"require-lang": requireLang,
|
|
@@ -66,4 +70,8 @@ module.exports = {
|
|
|
66
70
|
"no-restricted-attrs": noRestrictedAttrs,
|
|
67
71
|
"no-trailing-spaces": noTrailingSpaces,
|
|
68
72
|
"no-restricted-attr-values": noRestrictedAttrValues,
|
|
73
|
+
"no-script-style-type": noScriptStyleType,
|
|
74
|
+
lowercase: lowercase,
|
|
75
|
+
"require-open-graph-protocol": requireOpenGraphProtocol,
|
|
76
|
+
"sort-attrs": sortAttrs,
|
|
69
77
|
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const { NODE_TYPES } = require("@html-eslint/parser");
|
|
2
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
3
|
+
|
|
4
|
+
const MESSAGE_IDS = {
|
|
5
|
+
UNEXPECTED: "unexpected",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @type {Rule}
|
|
10
|
+
*/
|
|
11
|
+
module.exports = {
|
|
12
|
+
meta: {
|
|
13
|
+
type: "suggestion",
|
|
14
|
+
|
|
15
|
+
docs: {
|
|
16
|
+
description: "Enforce to use lowercase for tag and attribute names.",
|
|
17
|
+
category: RULE_CATEGORY.STYLE,
|
|
18
|
+
recommended: false,
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
fixable: "code",
|
|
22
|
+
schema: [],
|
|
23
|
+
messages: {
|
|
24
|
+
[MESSAGE_IDS.UNEXPECTED]: "'{{name}}' is not in lowercase.",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
create(context) {
|
|
29
|
+
/**
|
|
30
|
+
* @param {TagNode | StyleTagNode | ScriptTagNode} node
|
|
31
|
+
*/
|
|
32
|
+
function nameOf(node) {
|
|
33
|
+
if (node.type === NODE_TYPES.ScriptTag) return "script";
|
|
34
|
+
if (node.type === NODE_TYPES.StyleTag) return "style";
|
|
35
|
+
return node.name;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {TagNode | StyleTagNode | ScriptTagNode} node
|
|
40
|
+
*/
|
|
41
|
+
function check(node) {
|
|
42
|
+
const raw = node.openStart.value.slice(1);
|
|
43
|
+
if (nameOf(node) !== raw) {
|
|
44
|
+
context.report({
|
|
45
|
+
node: node.openStart,
|
|
46
|
+
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
47
|
+
data: {
|
|
48
|
+
name: raw,
|
|
49
|
+
},
|
|
50
|
+
fix(fixer) {
|
|
51
|
+
const name = nameOf(node);
|
|
52
|
+
const fixes = [
|
|
53
|
+
fixer.replaceTextRange(node.openStart.range, `<${name}`),
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
if (node.close) {
|
|
57
|
+
fixes.push(
|
|
58
|
+
fixer.replaceTextRange(node.close.range, `</${name}>`)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return fixes;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (node.attributes && node.attributes.length) {
|
|
67
|
+
node.attributes.forEach((attribute) => {
|
|
68
|
+
if (attribute.key.value !== attribute.key.value.toLowerCase()) {
|
|
69
|
+
context.report({
|
|
70
|
+
node: attribute.key,
|
|
71
|
+
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
72
|
+
data: {
|
|
73
|
+
name: attribute.key.value,
|
|
74
|
+
},
|
|
75
|
+
fix(fixer) {
|
|
76
|
+
return fixer.replaceText(
|
|
77
|
+
attribute.key,
|
|
78
|
+
attribute.key.value.toLowerCase()
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
Tag: check,
|
|
89
|
+
StyleTag: check,
|
|
90
|
+
ScriptTag: check,
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
|
-
const {
|
|
2
|
+
const { findAttr } = require("./utils/node");
|
|
7
3
|
|
|
8
4
|
const MESSAGE_IDS = {
|
|
9
5
|
UNEXPECTED: "unexpected",
|
|
@@ -46,15 +42,21 @@ module.exports = {
|
|
|
46
42
|
|
|
47
43
|
create(context) {
|
|
48
44
|
return {
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {TagNode | ScriptTagNode | StyleTagNode} node
|
|
48
|
+
*/
|
|
49
49
|
[["Tag", "ScriptTag", "StyleTag"].join(",")](node) {
|
|
50
|
-
const roleAttr =
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
const roleAttr = findAttr(node, "role");
|
|
51
|
+
if (
|
|
52
|
+
roleAttr &&
|
|
53
|
+
roleAttr.value &&
|
|
54
|
+
ABSTRACT_ROLE_SET.has(roleAttr.value.value)
|
|
55
|
+
) {
|
|
56
|
+
context.report({
|
|
57
|
+
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
58
|
+
node: roleAttr,
|
|
59
|
+
});
|
|
58
60
|
}
|
|
59
61
|
},
|
|
60
62
|
};
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
|
-
const {
|
|
2
|
+
const { findAttr } = require("./utils/node");
|
|
7
3
|
|
|
8
4
|
const MESSAGE_IDS = {
|
|
9
5
|
UNEXPECTED: "unexpected",
|
|
@@ -31,8 +27,11 @@ module.exports = {
|
|
|
31
27
|
|
|
32
28
|
create(context) {
|
|
33
29
|
return {
|
|
30
|
+
/**
|
|
31
|
+
* @param {TagNode | ScriptTagNode | StyleTagNode} node
|
|
32
|
+
*/
|
|
34
33
|
[["Tag", "ScriptTag", "StyleTag"].join(",")](node) {
|
|
35
|
-
const accessKeyAttr =
|
|
34
|
+
const accessKeyAttr = findAttr(node, "accesskey");
|
|
36
35
|
if (accessKeyAttr) {
|
|
37
36
|
context.report({
|
|
38
37
|
node: accessKeyAttr,
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
|
-
const {
|
|
2
|
+
const { findAttr } = require("./utils/node");
|
|
7
3
|
|
|
8
4
|
const MESSAGE_IDS = {
|
|
9
5
|
UNEXPECTED: "unexpected",
|
|
@@ -36,7 +32,7 @@ module.exports = {
|
|
|
36
32
|
if (node.name !== "body") {
|
|
37
33
|
return;
|
|
38
34
|
}
|
|
39
|
-
const ariaHiddenAttr =
|
|
35
|
+
const ariaHiddenAttr = findAttr(node, "aria-hidden");
|
|
40
36
|
if (ariaHiddenAttr) {
|
|
41
37
|
if (
|
|
42
38
|
(ariaHiddenAttr.value && ariaHiddenAttr.value.value !== "false") ||
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
2
|
|
|
7
3
|
const MESSAGE_IDS = {
|
|
@@ -31,6 +27,9 @@ module.exports = {
|
|
|
31
27
|
|
|
32
28
|
create(context) {
|
|
33
29
|
return {
|
|
30
|
+
/**
|
|
31
|
+
* @param {TagNode | StyleTagNode | ScriptTagNode} node
|
|
32
|
+
*/
|
|
34
33
|
[["Tag", "StyleTag", "ScriptTag"].join(",")](node) {
|
|
35
34
|
if (Array.isArray(node.attributes)) {
|
|
36
35
|
const attrsSet = new Set();
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
|
-
const {
|
|
2
|
+
const { findAttr } = require("./utils/node");
|
|
7
3
|
|
|
8
4
|
const MESSAGE_IDS = {
|
|
9
5
|
DUPLICATE_ID: "duplicateId",
|
|
@@ -32,11 +28,15 @@ module.exports = {
|
|
|
32
28
|
create(context) {
|
|
33
29
|
const IdAttrsMap = new Map();
|
|
34
30
|
return {
|
|
35
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @param {TagNode | ScriptTagNode | StyleTagNode} node
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
Tag(node) {
|
|
36
36
|
if (!node.attributes || node.attributes.length <= 0) {
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
|
-
const idAttr =
|
|
39
|
+
const idAttr = findAttr(node, "id");
|
|
40
40
|
if (idAttr && idAttr.value) {
|
|
41
41
|
if (!IdAttrsMap.has(idAttr.value.value)) {
|
|
42
42
|
IdAttrsMap.set(idAttr.value.value, []);
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
5
|
-
const {
|
|
2
|
+
const { getLocBetween } = require("./utils/node");
|
|
6
3
|
|
|
7
4
|
const MESSAGE_IDS = {
|
|
8
5
|
EXTRA_BETWEEN: "unexpectedBetween",
|
|
@@ -56,6 +53,9 @@ module.exports = {
|
|
|
56
53
|
.enforceBeforeSelfClose;
|
|
57
54
|
const disallowMissing = !!(context.options[0] || {}).disallowMissing;
|
|
58
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @param {AttributeNode[]} attrs
|
|
58
|
+
*/
|
|
59
59
|
function checkExtraSpacesBetweenAttrs(attrs) {
|
|
60
60
|
attrs.forEach((current, index, attrs) => {
|
|
61
61
|
if (index >= attrs.length - 1) {
|
|
@@ -69,7 +69,7 @@ module.exports = {
|
|
|
69
69
|
const spacesBetween = after.loc.start.column - current.loc.end.column;
|
|
70
70
|
if (spacesBetween > 1) {
|
|
71
71
|
context.report({
|
|
72
|
-
loc:
|
|
72
|
+
loc: getLocBetween(current, after),
|
|
73
73
|
messageId: MESSAGE_IDS.EXTRA_BETWEEN,
|
|
74
74
|
fix(fixer) {
|
|
75
75
|
return fixer.removeRange([current.range[1] + 1, after.range[0]]);
|
|
@@ -87,6 +87,12 @@ module.exports = {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* @param {OpenTagEndNode | OpenScriptTagEndNode | OpenStyleTagEndNode} openEnd
|
|
92
|
+
* @param {AttributeNode} lastAttr
|
|
93
|
+
* @param {boolean} isSelfClosed
|
|
94
|
+
* @returns {void}
|
|
95
|
+
*/
|
|
90
96
|
function checkExtraSpaceAfter(openEnd, lastAttr, isSelfClosed) {
|
|
91
97
|
if (openEnd.loc.end.line !== lastAttr.loc.end.line) {
|
|
92
98
|
// skip the attribute on the different line with the start tag
|
|
@@ -97,7 +103,7 @@ module.exports = {
|
|
|
97
103
|
|
|
98
104
|
if (spacesBetween > limit) {
|
|
99
105
|
context.report({
|
|
100
|
-
loc:
|
|
106
|
+
loc: getLocBetween(lastAttr, openEnd),
|
|
101
107
|
messageId: MESSAGE_IDS.EXTRA_AFTER,
|
|
102
108
|
fix(fixer) {
|
|
103
109
|
return fixer.removeRange([
|
|
@@ -110,7 +116,7 @@ module.exports = {
|
|
|
110
116
|
|
|
111
117
|
if (isSelfClosed && enforceBeforeSelfClose && spacesBetween < 1) {
|
|
112
118
|
context.report({
|
|
113
|
-
loc:
|
|
119
|
+
loc: getLocBetween(lastAttr, openEnd),
|
|
114
120
|
messageId: MESSAGE_IDS.MISSING_BEFORE_SELF_CLOSE,
|
|
115
121
|
fix(fixer) {
|
|
116
122
|
return fixer.insertTextAfter(lastAttr, " ");
|
|
@@ -119,6 +125,11 @@ module.exports = {
|
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
127
|
|
|
128
|
+
/**
|
|
129
|
+
* @param {OpenScriptTagStartNode | OpenTagStartNode | OpenStyleTagStartNode} node
|
|
130
|
+
* @param {AttributeNode} firstAttr
|
|
131
|
+
* @returns
|
|
132
|
+
*/
|
|
122
133
|
function checkExtraSpaceBefore(node, firstAttr) {
|
|
123
134
|
if (node.loc.start.line !== firstAttr.loc.start.line) {
|
|
124
135
|
// skip the attribute on the different line with the start tag
|
|
@@ -128,7 +139,7 @@ module.exports = {
|
|
|
128
139
|
const spacesBetween = firstAttr.loc.start.column - node.loc.end.column;
|
|
129
140
|
if (spacesBetween >= 2) {
|
|
130
141
|
context.report({
|
|
131
|
-
loc:
|
|
142
|
+
loc: getLocBetween(node, firstAttr),
|
|
132
143
|
|
|
133
144
|
messageId: MESSAGE_IDS.EXTRA_BEFORE,
|
|
134
145
|
fix(fixer) {
|
|
@@ -141,6 +152,11 @@ module.exports = {
|
|
|
141
152
|
}
|
|
142
153
|
}
|
|
143
154
|
|
|
155
|
+
/**
|
|
156
|
+
* @param {AnyNode} beforeSelfClosing
|
|
157
|
+
* @param {OpenTagEndNode | OpenScriptTagEndNode | OpenStyleTagEndNode} openEnd
|
|
158
|
+
* @returns
|
|
159
|
+
*/
|
|
144
160
|
function checkSpaceBeforeSelfClosing(beforeSelfClosing, openEnd) {
|
|
145
161
|
if (beforeSelfClosing.loc.start.line !== openEnd.loc.start.line) {
|
|
146
162
|
// skip the attribute on the different line with the start tag
|
|
@@ -148,7 +164,7 @@ module.exports = {
|
|
|
148
164
|
}
|
|
149
165
|
const spacesBetween =
|
|
150
166
|
openEnd.loc.start.column - beforeSelfClosing.loc.end.column;
|
|
151
|
-
const locBetween =
|
|
167
|
+
const locBetween = getLocBetween(beforeSelfClosing, openEnd);
|
|
152
168
|
|
|
153
169
|
if (spacesBetween > 1) {
|
|
154
170
|
context.report({
|
|
@@ -173,6 +189,10 @@ module.exports = {
|
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
return {
|
|
192
|
+
/**
|
|
193
|
+
* @param {TagNode | StyleTagNode | ScriptTagNode} node
|
|
194
|
+
* @returns
|
|
195
|
+
*/
|
|
176
196
|
[["Tag", "StyleTag", "ScriptTag"].join(",")](node) {
|
|
177
197
|
if (!node.attributes) {
|
|
178
198
|
return;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
2
|
-
const {
|
|
2
|
+
const { findAttr } = require("./utils/node");
|
|
3
3
|
|
|
4
4
|
const MESSAGE_IDS = {
|
|
5
5
|
INLINE_STYLE: "unexpectedInlineStyle",
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @type {Rule}
|
|
10
|
+
*/
|
|
8
11
|
module.exports = {
|
|
9
12
|
meta: {
|
|
10
13
|
type: "code",
|
|
@@ -25,7 +28,7 @@ module.exports = {
|
|
|
25
28
|
create(context) {
|
|
26
29
|
return {
|
|
27
30
|
Tag(node) {
|
|
28
|
-
const styleAttr =
|
|
31
|
+
const styleAttr = findAttr(node, "style");
|
|
29
32
|
if (styleAttr) {
|
|
30
33
|
context.report({
|
|
31
34
|
node: styleAttr,
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
2
|
|
|
7
3
|
const MESSAGE_IDS = {
|
|
@@ -45,6 +41,9 @@ module.exports = {
|
|
|
45
41
|
const lines = sourceCode.lines;
|
|
46
42
|
const max = context.options.length ? context.options[0].max : 2;
|
|
47
43
|
return {
|
|
44
|
+
/**
|
|
45
|
+
* @param {ProgramNode} node
|
|
46
|
+
*/
|
|
48
47
|
"Program:exit"(node) {
|
|
49
48
|
/** @type {number[]} */
|
|
50
49
|
const nonEmptyLineNumbers = [];
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
2
|
|
|
7
3
|
const MESSAGE_IDS = {
|
|
@@ -29,6 +25,9 @@ module.exports = {
|
|
|
29
25
|
},
|
|
30
26
|
|
|
31
27
|
create(context) {
|
|
28
|
+
/**
|
|
29
|
+
* @type {TagNode[]}
|
|
30
|
+
*/
|
|
32
31
|
const h1s = [];
|
|
33
32
|
|
|
34
33
|
return {
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
const { RULE_CATEGORY } = require("../constants");
|
|
6
|
-
const {
|
|
2
|
+
const { findAttr } = require("./utils/node");
|
|
7
3
|
|
|
8
4
|
const MESSAGE_IDS = {
|
|
9
5
|
UNEXPECTED: "unexpected",
|
|
@@ -36,8 +32,8 @@ module.exports = {
|
|
|
36
32
|
if (node.name !== "meta") {
|
|
37
33
|
return;
|
|
38
34
|
}
|
|
39
|
-
const nameAttr =
|
|
40
|
-
const contentAttr =
|
|
35
|
+
const nameAttr = findAttr(node, "name");
|
|
36
|
+
const contentAttr = findAttr(node, "content");
|
|
41
37
|
if (
|
|
42
38
|
nameAttr &&
|
|
43
39
|
nameAttr.value &&
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
const { RULE_CATEGORY } = require("../constants");
|
|
2
|
-
const { OBSOLETE_TAGS } = require("../constants");
|
|
1
|
+
const { RULE_CATEGORY, OBSOLETE_TAGS } = require("../constants");
|
|
3
2
|
|
|
4
3
|
const OBSOLETE_TAGS_SET = new Set(OBSOLETE_TAGS);
|
|
5
4
|
|
|
@@ -7,6 +6,9 @@ const MESSAGE_IDS = {
|
|
|
7
6
|
UNEXPECTED: "unexpected",
|
|
8
7
|
};
|
|
9
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @type {Rule}
|
|
11
|
+
*/
|
|
10
12
|
module.exports = {
|
|
11
13
|
meta: {
|
|
12
14
|
type: "code",
|