@html-eslint/eslint-plugin 0.31.1-alpha.0 → 0.32.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/attrs-newline.js +3 -5
- package/lib/rules/element-newline.js +11 -9
- package/lib/rules/id-naming-convention.js +5 -5
- package/lib/rules/indent/indent.js +119 -69
- package/lib/rules/index.js +12 -0
- package/lib/rules/lowercase.js +7 -8
- package/lib/rules/no-abstract-roles.js +4 -4
- package/lib/rules/no-accesskey-attrs.js +4 -4
- package/lib/rules/no-duplicate-attrs.js +4 -4
- package/lib/rules/no-duplicate-id.js +7 -7
- package/lib/rules/no-extra-spacing-attrs.js +14 -14
- package/lib/rules/no-extra-spacing-text.js +9 -10
- package/lib/rules/no-heading-inside-button.js +54 -0
- package/lib/rules/no-inline-styles.js +0 -1
- package/lib/rules/no-invalid-role.js +287 -0
- package/lib/rules/no-multiple-empty-lines.js +5 -5
- package/lib/rules/no-multiple-h1.js +2 -2
- package/lib/rules/no-nested-interactive.js +120 -0
- package/lib/rules/no-positive-tabindex.js +4 -4
- package/lib/rules/no-restricted-attr-values.js +6 -6
- package/lib/rules/no-restricted-attrs.js +6 -6
- package/lib/rules/no-script-style-type.js +4 -4
- package/lib/rules/no-skip-heading-levels.js +2 -2
- package/lib/rules/no-trailing-spaces.js +3 -3
- package/lib/rules/prefer-https.js +106 -0
- package/lib/rules/quotes.js +8 -8
- package/lib/rules/require-attrs.js +14 -6
- package/lib/rules/require-closing-tags.js +4 -4
- package/lib/rules/require-form-method.js +81 -0
- package/lib/rules/require-img-alt.js +2 -3
- package/lib/rules/require-input-label.js +77 -0
- package/lib/rules/require-meta-charset.js +3 -3
- package/lib/rules/require-meta-description.js +3 -3
- package/lib/rules/require-meta-viewport.js +3 -3
- package/lib/rules/require-open-graph-protocol.js +3 -3
- package/lib/rules/require-title.js +5 -5
- package/lib/rules/sort-attrs.js +12 -12
- package/lib/rules/utils/node.js +72 -32
- package/lib/rules/utils/settings.js +2 -2
- package/lib/rules/utils/visitors.js +1 -1
- package/lib/types/ast.d.ts +204 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/rule.d.ts +83 -0
- package/lib/types/settings.ts +13 -0
- package/package.json +4 -4
- package/types/rules/attrs-newline.d.ts +2 -3
- package/types/rules/attrs-newline.d.ts.map +1 -1
- package/types/rules/element-newline.d.ts +8 -9
- package/types/rules/element-newline.d.ts.map +1 -1
- package/types/rules/id-naming-convention.d.ts +4 -4
- package/types/rules/id-naming-convention.d.ts.map +1 -1
- package/types/rules/indent/indent.d.ts +12 -5
- package/types/rules/indent/indent.d.ts.map +1 -1
- package/types/rules/index.d.ts +6 -0
- package/types/rules/lowercase.d.ts +4 -5
- package/types/rules/lowercase.d.ts.map +1 -1
- package/types/rules/no-abstract-roles.d.ts +4 -4
- package/types/rules/no-abstract-roles.d.ts.map +1 -1
- package/types/rules/no-accesskey-attrs.d.ts +4 -4
- package/types/rules/no-accesskey-attrs.d.ts.map +1 -1
- package/types/rules/no-duplicate-attrs.d.ts +4 -4
- package/types/rules/no-duplicate-attrs.d.ts.map +1 -1
- package/types/rules/no-duplicate-id.d.ts +5 -5
- package/types/rules/no-duplicate-id.d.ts.map +1 -1
- package/types/rules/no-extra-spacing-attrs.d.ts +11 -11
- package/types/rules/no-extra-spacing-attrs.d.ts.map +1 -1
- package/types/rules/no-extra-spacing-text.d.ts +7 -8
- package/types/rules/no-extra-spacing-text.d.ts.map +1 -1
- package/types/rules/no-heading-inside-button.d.ts +7 -0
- package/types/rules/no-heading-inside-button.d.ts.map +1 -0
- package/types/rules/no-inline-styles.d.ts +1 -2
- package/types/rules/no-inline-styles.d.ts.map +1 -1
- package/types/rules/no-invalid-role.d.ts +7 -0
- package/types/rules/no-invalid-role.d.ts.map +1 -0
- package/types/rules/no-multiple-empty-lines.d.ts +5 -5
- package/types/rules/no-multiple-empty-lines.d.ts.map +1 -1
- package/types/rules/no-multiple-h1.d.ts +2 -2
- package/types/rules/no-multiple-h1.d.ts.map +1 -1
- package/types/rules/no-nested-interactive.d.ts +8 -0
- package/types/rules/no-nested-interactive.d.ts.map +1 -0
- package/types/rules/no-positive-tabindex.d.ts +4 -4
- package/types/rules/no-positive-tabindex.d.ts.map +1 -1
- package/types/rules/no-restricted-attr-values.d.ts +5 -5
- package/types/rules/no-restricted-attr-values.d.ts.map +1 -1
- package/types/rules/no-restricted-attrs.d.ts +5 -5
- package/types/rules/no-restricted-attrs.d.ts.map +1 -1
- package/types/rules/no-script-style-type.d.ts +4 -4
- package/types/rules/no-script-style-type.d.ts.map +1 -1
- package/types/rules/no-skip-heading-levels.d.ts +2 -2
- package/types/rules/no-skip-heading-levels.d.ts.map +1 -1
- package/types/rules/no-trailing-spaces.d.ts +3 -3
- package/types/rules/no-trailing-spaces.d.ts.map +1 -1
- package/types/rules/prefer-https.d.ts +11 -0
- package/types/rules/prefer-https.d.ts.map +1 -0
- package/types/rules/quotes.d.ts +6 -6
- package/types/rules/quotes.d.ts.map +1 -1
- package/types/rules/require-attrs.d.ts +9 -4
- package/types/rules/require-attrs.d.ts.map +1 -1
- package/types/rules/require-closing-tags.d.ts +2 -2
- package/types/rules/require-closing-tags.d.ts.map +1 -1
- package/types/rules/require-form-method.d.ts +7 -0
- package/types/rules/require-form-method.d.ts.map +1 -0
- package/types/rules/require-img-alt.d.ts +2 -2
- package/types/rules/require-img-alt.d.ts.map +1 -1
- package/types/rules/require-input-label.d.ts +8 -0
- package/types/rules/require-input-label.d.ts.map +1 -0
- package/types/rules/require-meta-charset.d.ts +3 -3
- package/types/rules/require-meta-charset.d.ts.map +1 -1
- package/types/rules/require-meta-description.d.ts +3 -3
- package/types/rules/require-meta-description.d.ts.map +1 -1
- package/types/rules/require-meta-viewport.d.ts +3 -3
- package/types/rules/require-meta-viewport.d.ts.map +1 -1
- package/types/rules/require-open-graph-protocol.d.ts +3 -3
- package/types/rules/require-open-graph-protocol.d.ts.map +1 -1
- package/types/rules/require-title.d.ts +4 -4
- package/types/rules/require-title.d.ts.map +1 -1
- package/types/rules/sort-attrs.d.ts +4 -4
- package/types/rules/sort-attrs.d.ts.map +1 -1
- package/types/rules/utils/node.d.ts +56 -37
- package/types/rules/utils/node.d.ts.map +1 -1
- package/types/rules/utils/settings.d.ts +2 -2
- package/types/rules/utils/settings.d.ts.map +1 -1
- package/types/rules/utils/source-code.d.ts +1 -1
- package/types/rules/utils/source-code.d.ts.map +1 -1
- package/types/rules/utils/visitors.d.ts +1 -1
- package/types/rules/utils/visitors.d.ts.map +1 -1
- package/types/types/settings.d.ts +13 -0
- package/types/types/settings.d.ts.map +1 -0
- package/lib/types.d.ts +0 -289
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
* @typedef { import("../types").ScriptTag } ScriptTag
|
|
5
|
+
* @typedef { import("../types").Attribute } Attribute
|
|
6
|
+
* @typedef { import("../types").AttributeValue } AttributeValue
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
10
|
+
const { findAttr, isScript } = require("./utils/node");
|
|
11
|
+
const { createVisitors } = require("./utils/visitors");
|
|
12
|
+
|
|
13
|
+
const MESSAGE_IDS = {
|
|
14
|
+
UNEXPECTED: "unexpected",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} url
|
|
19
|
+
*/
|
|
20
|
+
function getProtocol(url) {
|
|
21
|
+
try {
|
|
22
|
+
return new URL(url).protocol;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {Tag | ScriptTag} node
|
|
30
|
+
* @returns {AttributeValue | undefined}
|
|
31
|
+
*/
|
|
32
|
+
function getResourceAttributeValue(node) {
|
|
33
|
+
/**
|
|
34
|
+
* @type {Attribute | undefined}
|
|
35
|
+
*/
|
|
36
|
+
let attribute;
|
|
37
|
+
if (isScript(node)) {
|
|
38
|
+
attribute = findAttr(node, "src");
|
|
39
|
+
} else {
|
|
40
|
+
switch (node.name.toLowerCase()) {
|
|
41
|
+
case "img":
|
|
42
|
+
case "iframe":
|
|
43
|
+
case "audio":
|
|
44
|
+
case "video":
|
|
45
|
+
case "source":
|
|
46
|
+
case "embed": {
|
|
47
|
+
attribute = findAttr(node, "src");
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case "link": {
|
|
51
|
+
attribute = findAttr(node, "href");
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case "object": {
|
|
55
|
+
attribute = findAttr(node, "data");
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (attribute) {
|
|
61
|
+
return attribute.value;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @type {RuleModule}
|
|
68
|
+
*/
|
|
69
|
+
module.exports = {
|
|
70
|
+
meta: {
|
|
71
|
+
type: "code",
|
|
72
|
+
docs: {
|
|
73
|
+
description: "Prefer to use HTTPS for embedded resources",
|
|
74
|
+
recommended: false,
|
|
75
|
+
category: RULE_CATEGORY.BEST_PRACTICE,
|
|
76
|
+
},
|
|
77
|
+
fixable: false,
|
|
78
|
+
schema: [],
|
|
79
|
+
messages: {
|
|
80
|
+
[MESSAGE_IDS.UNEXPECTED]: "Unexpected use 'HTTP' protocol.",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
create(context) {
|
|
85
|
+
/**
|
|
86
|
+
* @param {Tag | ScriptTag} node
|
|
87
|
+
*/
|
|
88
|
+
function check(node) {
|
|
89
|
+
const attributeValue = getResourceAttributeValue(node);
|
|
90
|
+
if (attributeValue && !attributeValue.templates.length) {
|
|
91
|
+
const protocol = getProtocol(attributeValue.value);
|
|
92
|
+
if (protocol === "http:") {
|
|
93
|
+
context.report({
|
|
94
|
+
node: attributeValue,
|
|
95
|
+
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return createVisitors(context, {
|
|
102
|
+
ScriptTag: check,
|
|
103
|
+
Tag: check,
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
};
|
package/lib/rules/quotes.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @typedef { import("eslint").AST.Range } Range
|
|
2
3
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("../types").
|
|
4
|
-
* @typedef { import("../types").
|
|
5
|
-
* @typedef { import("../types").
|
|
6
|
-
* @typedef { import("../types").
|
|
7
|
-
* @typedef { import("../types").StyleTagNode } StyleTagNode
|
|
4
|
+
* @typedef { import("../types").Attribute } Attribute
|
|
5
|
+
* @typedef { import("../types").Tag } Tag
|
|
6
|
+
* @typedef { import("../types").ScriptTag } ScriptTag
|
|
7
|
+
* @typedef { import("../types").StyleTag } StyleTag
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const { RULE_CATEGORY } = require("../constants");
|
|
@@ -68,7 +68,7 @@ module.exports = {
|
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
*
|
|
71
|
-
* @param {
|
|
71
|
+
* @param {Attribute} attr
|
|
72
72
|
* @returns {[string, string]}
|
|
73
73
|
*/
|
|
74
74
|
function getQuotes(attr) {
|
|
@@ -77,7 +77,7 @@ module.exports = {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* @param {
|
|
80
|
+
* @param {Attribute} attr
|
|
81
81
|
* @returns {void}
|
|
82
82
|
*/
|
|
83
83
|
function checkQuotes(attr) {
|
|
@@ -133,7 +133,7 @@ module.exports = {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
|
-
* @param {
|
|
136
|
+
* @param {Tag | ScriptTag | StyleTag} node
|
|
137
137
|
*/
|
|
138
138
|
function check(node) {
|
|
139
139
|
node.attributes.forEach((attr) => checkQuotes(attr));
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("../types").
|
|
4
|
-
* @typedef { import("../types").
|
|
5
|
-
* @typedef { import("../types").
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
* @typedef { import("../types").ScriptTag } ScriptTag
|
|
5
|
+
* @typedef { import("../types").StyleTag } StyleTag
|
|
6
|
+
*
|
|
7
|
+
* @typedef {Object} Option
|
|
8
|
+
* @property {string} tag
|
|
9
|
+
* @property {string} attr
|
|
10
|
+
* @property {string | undefined} value
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
13
|
const { NODE_TYPES } = require("@html-eslint/parser");
|
|
@@ -48,6 +53,9 @@ module.exports = {
|
|
|
48
53
|
},
|
|
49
54
|
|
|
50
55
|
create(context) {
|
|
56
|
+
/**
|
|
57
|
+
* @type {Option[]}
|
|
58
|
+
*/
|
|
51
59
|
const options = context.options || [];
|
|
52
60
|
/**
|
|
53
61
|
* @type {Map<string, { tag: string, attr: string, value?: string}[]>}
|
|
@@ -67,7 +75,7 @@ module.exports = {
|
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
/**
|
|
70
|
-
* @param {
|
|
78
|
+
* @param {StyleTag | ScriptTag | Tag} node
|
|
71
79
|
* @param {string} tagName
|
|
72
80
|
*/
|
|
73
81
|
function check(node, tagName) {
|
|
@@ -105,7 +113,7 @@ module.exports = {
|
|
|
105
113
|
}
|
|
106
114
|
|
|
107
115
|
/**
|
|
108
|
-
* @param {
|
|
116
|
+
* @param {StyleTag | ScriptTag} node
|
|
109
117
|
*/
|
|
110
118
|
function checkStyleOrScript(node) {
|
|
111
119
|
const tagName = node.type === NODE_TYPES.StyleTag ? "style" : "script";
|
|
@@ -116,7 +124,7 @@ module.exports = {
|
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
/**
|
|
119
|
-
* @param {
|
|
127
|
+
* @param {Tag} node
|
|
120
128
|
*/
|
|
121
129
|
function checkTag(node) {
|
|
122
130
|
const tagName = node.name.toLowerCase();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("../types").
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { RULE_CATEGORY, VOID_ELEMENTS } = require("../constants");
|
|
@@ -70,7 +70,7 @@ module.exports = {
|
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
* @param {
|
|
73
|
+
* @param {Tag} node
|
|
74
74
|
*/
|
|
75
75
|
function checkClosingTag(node) {
|
|
76
76
|
if (!node.close) {
|
|
@@ -85,7 +85,7 @@ module.exports = {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* @param {
|
|
88
|
+
* @param {Tag} node
|
|
89
89
|
* @param {boolean} shouldSelfClose
|
|
90
90
|
* @param {boolean} fixable
|
|
91
91
|
*/
|
|
@@ -150,7 +150,7 @@ module.exports = {
|
|
|
150
150
|
if (["svg", "math"].includes(node.name)) foreignContext.push(node.name);
|
|
151
151
|
},
|
|
152
152
|
/**
|
|
153
|
-
* @param {
|
|
153
|
+
* @param {Tag} node
|
|
154
154
|
*/
|
|
155
155
|
"Tag:exit"(node) {
|
|
156
156
|
if (node.name === foreignContext[foreignContext.length - 1]) {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
6
|
+
const { findAttr } = require("./utils/node");
|
|
7
|
+
const { createVisitors } = require("./utils/visitors");
|
|
8
|
+
|
|
9
|
+
const MESSAGE_IDS = {
|
|
10
|
+
MISSING: "missing",
|
|
11
|
+
MISSING_VALUE: "missingValue",
|
|
12
|
+
UNEXPECTED: "unexpected",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const ALLOWED_METHODS = new Set(["GET", "POST", "DIALOG"]);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @type {RuleModule}
|
|
19
|
+
*/
|
|
20
|
+
module.exports = {
|
|
21
|
+
meta: {
|
|
22
|
+
type: "code",
|
|
23
|
+
|
|
24
|
+
docs: {
|
|
25
|
+
description: "Require `method` attribute in `<form>`",
|
|
26
|
+
category: RULE_CATEGORY.ACCESSIBILITY,
|
|
27
|
+
recommended: false,
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
fixable: false,
|
|
31
|
+
schema: [],
|
|
32
|
+
messages: {
|
|
33
|
+
[MESSAGE_IDS.MISSING]: "The 'method' attribute is missing on the <form>.",
|
|
34
|
+
[MESSAGE_IDS.MISSING_VALUE]:
|
|
35
|
+
"The 'method' attribute value is missing on the <form>",
|
|
36
|
+
[MESSAGE_IDS.UNEXPECTED]:
|
|
37
|
+
"The 'method' attribute has an invalid value on the <form>.",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
create(context) {
|
|
42
|
+
return createVisitors(context, {
|
|
43
|
+
Tag(node) {
|
|
44
|
+
if (node.name.toLowerCase() !== "form") {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const method = findAttr(node, "method");
|
|
48
|
+
|
|
49
|
+
if (!method) {
|
|
50
|
+
context.report({
|
|
51
|
+
node: node.openStart,
|
|
52
|
+
messageId: MESSAGE_IDS.MISSING,
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!method.value) {
|
|
58
|
+
context.report({
|
|
59
|
+
node: node.openStart,
|
|
60
|
+
messageId: MESSAGE_IDS.MISSING_VALUE,
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
method.value.templates &&
|
|
67
|
+
method.value.templates.some((template) => template.isTemplate)
|
|
68
|
+
) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!ALLOWED_METHODS.has(method.value.value.toUpperCase())) {
|
|
73
|
+
context.report({
|
|
74
|
+
node: node.openStart,
|
|
75
|
+
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("../types").
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { RULE_CATEGORY } = require("../constants");
|
|
@@ -72,8 +72,7 @@ module.exports = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
* @param {TagNode} node
|
|
75
|
+
* @param {Tag} node
|
|
77
76
|
* @param {string[]} substitute
|
|
78
77
|
* @returns
|
|
79
78
|
*/
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
7
|
+
const { createVisitors } = require("./utils/visitors");
|
|
8
|
+
const { findParent, isTag } = require("./utils/node");
|
|
9
|
+
|
|
10
|
+
const MESSAGE_IDS = {
|
|
11
|
+
MISSING: "missingLabel",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const INPUT_TAGS = new Set(["input", "textarea", "select"]);
|
|
15
|
+
|
|
16
|
+
const LABEL_ATTRIBUTES = new Set(["id", "aria-labelledby", "aria-label"]);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @type {RuleModule}
|
|
20
|
+
*/
|
|
21
|
+
module.exports = {
|
|
22
|
+
meta: {
|
|
23
|
+
type: "code",
|
|
24
|
+
|
|
25
|
+
docs: {
|
|
26
|
+
description:
|
|
27
|
+
"Enforces use of label for form elements(`input`, `textarea`, `select`)",
|
|
28
|
+
category: RULE_CATEGORY.ACCESSIBILITY,
|
|
29
|
+
recommended: false,
|
|
30
|
+
},
|
|
31
|
+
fixable: null,
|
|
32
|
+
schema: [],
|
|
33
|
+
messages: {
|
|
34
|
+
[MESSAGE_IDS.MISSING]: "Missing an associated label",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
create(context) {
|
|
38
|
+
return createVisitors(context, {
|
|
39
|
+
Tag(node) {
|
|
40
|
+
if (!INPUT_TAGS.has(node.name.toLowerCase())) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const attr of node.attributes) {
|
|
45
|
+
if (
|
|
46
|
+
LABEL_ATTRIBUTES.has(attr.key.value.toLowerCase()) &&
|
|
47
|
+
attr.value &&
|
|
48
|
+
attr.value.value
|
|
49
|
+
) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
attr.key.value.toLowerCase() === "type" &&
|
|
55
|
+
attr.value &&
|
|
56
|
+
attr.value.value === "hidden"
|
|
57
|
+
) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const label = findParent(node, (parent) => {
|
|
63
|
+
return isTag(parent) && parent.name.toLowerCase() === "label";
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (label) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
context.report({
|
|
71
|
+
node,
|
|
72
|
+
messageId: "missingLabel",
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("
|
|
4
|
-
* @typedef { import("
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
* @typedef { import("../types").AnyNode } AnyNode
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { RULE_CATEGORY } = require("../constants");
|
|
@@ -15,7 +15,7 @@ const MESSAGE_IDS = {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @param {AnyNode} node
|
|
18
|
-
* @returns {node is
|
|
18
|
+
* @returns {node is Tag}
|
|
19
19
|
*/
|
|
20
20
|
function isMetaCharset(node) {
|
|
21
21
|
return isTag(node) && node.name === "meta" && !!findAttr(node, "charset");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("
|
|
4
|
-
* @typedef { import("
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
* @typedef { import("../types").AnyNode } AnyNode
|
|
5
5
|
*/
|
|
6
6
|
const { RULE_CATEGORY } = require("../constants");
|
|
7
7
|
const { filter } = require("./utils/array");
|
|
@@ -14,7 +14,7 @@ const MESSAGE_IDS = {
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @param {AnyNode} node
|
|
17
|
-
* @returns {node is
|
|
17
|
+
* @returns {node is Tag}
|
|
18
18
|
*/
|
|
19
19
|
function isMetaTagNode(node) {
|
|
20
20
|
return isTag(node) && node.name === "meta";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("
|
|
4
|
-
* @typedef { import("
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
* @typedef { import("../types").AnyNode } AnyNode
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { RULE_CATEGORY } = require("../constants");
|
|
@@ -15,7 +15,7 @@ const MESSAGE_IDS = {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @param {AnyNode} node
|
|
18
|
-
* @returns {node is
|
|
18
|
+
* @returns {node is Tag}
|
|
19
19
|
*/
|
|
20
20
|
function isMetaViewport(node) {
|
|
21
21
|
if (isTag(node) && node.name === "meta") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("
|
|
4
|
-
* @typedef { import("
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
* @typedef { import("../types").AnyNode } AnyNode
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { RULE_CATEGORY } = require("../constants");
|
|
@@ -71,7 +71,7 @@ module.exports = {
|
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* @param {AnyNode} node
|
|
74
|
-
* @returns {node is
|
|
74
|
+
* @returns {node is Tag}
|
|
75
75
|
*/
|
|
76
76
|
function isOgpMeta(node) {
|
|
77
77
|
const isMeta = isTag(node) && node.name === "meta";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("
|
|
4
|
-
* @typedef { import("
|
|
5
|
-
* @typedef { import("
|
|
3
|
+
* @typedef { import("../types").Tag } Tag
|
|
4
|
+
* @typedef { import("../types").Text } Text
|
|
5
|
+
* @typedef { import("../types").AnyNode } AnyNode
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const { RULE_CATEGORY } = require("../constants");
|
|
@@ -16,7 +16,7 @@ const MESSAGE_IDS = {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @param {AnyNode} node
|
|
19
|
-
* @returns {node is
|
|
19
|
+
* @returns {node is Tag}
|
|
20
20
|
*/
|
|
21
21
|
function isTitle(node) {
|
|
22
22
|
return isTag(node) && node.name === "title";
|
|
@@ -24,7 +24,7 @@ function isTitle(node) {
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* @param {AnyNode} node
|
|
27
|
-
* @returns {node is
|
|
27
|
+
* @returns {node is Text}
|
|
28
28
|
*/
|
|
29
29
|
function isNonEmptyText(node) {
|
|
30
30
|
return isText(node) && node.value.trim().length > 0;
|
package/lib/rules/sort-attrs.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @typedef { import("eslint").Rule.RuleFixer } RuleFixer
|
|
2
3
|
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
-
* @typedef { import("../types").
|
|
4
|
-
* @typedef { import("../types").
|
|
5
|
-
* @typedef { import("../types").RuleFixer } RuleFixer
|
|
4
|
+
* @typedef { import("../types").Attribute } Attribute
|
|
5
|
+
* @typedef { import("../types").Text } Text
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const { RULE_CATEGORY } = require("../constants");
|
|
@@ -55,8 +55,8 @@ module.exports = {
|
|
|
55
55
|
const priority = option.priority;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* @param {
|
|
59
|
-
* @param {
|
|
58
|
+
* @param {Attribute} attrA
|
|
59
|
+
* @param {Attribute} attrB
|
|
60
60
|
* @return {number}
|
|
61
61
|
*/
|
|
62
62
|
function compare(attrA, attrB) {
|
|
@@ -76,8 +76,8 @@ module.exports = {
|
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* @param {string} source
|
|
79
|
-
* @param {
|
|
80
|
-
* @param {
|
|
79
|
+
* @param {Attribute[]} unsorted
|
|
80
|
+
* @param {Attribute[]} sorted
|
|
81
81
|
* @returns {string}
|
|
82
82
|
*/
|
|
83
83
|
function getSortedCode(source, unsorted, sorted) {
|
|
@@ -99,8 +99,8 @@ module.exports = {
|
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* @param {RuleFixer} fixer
|
|
102
|
-
* @param {
|
|
103
|
-
* @param {
|
|
102
|
+
* @param {Attribute[]} unsorted
|
|
103
|
+
* @param {Attribute[]} sorted
|
|
104
104
|
*/
|
|
105
105
|
function fix(fixer, unsorted, sorted) {
|
|
106
106
|
const source = sourceCode.getText();
|
|
@@ -111,8 +111,8 @@ module.exports = {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
|
-
* @param {
|
|
115
|
-
* @param {
|
|
114
|
+
* @param {Attribute[]} before
|
|
115
|
+
* @param {Attribute[]} after
|
|
116
116
|
* @returns {boolean}
|
|
117
117
|
*/
|
|
118
118
|
function isChanged(before, after) {
|
|
@@ -123,7 +123,7 @@ module.exports = {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
* @param {
|
|
126
|
+
* @param {Attribute[]} unsorted
|
|
127
127
|
*/
|
|
128
128
|
function checkSorting(unsorted) {
|
|
129
129
|
if (unsorted.length <= 1) {
|