@html-eslint/eslint-plugin 0.13.2 → 0.14.1
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 +165 -179
- 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 +29 -70
- 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 +21 -17
- 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", "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,7 +13,7 @@
|
|
|
12
13
|
* @typedef {Object} MessageId
|
|
13
14
|
* @property {"wrongIndent"} WRONG_INDENT
|
|
14
15
|
*/
|
|
15
|
-
const { RULE_CATEGORY
|
|
16
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
16
17
|
const { NodeUtils } = require("./utils");
|
|
17
18
|
|
|
18
19
|
/** @type {MessageId} */
|
|
@@ -26,12 +27,7 @@ const INDENT_TYPES = {
|
|
|
26
27
|
SPACE: "space",
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
const IGNORING_NODES = [
|
|
30
|
-
NODE_TYPES.PRE,
|
|
31
|
-
NODE_TYPES.SCRIPT,
|
|
32
|
-
NODE_TYPES.STYLE,
|
|
33
|
-
NODE_TYPES.XMP,
|
|
34
|
-
];
|
|
30
|
+
const IGNORING_NODES = ["pre", "xmp", "script", "style"];
|
|
35
31
|
|
|
36
32
|
/**
|
|
37
33
|
* @type {Rule}
|
|
@@ -67,219 +63,209 @@ module.exports = {
|
|
|
67
63
|
},
|
|
68
64
|
create(context) {
|
|
69
65
|
const sourceCode = context.getSourceCode();
|
|
66
|
+
let indentLevel = -1;
|
|
67
|
+
let parentIgnoringChildCount = 0;
|
|
70
68
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
96
|
* @param {string} str
|
|
97
|
+
* @returns {number}
|
|
78
98
|
*/
|
|
79
|
-
function
|
|
99
|
+
function countLeftPadding(str) {
|
|
80
100
|
return str.length - str.replace(/^[\s\t]+/, "").length;
|
|
81
101
|
}
|
|
82
102
|
|
|
83
103
|
/**
|
|
84
|
-
* @param {
|
|
104
|
+
* @param {AnyNode} node
|
|
105
|
+
* @returns {node is LineNode}
|
|
85
106
|
*/
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
const line = lines[node.loc.start.line - 1];
|
|
89
|
-
let end = node.loc.start.column;
|
|
90
|
-
// @ts-ignore
|
|
91
|
-
if (typeof node.textLine === "string") {
|
|
92
|
-
// @ts-ignore
|
|
93
|
-
end += countIndentSize(node.textLine);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return line.slice(0, end);
|
|
107
|
+
function isLineNode(node) {
|
|
108
|
+
return node.type === "Line";
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
/**
|
|
100
|
-
* @param {
|
|
101
|
-
* @
|
|
112
|
+
* @param {AnyNode} node
|
|
113
|
+
* @returns {string}
|
|
102
114
|
*/
|
|
103
|
-
function
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const expectedIndent = indentUnit.repeat(level);
|
|
108
|
-
if (codeBefore !== expectedIndent) {
|
|
109
|
-
const expected = `${
|
|
110
|
-
indentType === INDENT_TYPES.SPACE ? level * indentSize : level
|
|
111
|
-
} ${indentType}`;
|
|
112
|
-
const actualTabs = (codeBefore.match(/\t/g) || []).length;
|
|
113
|
-
const actualSpaces = (codeBefore.match(/[^\S\t\n\r]/g) || []).length;
|
|
114
|
-
|
|
115
|
-
let actual = "";
|
|
116
|
-
|
|
117
|
-
if (!actualTabs && !actualSpaces) {
|
|
118
|
-
actual = "no indent";
|
|
119
|
-
}
|
|
120
|
-
if (actualTabs) {
|
|
121
|
-
actual += `${actualTabs} ${INDENT_TYPES.TAB}`;
|
|
122
|
-
}
|
|
123
|
-
if (actualSpaces) {
|
|
124
|
-
actual += `${actual.length ? ", " : ""}${actualSpaces} ${
|
|
125
|
-
INDENT_TYPES.SPACE
|
|
126
|
-
}`;
|
|
127
|
-
}
|
|
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;
|
|
128
119
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
messageId: MESSAGE_ID.WRONG_INDENT,
|
|
132
|
-
data: {
|
|
133
|
-
expected,
|
|
134
|
-
actual,
|
|
135
|
-
},
|
|
136
|
-
fix(fixer) {
|
|
137
|
-
const start = node.range[0] - node.loc.start.column;
|
|
138
|
-
let end = node.range[0];
|
|
139
|
-
// @ts-ignore
|
|
140
|
-
if (node.textLine) {
|
|
141
|
-
end += codeBefore.length;
|
|
142
|
-
}
|
|
143
|
-
return fixer.replaceTextRange([start, end], expectedIndent);
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
}
|
|
120
|
+
if (isLineNode(node)) {
|
|
121
|
+
column += countLeftPadding(node.value);
|
|
147
122
|
}
|
|
123
|
+
|
|
124
|
+
return line.slice(0, column);
|
|
148
125
|
}
|
|
149
126
|
|
|
150
127
|
/**
|
|
151
|
-
* @
|
|
152
|
-
* @param {AttrNode[]} attrs
|
|
128
|
+
* @returns {string}
|
|
153
129
|
*/
|
|
154
|
-
function
|
|
155
|
-
|
|
156
|
-
if (attr.loc.start.line !== startTag.loc.start.line) {
|
|
157
|
-
checkIndent(attr);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
130
|
+
function getExpectedIndent() {
|
|
131
|
+
return indentChar.repeat(indentLevel);
|
|
160
132
|
}
|
|
161
133
|
|
|
162
134
|
/**
|
|
163
|
-
* @param {
|
|
135
|
+
* @param {AnyNode} node
|
|
136
|
+
* @param {string} actualIndent
|
|
137
|
+
* @return {BaseNode}
|
|
164
138
|
*/
|
|
165
|
-
function
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
139
|
+
function getIndentNodeToReport(node, actualIndent) {
|
|
140
|
+
let rangeStart = node.range[0];
|
|
141
|
+
|
|
142
|
+
if (node.type !== "Line") {
|
|
143
|
+
rangeStart -= actualIndent.length;
|
|
144
|
+
}
|
|
171
145
|
|
|
172
|
-
|
|
173
|
-
range: [
|
|
174
|
-
start,
|
|
175
|
-
end,
|
|
146
|
+
return {
|
|
147
|
+
range: [rangeStart, rangeStart + actualIndent.length],
|
|
176
148
|
loc: {
|
|
177
149
|
start: {
|
|
178
|
-
|
|
179
|
-
|
|
150
|
+
column: 0,
|
|
151
|
+
line: node.loc.start.line,
|
|
180
152
|
},
|
|
181
153
|
end: {
|
|
182
|
-
|
|
183
|
-
|
|
154
|
+
column: actualIndent.length,
|
|
155
|
+
line: node.loc.start.line,
|
|
184
156
|
},
|
|
185
157
|
},
|
|
186
|
-
}
|
|
158
|
+
};
|
|
187
159
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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`;
|
|
197
174
|
}
|
|
198
|
-
if (
|
|
199
|
-
|
|
175
|
+
if (actualSpaces) {
|
|
176
|
+
if (actual) {
|
|
177
|
+
actual += ", ";
|
|
178
|
+
}
|
|
179
|
+
actual += `${actualSpaces} space`;
|
|
200
180
|
}
|
|
181
|
+
}
|
|
201
182
|
|
|
202
|
-
|
|
183
|
+
if (indentType === "space") {
|
|
184
|
+
expectedIndentSize *= indentSize;
|
|
185
|
+
}
|
|
203
186
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
187
|
+
return {
|
|
188
|
+
actual,
|
|
189
|
+
expected: `${expectedIndentSize} ${indentType}`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
207
192
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
+
},
|
|
215
214
|
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (!node.startTag) {
|
|
223
|
-
indentLevel.down();
|
|
224
|
-
}
|
|
225
|
-
node.lineNodes.forEach((lineNode) => {
|
|
226
|
-
if (lineNode.textLine.trim().length) {
|
|
227
|
-
checkIndent(lineNode);
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
if (!node.startTag) {
|
|
231
|
-
indentLevel.up();
|
|
232
|
-
}
|
|
218
|
+
return {
|
|
219
|
+
// Tag
|
|
220
|
+
Tag(node) {
|
|
221
|
+
if (IGNORING_NODES.includes(node.name)) {
|
|
222
|
+
parentIgnoringChildCount++;
|
|
233
223
|
}
|
|
224
|
+
indent();
|
|
234
225
|
},
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return;
|
|
226
|
+
OpenTagStart: checkIndent,
|
|
227
|
+
OpenTagEnd: checkIndent,
|
|
228
|
+
CloseTag: checkIndent,
|
|
229
|
+
"Tag:exit"(node) {
|
|
230
|
+
if (IGNORING_NODES.includes(node.name)) {
|
|
231
|
+
parentIgnoringChildCount--;
|
|
242
232
|
}
|
|
243
|
-
|
|
233
|
+
unindent();
|
|
234
|
+
},
|
|
244
235
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
+
});
|
|
248
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,
|
|
249
269
|
};
|
|
250
270
|
},
|
|
251
271
|
};
|
|
252
|
-
|
|
253
|
-
function getIndentTypeAndSize(options) {
|
|
254
|
-
/**
|
|
255
|
-
* @type {IndentType['SPACE'] | IndentType['TAB']}
|
|
256
|
-
*/
|
|
257
|
-
let indentType = INDENT_TYPES.SPACE;
|
|
258
|
-
let indentSize = 4;
|
|
259
|
-
if (options.length) {
|
|
260
|
-
if (options[0] === INDENT_TYPES.TAB) {
|
|
261
|
-
indentType = INDENT_TYPES.TAB;
|
|
262
|
-
} else {
|
|
263
|
-
indentSize = options[0];
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return { indentType, indentSize };
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
class IndentLevel {
|
|
270
|
-
constructor() {
|
|
271
|
-
this.level = -1;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
up() {
|
|
275
|
-
this.level++;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
down() {
|
|
279
|
-
this.level--;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
get() {
|
|
283
|
-
return this.level;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
@@ -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"() {
|