@html-eslint/eslint-plugin 0.13.1 → 0.14.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/rules/element-newline.js +21 -56
- package/lib/rules/id-naming-convention.js +5 -2
- package/lib/rules/indent.js +171 -168
- package/lib/rules/no-abstract-roles.js +2 -2
- package/lib/rules/no-accesskey-attrs.js +1 -1
- package/lib/rules/no-aria-hidden-body.js +14 -6
- package/lib/rules/no-duplicate-attrs.js +7 -7
- package/lib/rules/no-duplicate-id.js +8 -5
- package/lib/rules/no-extra-spacing-attrs.js +21 -40
- package/lib/rules/no-inline-styles.js +4 -10
- package/lib/rules/no-multiple-h1.js +4 -2
- package/lib/rules/no-non-scalable-viewport.js +8 -3
- package/lib/rules/no-obsolete-tags.js +4 -15
- package/lib/rules/no-positive-tabindex.js +6 -2
- package/lib/rules/no-restricted-attrs.js +12 -18
- package/lib/rules/no-skip-heading-levels.js +5 -2
- package/lib/rules/no-target-blank.js +8 -5
- package/lib/rules/quotes.js +31 -68
- package/lib/rules/require-button-type.js +8 -5
- package/lib/rules/require-closing-tags.js +26 -41
- package/lib/rules/require-doctype.js +1 -1
- package/lib/rules/require-frame-title.js +7 -11
- package/lib/rules/require-img-alt.js +15 -17
- package/lib/rules/require-lang.js +13 -7
- package/lib/rules/require-li-container.js +7 -11
- package/lib/rules/require-meta-charset.js +23 -19
- package/lib/rules/require-meta-description.js +30 -16
- package/lib/rules/require-meta-viewport.js +36 -23
- package/lib/rules/require-title.js +38 -16
- package/lib/rules/utils/node-utils.js +58 -20
- package/lib/types.d.ts +193 -62
- package/package.json +4 -3
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* @typedef {import("../types").ElementNode} ElementNode
|
|
3
|
-
* @typedef {import("../types").AnyNode} AnyNode
|
|
4
|
-
* @typedef {import("../types").Context} Context
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
|
|
1
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
8
2
|
|
|
9
3
|
const MESSAGE_IDS = {
|
|
10
4
|
EXPECT_NEW_LINE_AFTER: "expectAfter",
|
|
@@ -31,13 +25,10 @@ module.exports = {
|
|
|
31
25
|
},
|
|
32
26
|
},
|
|
33
27
|
|
|
34
|
-
/**
|
|
35
|
-
* @param {Context} context
|
|
36
|
-
*/
|
|
37
28
|
create(context) {
|
|
38
29
|
function checkSiblings(siblings) {
|
|
39
30
|
siblings
|
|
40
|
-
.filter((node) => node.type !==
|
|
31
|
+
.filter((node) => node.type !== "Text")
|
|
41
32
|
.forEach((current, index, arr) => {
|
|
42
33
|
const after = arr[index + 1];
|
|
43
34
|
if (after) {
|
|
@@ -45,7 +36,7 @@ module.exports = {
|
|
|
45
36
|
context.report({
|
|
46
37
|
node: current,
|
|
47
38
|
messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
|
|
48
|
-
data: { tag: `<${current.
|
|
39
|
+
data: { tag: `<${current.name}>` },
|
|
49
40
|
fix(fixer) {
|
|
50
41
|
return fixer.insertTextAfter(current, "\n");
|
|
51
42
|
},
|
|
@@ -55,75 +46,49 @@ module.exports = {
|
|
|
55
46
|
});
|
|
56
47
|
}
|
|
57
48
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
function checkChild(node) {
|
|
63
|
-
const children = (node.childNodes || []).filter(
|
|
64
|
-
(n) => !!n.range[0] && n.type !== NODE_TYPES.TEXT
|
|
65
|
-
);
|
|
66
|
-
const first = children[0];
|
|
67
|
-
const last = children[children.length - 1];
|
|
49
|
+
function checkChild(node, children) {
|
|
50
|
+
const targetChildren = children.filter((n) => n.type !== "Text");
|
|
51
|
+
const first = targetChildren[0];
|
|
52
|
+
const last = targetChildren[targetChildren.length - 1];
|
|
68
53
|
if (first) {
|
|
69
|
-
if (
|
|
54
|
+
if (isOnTheSameLine(node.openEnd, first)) {
|
|
70
55
|
context.report({
|
|
71
|
-
node: node.
|
|
56
|
+
node: node.openEnd,
|
|
72
57
|
messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
|
|
73
|
-
data: { tag: `<${node.
|
|
58
|
+
data: { tag: `<${node.name}>` },
|
|
74
59
|
fix(fixer) {
|
|
75
|
-
|
|
76
|
-
return fixer.insertTextAfter(node.startTag, "\n");
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
60
|
+
return fixer.insertTextAfter(node.openEnd, "\n");
|
|
79
61
|
},
|
|
80
62
|
});
|
|
81
63
|
}
|
|
82
64
|
}
|
|
65
|
+
|
|
83
66
|
if (last) {
|
|
84
|
-
if (node.
|
|
67
|
+
if (node.close && isOnTheSameLine(node.close, last)) {
|
|
85
68
|
context.report({
|
|
86
|
-
node: node.
|
|
69
|
+
node: node.close,
|
|
87
70
|
messageId: MESSAGE_IDS.EXPECT_NEW_LINE_BEFORE,
|
|
88
|
-
data: { tag: `</${node.
|
|
71
|
+
data: { tag: `</${node.name}>` },
|
|
89
72
|
fix(fixer) {
|
|
90
|
-
|
|
91
|
-
return fixer.insertTextBefore(node.endTag, "\n");
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
73
|
+
return fixer.insertTextBefore(node.close, "\n");
|
|
94
74
|
},
|
|
95
75
|
});
|
|
96
76
|
}
|
|
97
77
|
}
|
|
98
78
|
}
|
|
99
79
|
return {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (node.type !== NODE_TYPES.TEXT) {
|
|
105
|
-
checkSiblings(node.childNodes || []);
|
|
106
|
-
checkChild(node);
|
|
107
|
-
}
|
|
80
|
+
[["Tag", "StyleTag", "ScriptTag", "Program"].join(",")](node) {
|
|
81
|
+
const children = node.type === "Program" ? node.body : node.children;
|
|
82
|
+
checkSiblings(children);
|
|
83
|
+
checkChild(node, children);
|
|
108
84
|
},
|
|
109
85
|
};
|
|
110
86
|
},
|
|
111
87
|
};
|
|
112
88
|
|
|
113
|
-
/**
|
|
114
|
-
* Checks whether two nodes are on the same line or not.
|
|
115
|
-
* @param {AnyNode} nodeBefore A node before
|
|
116
|
-
* @param {AnyNode} nodeAfter A node after
|
|
117
|
-
* @returns {boolean} `true` if two nodes are on the same line, otherwise `false`.
|
|
118
|
-
*/
|
|
119
89
|
function isOnTheSameLine(nodeBefore, nodeAfter) {
|
|
120
90
|
if (nodeBefore && nodeAfter) {
|
|
121
|
-
|
|
122
|
-
if (nodeBefore.endTag) {
|
|
123
|
-
// @ts-ignore
|
|
124
|
-
return nodeBefore.endTag.loc.end.line === nodeAfter.loc.start.line;
|
|
125
|
-
}
|
|
126
|
-
return nodeBefore.loc.start.line === nodeAfter.loc.start.line;
|
|
91
|
+
return nodeBefore.loc.end.line === nodeAfter.loc.start.line;
|
|
127
92
|
}
|
|
128
93
|
return false;
|
|
129
94
|
}
|
|
@@ -58,12 +58,15 @@ module.exports = {
|
|
|
58
58
|
|
|
59
59
|
return {
|
|
60
60
|
"*"(node) {
|
|
61
|
+
if (!node.attributes || node.attributes.length <= 0) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
61
64
|
const idAttr = NodeUtils.findAttr(node, "id");
|
|
62
|
-
if (idAttr && idAttr.value && !checkNaming(idAttr.value)) {
|
|
65
|
+
if (idAttr && idAttr.value && !checkNaming(idAttr.value.value)) {
|
|
63
66
|
context.report({
|
|
64
67
|
node: idAttr,
|
|
65
68
|
data: {
|
|
66
|
-
actual: idAttr.value,
|
|
69
|
+
actual: idAttr.value.value,
|
|
67
70
|
convention,
|
|
68
71
|
},
|
|
69
72
|
messageId: MESSAGE_IDS.WRONG,
|
package/lib/rules/indent.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import("../types").Rule} Rule
|
|
3
|
-
* @typedef {import("../types").ElementNode} ElementNode
|
|
4
|
-
* @typedef {import("../types").AttrNode} AttrNode
|
|
5
3
|
* @typedef {import("../types").TagNode} TagNode
|
|
6
|
-
* @typedef {import("../types").AnyNode} AnyNode
|
|
7
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
|
|
8
9
|
* @typedef {Object} IndentType
|
|
9
10
|
* @property {"tab"} TAB
|
|
10
11
|
* @property {"space"} SPACE
|
|
@@ -12,8 +13,7 @@
|
|
|
12
13
|
* @typedef {Object} MessageId
|
|
13
14
|
* @property {"wrongIndent"} WRONG_INDENT
|
|
14
15
|
*/
|
|
15
|
-
|
|
16
|
-
const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
|
|
16
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
17
17
|
const { NodeUtils } = require("./utils");
|
|
18
18
|
|
|
19
19
|
/** @type {MessageId} */
|
|
@@ -27,12 +27,7 @@ const INDENT_TYPES = {
|
|
|
27
27
|
SPACE: "space",
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
const IGNORING_NODES = [
|
|
31
|
-
NODE_TYPES.PRE,
|
|
32
|
-
NODE_TYPES.SCRIPT,
|
|
33
|
-
NODE_TYPES.STYLE,
|
|
34
|
-
NODE_TYPES.XMP,
|
|
35
|
-
];
|
|
30
|
+
const IGNORING_NODES = ["pre", "xmp", "script", "style"];
|
|
36
31
|
|
|
37
32
|
/**
|
|
38
33
|
* @type {Rule}
|
|
@@ -68,201 +63,209 @@ module.exports = {
|
|
|
68
63
|
},
|
|
69
64
|
create(context) {
|
|
70
65
|
const sourceCode = context.getSourceCode();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
let indentLevel = -1;
|
|
67
|
+
let parentIgnoringChildCount = 0;
|
|
68
|
+
|
|
69
|
+
const { indentType, indentSize, indentChar } = (function () {
|
|
70
|
+
const options = context.options;
|
|
71
|
+
/**
|
|
72
|
+
* @type {IndentType['SPACE'] | IndentType['TAB']}
|
|
73
|
+
*/
|
|
74
|
+
let indentType = INDENT_TYPES.SPACE;
|
|
75
|
+
let indentSize = 4;
|
|
76
|
+
if (options.length) {
|
|
77
|
+
if (options[0] === INDENT_TYPES.TAB) {
|
|
78
|
+
indentType = INDENT_TYPES.TAB;
|
|
79
|
+
} else {
|
|
80
|
+
indentSize = options[0];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const indentChar =
|
|
84
|
+
indentType === INDENT_TYPES.SPACE ? " ".repeat(indentSize) : "\t";
|
|
85
|
+
return { indentType, indentSize, indentChar };
|
|
86
|
+
})();
|
|
87
|
+
|
|
88
|
+
function indent() {
|
|
89
|
+
indentLevel++;
|
|
90
|
+
}
|
|
91
|
+
function unindent() {
|
|
92
|
+
indentLevel--;
|
|
93
|
+
}
|
|
75
94
|
|
|
76
95
|
/**
|
|
77
|
-
* @param {
|
|
96
|
+
* @param {string} str
|
|
97
|
+
* @returns {number}
|
|
78
98
|
*/
|
|
79
|
-
function
|
|
80
|
-
return
|
|
81
|
-
.slice(node.range[0] - node.loc.start.column, node.range[0])
|
|
82
|
-
.replace("\n", "");
|
|
99
|
+
function countLeftPadding(str) {
|
|
100
|
+
return str.length - str.replace(/^[\s\t]+/, "").length;
|
|
83
101
|
}
|
|
84
102
|
|
|
85
103
|
/**
|
|
86
|
-
* @param {
|
|
87
|
-
* @
|
|
104
|
+
* @param {AnyNode} node
|
|
105
|
+
* @returns {node is LineNode}
|
|
88
106
|
*/
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const level = indentLevel.get();
|
|
93
|
-
const expectedIndent = indentUnit.repeat(level);
|
|
94
|
-
if (codeBefore !== expectedIndent) {
|
|
95
|
-
const expected = `${
|
|
96
|
-
indentType === INDENT_TYPES.SPACE ? level * indentSize : level
|
|
97
|
-
} ${indentType}`;
|
|
98
|
-
const actualTabs = (codeBefore.match(/\t/g) || []).length;
|
|
99
|
-
const actualSpaces = (codeBefore.match(/[^\S\t\n\r]/g) || []).length;
|
|
100
|
-
|
|
101
|
-
let actual = "";
|
|
107
|
+
function isLineNode(node) {
|
|
108
|
+
return node.type === "Line";
|
|
109
|
+
}
|
|
102
110
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
INDENT_TYPES.SPACE
|
|
112
|
-
}`;
|
|
113
|
-
}
|
|
111
|
+
/**
|
|
112
|
+
* @param {AnyNode} node
|
|
113
|
+
* @returns {string}
|
|
114
|
+
*/
|
|
115
|
+
function getActualIndent(node) {
|
|
116
|
+
const lines = sourceCode.getLines();
|
|
117
|
+
const line = lines[node.loc.start.line - 1];
|
|
118
|
+
let column = node.loc.start.column;
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
messageId: MESSAGE_ID.WRONG_INDENT,
|
|
118
|
-
data: {
|
|
119
|
-
expected,
|
|
120
|
-
actual,
|
|
121
|
-
},
|
|
122
|
-
fix(fixer) {
|
|
123
|
-
return fixer.replaceTextRange(
|
|
124
|
-
[node.range[0] - (node.loc.start.column - 1), node.range[0]],
|
|
125
|
-
expectedIndent
|
|
126
|
-
);
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
}
|
|
120
|
+
if (isLineNode(node)) {
|
|
121
|
+
column += countLeftPadding(node.value);
|
|
130
122
|
}
|
|
123
|
+
|
|
124
|
+
return line.slice(0, column);
|
|
131
125
|
}
|
|
132
126
|
|
|
133
127
|
/**
|
|
134
|
-
* @
|
|
135
|
-
* @param {AttrNode[]} attrs
|
|
128
|
+
* @returns {string}
|
|
136
129
|
*/
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
if (attr.loc.start.line !== startTag.loc.start.line) {
|
|
140
|
-
checkIndent(attr);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
130
|
+
function getExpectedIndent() {
|
|
131
|
+
return indentChar.repeat(indentLevel);
|
|
143
132
|
}
|
|
144
133
|
|
|
145
134
|
/**
|
|
146
|
-
* @param {AnyNode}
|
|
135
|
+
* @param {AnyNode} node
|
|
136
|
+
* @param {string} actualIndent
|
|
137
|
+
* @return {BaseNode}
|
|
147
138
|
*/
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
const end = startTag.range[1];
|
|
151
|
-
const line = startTag.loc.end.line;
|
|
152
|
-
const endCol = startTag.loc.end.column;
|
|
153
|
-
const startCol = startTag.loc.end.column - 1;
|
|
139
|
+
function getIndentNodeToReport(node, actualIndent) {
|
|
140
|
+
let rangeStart = node.range[0];
|
|
154
141
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
142
|
+
if (node.type !== "Line") {
|
|
143
|
+
rangeStart -= actualIndent.length;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
range: [rangeStart, rangeStart + actualIndent.length],
|
|
159
148
|
loc: {
|
|
160
149
|
start: {
|
|
161
|
-
|
|
162
|
-
|
|
150
|
+
column: 0,
|
|
151
|
+
line: node.loc.start.line,
|
|
163
152
|
},
|
|
164
153
|
end: {
|
|
165
|
-
|
|
166
|
-
|
|
154
|
+
column: actualIndent.length,
|
|
155
|
+
line: node.loc.start.line,
|
|
167
156
|
},
|
|
168
157
|
},
|
|
169
|
-
}
|
|
158
|
+
};
|
|
170
159
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} actualIndent
|
|
163
|
+
* @param {number} expectedIndentSize
|
|
164
|
+
*/
|
|
165
|
+
function getMessageData(actualIndent, expectedIndentSize) {
|
|
166
|
+
const actualTabs = (actualIndent.match(/\t/g) || []).length;
|
|
167
|
+
const actualSpaces = (actualIndent.match(/[^\S\t\n\r]/g) || []).length;
|
|
168
|
+
let actual = "";
|
|
169
|
+
if (!actualTabs && !actualSpaces) {
|
|
170
|
+
actual = "no indent";
|
|
171
|
+
} else {
|
|
172
|
+
if (actualTabs) {
|
|
173
|
+
actual += `${actualTabs} tab`;
|
|
180
174
|
}
|
|
181
|
-
if (
|
|
182
|
-
|
|
175
|
+
if (actualSpaces) {
|
|
176
|
+
if (actual) {
|
|
177
|
+
actual += ", ";
|
|
178
|
+
}
|
|
179
|
+
actual += `${actualSpaces} space`;
|
|
183
180
|
}
|
|
181
|
+
}
|
|
184
182
|
|
|
185
|
-
|
|
183
|
+
if (indentType === "space") {
|
|
184
|
+
expectedIndentSize *= indentSize;
|
|
185
|
+
}
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
187
|
+
return {
|
|
188
|
+
actual,
|
|
189
|
+
expected: `${expectedIndentSize} ${indentType}`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
190
192
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
/**
|
|
194
|
+
* @param {AnyNode} node
|
|
195
|
+
*/
|
|
196
|
+
function checkIndent(node) {
|
|
197
|
+
if (parentIgnoringChildCount > 0) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const actualIndent = getActualIndent(node);
|
|
201
|
+
const expectedIndent = getExpectedIndent();
|
|
202
|
+
if (actualIndent.trim().length) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (actualIndent !== expectedIndent) {
|
|
206
|
+
const targetNode = getIndentNodeToReport(node, actualIndent);
|
|
207
|
+
context.report({
|
|
208
|
+
node: targetNode,
|
|
209
|
+
messageId: MESSAGE_ID.WRONG_INDENT,
|
|
210
|
+
data: getMessageData(actualIndent, indentLevel),
|
|
211
|
+
fix(fixer) {
|
|
212
|
+
return fixer.replaceText(targetNode, expectedIndent);
|
|
213
|
+
},
|
|
198
214
|
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
199
217
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (!node.startTag) {
|
|
206
|
-
indentLevel.down();
|
|
207
|
-
}
|
|
208
|
-
node.lineNodes.forEach((lineNode) => {
|
|
209
|
-
if (lineNode.textLine.trim().length) {
|
|
210
|
-
checkIndent(lineNode, node);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
if (!node.startTag) {
|
|
214
|
-
indentLevel.up();
|
|
215
|
-
}
|
|
218
|
+
return {
|
|
219
|
+
// Tag
|
|
220
|
+
Tag(node) {
|
|
221
|
+
if (IGNORING_NODES.includes(node.name)) {
|
|
222
|
+
parentIgnoringChildCount++;
|
|
216
223
|
}
|
|
224
|
+
indent();
|
|
217
225
|
},
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return;
|
|
226
|
+
OpenTagStart: checkIndent,
|
|
227
|
+
OpenTagEnd: checkIndent,
|
|
228
|
+
CloseTag: checkIndent,
|
|
229
|
+
"Tag:exit"(node) {
|
|
230
|
+
if (IGNORING_NODES.includes(node.name)) {
|
|
231
|
+
parentIgnoringChildCount--;
|
|
225
232
|
}
|
|
226
|
-
|
|
233
|
+
unindent();
|
|
234
|
+
},
|
|
227
235
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
236
|
+
// Attribute
|
|
237
|
+
Attribute: indent,
|
|
238
|
+
AttributeKey: checkIndent,
|
|
239
|
+
AttributeValue: checkIndent,
|
|
240
|
+
"Attribute:exit": unindent,
|
|
241
|
+
|
|
242
|
+
// Text
|
|
243
|
+
Text(node) {
|
|
244
|
+
indent();
|
|
245
|
+
const lineNodes = NodeUtils.splitToLineNodes(node);
|
|
246
|
+
lineNodes.forEach((lineNode) => {
|
|
247
|
+
if (lineNode.value.trim().length) {
|
|
248
|
+
checkIndent(lineNode);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
231
251
|
},
|
|
252
|
+
"Text:exit": unindent,
|
|
253
|
+
|
|
254
|
+
// Comment
|
|
255
|
+
Comment: indent,
|
|
256
|
+
CommentOpen: checkIndent,
|
|
257
|
+
CommentContent(node) {
|
|
258
|
+
indent();
|
|
259
|
+
const lineNodes = NodeUtils.splitToLineNodes(node);
|
|
260
|
+
lineNodes.forEach((lineNode) => {
|
|
261
|
+
if (lineNode.value.trim().length) {
|
|
262
|
+
checkIndent(lineNode);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
CommentClose: checkIndent,
|
|
267
|
+
"Comment:exit": unindent,
|
|
268
|
+
"CommentContent:exit": unindent,
|
|
232
269
|
};
|
|
233
270
|
},
|
|
234
271
|
};
|
|
235
|
-
|
|
236
|
-
function getIndentTypeAndSize(options) {
|
|
237
|
-
/**
|
|
238
|
-
* @type {IndentType['SPACE'] | IndentType['TAB']}
|
|
239
|
-
*/
|
|
240
|
-
let indentType = INDENT_TYPES.SPACE;
|
|
241
|
-
let indentSize = 4;
|
|
242
|
-
if (options.length) {
|
|
243
|
-
if (options[0] === INDENT_TYPES.TAB) {
|
|
244
|
-
indentType = INDENT_TYPES.TAB;
|
|
245
|
-
} else {
|
|
246
|
-
indentSize = options[0];
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return { indentType, indentSize };
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
class IndentLevel {
|
|
253
|
-
constructor() {
|
|
254
|
-
this.level = -1;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
up() {
|
|
258
|
-
this.level++;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
down() {
|
|
262
|
-
this.level--;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
get() {
|
|
266
|
-
return this.level;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
@@ -46,10 +46,10 @@ module.exports = {
|
|
|
46
46
|
|
|
47
47
|
create(context) {
|
|
48
48
|
return {
|
|
49
|
-
"
|
|
49
|
+
[["Tag", "ScriptTag", "StyleTag"].join(",")](node) {
|
|
50
50
|
const roleAttr = NodeUtils.findAttr(node, "role");
|
|
51
51
|
if (roleAttr) {
|
|
52
|
-
if (ABSTRACT_ROLE_SET.has(roleAttr.value)) {
|
|
52
|
+
if (roleAttr.value && ABSTRACT_ROLE_SET.has(roleAttr.value.value)) {
|
|
53
53
|
context.report({
|
|
54
54
|
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
55
55
|
node: roleAttr,
|
|
@@ -32,13 +32,21 @@ module.exports = {
|
|
|
32
32
|
|
|
33
33
|
create(context) {
|
|
34
34
|
return {
|
|
35
|
-
|
|
35
|
+
Tag(node) {
|
|
36
|
+
if (node.name !== "body") {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
36
39
|
const ariaHiddenAttr = NodeUtils.findAttr(node, "aria-hidden");
|
|
37
|
-
if (ariaHiddenAttr
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
if (ariaHiddenAttr) {
|
|
41
|
+
if (
|
|
42
|
+
(ariaHiddenAttr.value && ariaHiddenAttr.value.value !== "false") ||
|
|
43
|
+
!ariaHiddenAttr.value
|
|
44
|
+
) {
|
|
45
|
+
context.report({
|
|
46
|
+
node: ariaHiddenAttr,
|
|
47
|
+
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
42
50
|
}
|
|
43
51
|
},
|
|
44
52
|
};
|
|
@@ -31,20 +31,20 @@ module.exports = {
|
|
|
31
31
|
|
|
32
32
|
create(context) {
|
|
33
33
|
return {
|
|
34
|
-
"
|
|
35
|
-
if (Array.isArray(node.
|
|
34
|
+
[["Tag", "StyleTag", "ScriptTag"].join(",")](node) {
|
|
35
|
+
if (Array.isArray(node.attributes)) {
|
|
36
36
|
const attrsSet = new Set();
|
|
37
|
-
node.
|
|
38
|
-
if (attrsSet.has(attr.
|
|
37
|
+
node.attributes.forEach((attr) => {
|
|
38
|
+
if (attr.key && attrsSet.has(attr.key.value)) {
|
|
39
39
|
context.report({
|
|
40
|
-
node:
|
|
40
|
+
node: attr,
|
|
41
41
|
data: {
|
|
42
|
-
attrName: attr.
|
|
42
|
+
attrName: attr.key.value,
|
|
43
43
|
},
|
|
44
44
|
messageId: MESSAGE_IDS.DUPLICATE_ATTRS,
|
|
45
45
|
});
|
|
46
46
|
} else {
|
|
47
|
-
attrsSet.add(attr.
|
|
47
|
+
attrsSet.add(attr.key.value);
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
}
|
|
@@ -33,13 +33,16 @@ module.exports = {
|
|
|
33
33
|
const IdAttrsMap = new Map();
|
|
34
34
|
return {
|
|
35
35
|
"*"(node) {
|
|
36
|
+
if (!node.attributes || node.attributes.length <= 0) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
36
39
|
const idAttr = NodeUtils.findAttr(node, "id");
|
|
37
|
-
if (idAttr) {
|
|
38
|
-
if (!IdAttrsMap.has(idAttr.value)) {
|
|
39
|
-
IdAttrsMap.set(idAttr.value, []);
|
|
40
|
+
if (idAttr && idAttr.value) {
|
|
41
|
+
if (!IdAttrsMap.has(idAttr.value.value)) {
|
|
42
|
+
IdAttrsMap.set(idAttr.value.value, []);
|
|
40
43
|
}
|
|
41
|
-
const nodes = IdAttrsMap.get(idAttr.value);
|
|
42
|
-
nodes.push(idAttr);
|
|
44
|
+
const nodes = IdAttrsMap.get(idAttr.value.value);
|
|
45
|
+
nodes.push(idAttr.value);
|
|
43
46
|
}
|
|
44
47
|
},
|
|
45
48
|
"Program:exit"() {
|