@html-eslint/eslint-plugin-react 0.56.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/LICENSE +21 -0
- package/README.md +11 -0
- package/lib/configs/index.js +14 -0
- package/lib/constants/node-types.js +176 -0
- package/lib/index.js +37 -0
- package/lib/rules/index.js +15 -0
- package/lib/rules/no-ineffective-attrs.js +67 -0
- package/lib/rules/no-invalid-attr-value.js +96 -0
- package/lib/rules/no-obsolete-attrs.js +55 -0
- package/lib/rules/no-obsolete-tags.js +54 -0
- package/lib/rules/use-baseline.js +92 -0
- package/lib/rules/utils/adapter.js +180 -0
- package/lib/types.js +1 -0
- package/lib/types.ts +401 -0
- package/package.json +55 -0
- package/types/configs/index.d.ts +2 -0
- package/types/configs/index.d.ts.map +1 -0
- package/types/constants/node-types.d.ts +171 -0
- package/types/constants/node-types.d.ts.map +1 -0
- package/types/index.d.ts +6 -0
- package/types/index.d.ts.map +1 -0
- package/types/rules/index.d.ts +22 -0
- package/types/rules/index.d.ts.map +1 -0
- package/types/rules/no-ineffective-attrs.d.ts +4 -0
- package/types/rules/no-ineffective-attrs.d.ts.map +1 -0
- package/types/rules/no-invalid-attr-value.d.ts +10 -0
- package/types/rules/no-invalid-attr-value.d.ts.map +1 -0
- package/types/rules/no-obsolete-attrs.d.ts +4 -0
- package/types/rules/no-obsolete-attrs.d.ts.map +1 -0
- package/types/rules/no-obsolete-tags.d.ts +10 -0
- package/types/rules/no-obsolete-tags.d.ts.map +1 -0
- package/types/rules/use-baseline.d.ts +10 -0
- package/types/rules/use-baseline.d.ts.map +1 -0
- package/types/rules/utils/adapter.d.ts +14 -0
- package/types/rules/utils/adapter.d.ts.map +1 -0
- package/types/types.d.ts +255 -0
- package/types/types.d.ts.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 YeonJuan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# `@html-eslint/eslint-plugin-react`
|
|
2
|
+
|
|
3
|
+
An ESLint plugin which provides lint rules for React.
|
|
4
|
+
|
|
5
|
+
1. [Getting Started](https://html-eslint.org/docs/react/getting-started)
|
|
6
|
+
1. [Rules](https://html-eslint.org/docs/react/rules)
|
|
7
|
+
1. [License](#License)
|
|
8
|
+
|
|
9
|
+
## License
|
|
10
|
+
|
|
11
|
+
Distributed under the MIT License.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const rules = require("../rules");
|
|
2
|
+
const ruleKeys = Object.keys(rules);
|
|
3
|
+
|
|
4
|
+
const allRules = ruleKeys.reduce((acc, ruleName) => {
|
|
5
|
+
acc[`@html-eslint/react/${ruleName}`] = "error";
|
|
6
|
+
if (ruleName === "use-baseline") {
|
|
7
|
+
acc[`@html-eslint/react/${ruleName}`] = "warn";
|
|
8
|
+
}
|
|
9
|
+
return acc;
|
|
10
|
+
}, /** @type {Record<string, "error" | "warn">} */ ({}));
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
allRules,
|
|
14
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST Node Types Re-defined from @typescript-eslint/types AST_NODE_TYPES enum
|
|
3
|
+
* as string constants to avoid runtime dependency on @typescript-eslint/types
|
|
4
|
+
*/
|
|
5
|
+
const AST_NODE_TYPES = /** @type {const} */ ({
|
|
6
|
+
AccessorProperty: "AccessorProperty",
|
|
7
|
+
ArrayExpression: "ArrayExpression",
|
|
8
|
+
ArrayPattern: "ArrayPattern",
|
|
9
|
+
ArrowFunctionExpression: "ArrowFunctionExpression",
|
|
10
|
+
AssignmentExpression: "AssignmentExpression",
|
|
11
|
+
AssignmentPattern: "AssignmentPattern",
|
|
12
|
+
AwaitExpression: "AwaitExpression",
|
|
13
|
+
BinaryExpression: "BinaryExpression",
|
|
14
|
+
BlockStatement: "BlockStatement",
|
|
15
|
+
BreakStatement: "BreakStatement",
|
|
16
|
+
CallExpression: "CallExpression",
|
|
17
|
+
CatchClause: "CatchClause",
|
|
18
|
+
ChainExpression: "ChainExpression",
|
|
19
|
+
ClassBody: "ClassBody",
|
|
20
|
+
ClassDeclaration: "ClassDeclaration",
|
|
21
|
+
ClassExpression: "ClassExpression",
|
|
22
|
+
ConditionalExpression: "ConditionalExpression",
|
|
23
|
+
ContinueStatement: "ContinueStatement",
|
|
24
|
+
DebuggerStatement: "DebuggerStatement",
|
|
25
|
+
Decorator: "Decorator",
|
|
26
|
+
DoWhileStatement: "DoWhileStatement",
|
|
27
|
+
EmptyStatement: "EmptyStatement",
|
|
28
|
+
ExportAllDeclaration: "ExportAllDeclaration",
|
|
29
|
+
ExportDefaultDeclaration: "ExportDefaultDeclaration",
|
|
30
|
+
ExportNamedDeclaration: "ExportNamedDeclaration",
|
|
31
|
+
ExportSpecifier: "ExportSpecifier",
|
|
32
|
+
ExpressionStatement: "ExpressionStatement",
|
|
33
|
+
ForInStatement: "ForInStatement",
|
|
34
|
+
ForOfStatement: "ForOfStatement",
|
|
35
|
+
ForStatement: "ForStatement",
|
|
36
|
+
FunctionDeclaration: "FunctionDeclaration",
|
|
37
|
+
FunctionExpression: "FunctionExpression",
|
|
38
|
+
Identifier: "Identifier",
|
|
39
|
+
IfStatement: "IfStatement",
|
|
40
|
+
ImportAttribute: "ImportAttribute",
|
|
41
|
+
ImportDeclaration: "ImportDeclaration",
|
|
42
|
+
ImportDefaultSpecifier: "ImportDefaultSpecifier",
|
|
43
|
+
ImportExpression: "ImportExpression",
|
|
44
|
+
ImportNamespaceSpecifier: "ImportNamespaceSpecifier",
|
|
45
|
+
ImportSpecifier: "ImportSpecifier",
|
|
46
|
+
JSXAttribute: "JSXAttribute",
|
|
47
|
+
JSXClosingElement: "JSXClosingElement",
|
|
48
|
+
JSXClosingFragment: "JSXClosingFragment",
|
|
49
|
+
JSXElement: "JSXElement",
|
|
50
|
+
JSXEmptyExpression: "JSXEmptyExpression",
|
|
51
|
+
JSXExpressionContainer: "JSXExpressionContainer",
|
|
52
|
+
JSXFragment: "JSXFragment",
|
|
53
|
+
JSXIdentifier: "JSXIdentifier",
|
|
54
|
+
JSXMemberExpression: "JSXMemberExpression",
|
|
55
|
+
JSXNamespacedName: "JSXNamespacedName",
|
|
56
|
+
JSXOpeningElement: "JSXOpeningElement",
|
|
57
|
+
JSXOpeningFragment: "JSXOpeningFragment",
|
|
58
|
+
JSXSpreadAttribute: "JSXSpreadAttribute",
|
|
59
|
+
JSXSpreadChild: "JSXSpreadChild",
|
|
60
|
+
JSXText: "JSXText",
|
|
61
|
+
LabeledStatement: "LabeledStatement",
|
|
62
|
+
Literal: "Literal",
|
|
63
|
+
LogicalExpression: "LogicalExpression",
|
|
64
|
+
MemberExpression: "MemberExpression",
|
|
65
|
+
MetaProperty: "MetaProperty",
|
|
66
|
+
MethodDefinition: "MethodDefinition",
|
|
67
|
+
NewExpression: "NewExpression",
|
|
68
|
+
ObjectExpression: "ObjectExpression",
|
|
69
|
+
ObjectPattern: "ObjectPattern",
|
|
70
|
+
PrivateIdentifier: "PrivateIdentifier",
|
|
71
|
+
Program: "Program",
|
|
72
|
+
Property: "Property",
|
|
73
|
+
PropertyDefinition: "PropertyDefinition",
|
|
74
|
+
RestElement: "RestElement",
|
|
75
|
+
ReturnStatement: "ReturnStatement",
|
|
76
|
+
SequenceExpression: "SequenceExpression",
|
|
77
|
+
SpreadElement: "SpreadElement",
|
|
78
|
+
StaticBlock: "StaticBlock",
|
|
79
|
+
Super: "Super",
|
|
80
|
+
SwitchCase: "SwitchCase",
|
|
81
|
+
SwitchStatement: "SwitchStatement",
|
|
82
|
+
TaggedTemplateExpression: "TaggedTemplateExpression",
|
|
83
|
+
TemplateLiteral: "TemplateLiteral",
|
|
84
|
+
TemplateElement: "TemplateElement",
|
|
85
|
+
ThisExpression: "ThisExpression",
|
|
86
|
+
ThrowStatement: "ThrowStatement",
|
|
87
|
+
TryStatement: "TryStatement",
|
|
88
|
+
TSAbstractAccessorProperty: "TSAbstractAccessorProperty",
|
|
89
|
+
TSAbstractKeyword: "TSAbstractKeyword",
|
|
90
|
+
TSAbstractMethodDefinition: "TSAbstractMethodDefinition",
|
|
91
|
+
TSAbstractPropertyDefinition: "TSAbstractPropertyDefinition",
|
|
92
|
+
TSAnyKeyword: "TSAnyKeyword",
|
|
93
|
+
TSArrayType: "TSArrayType",
|
|
94
|
+
TSAsExpression: "TSAsExpression",
|
|
95
|
+
TSAsyncKeyword: "TSAsyncKeyword",
|
|
96
|
+
TSBigIntKeyword: "TSBigIntKeyword",
|
|
97
|
+
TSBooleanKeyword: "TSBooleanKeyword",
|
|
98
|
+
TSCallSignatureDeclaration: "TSCallSignatureDeclaration",
|
|
99
|
+
TSClassImplements: "TSClassImplements",
|
|
100
|
+
TSConditionalType: "TSConditionalType",
|
|
101
|
+
TSConstructorType: "TSConstructorType",
|
|
102
|
+
TSConstructSignatureDeclaration: "TSConstructSignatureDeclaration",
|
|
103
|
+
TSDeclareFunction: "TSDeclareFunction",
|
|
104
|
+
TSDeclareKeyword: "TSDeclareKeyword",
|
|
105
|
+
TSEmptyBodyFunctionExpression: "TSEmptyBodyFunctionExpression",
|
|
106
|
+
TSEnumBody: "TSEnumBody",
|
|
107
|
+
TSEnumDeclaration: "TSEnumDeclaration",
|
|
108
|
+
TSEnumMember: "TSEnumMember",
|
|
109
|
+
TSExportAssignment: "TSExportAssignment",
|
|
110
|
+
TSExportKeyword: "TSExportKeyword",
|
|
111
|
+
TSExternalModuleReference: "TSExternalModuleReference",
|
|
112
|
+
TSFunctionType: "TSFunctionType",
|
|
113
|
+
TSImportEqualsDeclaration: "TSImportEqualsDeclaration",
|
|
114
|
+
TSImportType: "TSImportType",
|
|
115
|
+
TSIndexedAccessType: "TSIndexedAccessType",
|
|
116
|
+
TSIndexSignature: "TSIndexSignature",
|
|
117
|
+
TSInferType: "TSInferType",
|
|
118
|
+
TSInstantiationExpression: "TSInstantiationExpression",
|
|
119
|
+
TSInterfaceBody: "TSInterfaceBody",
|
|
120
|
+
TSInterfaceDeclaration: "TSInterfaceDeclaration",
|
|
121
|
+
TSInterfaceHeritage: "TSInterfaceHeritage",
|
|
122
|
+
TSIntersectionType: "TSIntersectionType",
|
|
123
|
+
TSIntrinsicKeyword: "TSIntrinsicKeyword",
|
|
124
|
+
TSLiteralType: "TSLiteralType",
|
|
125
|
+
TSMappedType: "TSMappedType",
|
|
126
|
+
TSMethodSignature: "TSMethodSignature",
|
|
127
|
+
TSModuleBlock: "TSModuleBlock",
|
|
128
|
+
TSModuleDeclaration: "TSModuleDeclaration",
|
|
129
|
+
TSNamedTupleMember: "TSNamedTupleMember",
|
|
130
|
+
TSNamespaceExportDeclaration: "TSNamespaceExportDeclaration",
|
|
131
|
+
TSNeverKeyword: "TSNeverKeyword",
|
|
132
|
+
TSNonNullExpression: "TSNonNullExpression",
|
|
133
|
+
TSNullKeyword: "TSNullKeyword",
|
|
134
|
+
TSNumberKeyword: "TSNumberKeyword",
|
|
135
|
+
TSObjectKeyword: "TSObjectKeyword",
|
|
136
|
+
TSOptionalType: "TSOptionalType",
|
|
137
|
+
TSParameterProperty: "TSParameterProperty",
|
|
138
|
+
TSPrivateKeyword: "TSPrivateKeyword",
|
|
139
|
+
TSPropertySignature: "TSPropertySignature",
|
|
140
|
+
TSProtectedKeyword: "TSProtectedKeyword",
|
|
141
|
+
TSPublicKeyword: "TSPublicKeyword",
|
|
142
|
+
TSQualifiedName: "TSQualifiedName",
|
|
143
|
+
TSReadonlyKeyword: "TSReadonlyKeyword",
|
|
144
|
+
TSRestType: "TSRestType",
|
|
145
|
+
TSSatisfiesExpression: "TSSatisfiesExpression",
|
|
146
|
+
TSStaticKeyword: "TSStaticKeyword",
|
|
147
|
+
TSStringKeyword: "TSStringKeyword",
|
|
148
|
+
TSSymbolKeyword: "TSSymbolKeyword",
|
|
149
|
+
TSTemplateLiteralType: "TSTemplateLiteralType",
|
|
150
|
+
TSThisType: "TSThisType",
|
|
151
|
+
TSTupleType: "TSTupleType",
|
|
152
|
+
TSTypeAliasDeclaration: "TSTypeAliasDeclaration",
|
|
153
|
+
TSTypeAnnotation: "TSTypeAnnotation",
|
|
154
|
+
TSTypeAssertion: "TSTypeAssertion",
|
|
155
|
+
TSTypeLiteral: "TSTypeLiteral",
|
|
156
|
+
TSTypeOperator: "TSTypeOperator",
|
|
157
|
+
TSTypeParameter: "TSTypeParameter",
|
|
158
|
+
TSTypeParameterDeclaration: "TSTypeParameterDeclaration",
|
|
159
|
+
TSTypeParameterInstantiation: "TSTypeParameterInstantiation",
|
|
160
|
+
TSTypePredicate: "TSTypePredicate",
|
|
161
|
+
TSTypeQuery: "TSTypeQuery",
|
|
162
|
+
TSTypeReference: "TSTypeReference",
|
|
163
|
+
TSUndefinedKeyword: "TSUndefinedKeyword",
|
|
164
|
+
TSUnionType: "TSUnionType",
|
|
165
|
+
TSUnknownKeyword: "TSUnknownKeyword",
|
|
166
|
+
TSVoidKeyword: "TSVoidKeyword",
|
|
167
|
+
UnaryExpression: "UnaryExpression",
|
|
168
|
+
UpdateExpression: "UpdateExpression",
|
|
169
|
+
VariableDeclaration: "VariableDeclaration",
|
|
170
|
+
VariableDeclarator: "VariableDeclarator",
|
|
171
|
+
WhileStatement: "WhileStatement",
|
|
172
|
+
WithStatement: "WithStatement",
|
|
173
|
+
YieldExpression: "YieldExpression",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
module.exports = { AST_NODE_TYPES };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const rules = require("./rules");
|
|
2
|
+
const { name, version } = require("../package.json");
|
|
3
|
+
const { allRules } = require("./configs");
|
|
4
|
+
|
|
5
|
+
/** @import {ESLint} from "eslint" */
|
|
6
|
+
|
|
7
|
+
/** @type {ESLint.Plugin} */
|
|
8
|
+
const plugin = {
|
|
9
|
+
meta: {
|
|
10
|
+
name,
|
|
11
|
+
version,
|
|
12
|
+
},
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
rules,
|
|
15
|
+
configs: {
|
|
16
|
+
recommended: {
|
|
17
|
+
rules: allRules,
|
|
18
|
+
plugins: {
|
|
19
|
+
/** @returns {ESLint.Plugin} */
|
|
20
|
+
get ["@html-eslint/react"]() {
|
|
21
|
+
return require(".");
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
all: {
|
|
26
|
+
rules: allRules,
|
|
27
|
+
plugins: {
|
|
28
|
+
/** @returns {ESLint.Plugin} */
|
|
29
|
+
get ["@html-eslint/react"]() {
|
|
30
|
+
return require(".");
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = plugin;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const noInvalidAttrValue = require("./no-invalid-attr-value");
|
|
2
|
+
const useBaseline = require("./use-baseline");
|
|
3
|
+
const noIneffectiveAttrs = require("./no-ineffective-attrs");
|
|
4
|
+
const noObsoleteTags = require("./no-obsolete-tags");
|
|
5
|
+
const noObsoleteAttrs = require("./no-obsolete-attrs");
|
|
6
|
+
|
|
7
|
+
const rules = {
|
|
8
|
+
"no-invalid-attr-value": noInvalidAttrValue,
|
|
9
|
+
"use-baseline": useBaseline,
|
|
10
|
+
"no-ineffective-attrs": noIneffectiveAttrs,
|
|
11
|
+
"no-obsolete-tags": noObsoleteTags,
|
|
12
|
+
"no-obsolete-attrs": noObsoleteAttrs,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = rules;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {
|
|
3
|
+
* JSXAttribute,
|
|
4
|
+
* JSXOpeningElement,
|
|
5
|
+
* JSXSpreadAttribute,
|
|
6
|
+
* RuleModule
|
|
7
|
+
* } from "../types"
|
|
8
|
+
*/
|
|
9
|
+
const {
|
|
10
|
+
noIneffectiveAttrs,
|
|
11
|
+
NO_INEFFECTIVE_ATTRS_MESSAGE_IDS,
|
|
12
|
+
} = require("@html-eslint/core");
|
|
13
|
+
const { elementNodeAdapter } = require("./utils/adapter");
|
|
14
|
+
const { AST_NODE_TYPES } = require("../constants/node-types");
|
|
15
|
+
|
|
16
|
+
/** @type {RuleModule<[]>} */
|
|
17
|
+
module.exports = {
|
|
18
|
+
meta: {
|
|
19
|
+
type: "problem",
|
|
20
|
+
|
|
21
|
+
docs: {
|
|
22
|
+
description:
|
|
23
|
+
"Disallow HTML attributes that have no effect in their context",
|
|
24
|
+
category: "Best Practice",
|
|
25
|
+
recommended: true,
|
|
26
|
+
url: "https://html-eslint.org/docs/react/rules/no-ineffective-attrs",
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
fixable: null,
|
|
30
|
+
schema: [],
|
|
31
|
+
messages: {
|
|
32
|
+
[NO_INEFFECTIVE_ATTRS_MESSAGE_IDS.ineffective]: "{{ message }}",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
create(context) {
|
|
37
|
+
const ruleCore = /**
|
|
38
|
+
* @type {ReturnType<
|
|
39
|
+
* typeof noIneffectiveAttrs<
|
|
40
|
+
* JSXOpeningElement,
|
|
41
|
+
* JSXSpreadAttribute | JSXAttribute["name"] | null,
|
|
42
|
+
* JSXAttribute["value"]
|
|
43
|
+
* >
|
|
44
|
+
* >}
|
|
45
|
+
*/ (noIneffectiveAttrs());
|
|
46
|
+
return {
|
|
47
|
+
JSXOpeningElement(node) {
|
|
48
|
+
if (
|
|
49
|
+
node.name.type !== AST_NODE_TYPES.JSXIdentifier ||
|
|
50
|
+
node.name.name.toLocaleLowerCase() !== node.name.name ||
|
|
51
|
+
node.name.name.includes("-")
|
|
52
|
+
) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const adapter = elementNodeAdapter(node);
|
|
56
|
+
const result = ruleCore.checkAttributes(adapter);
|
|
57
|
+
for (const r of result) {
|
|
58
|
+
context.report({
|
|
59
|
+
node: r.node || undefined,
|
|
60
|
+
messageId: r.messageId,
|
|
61
|
+
data: r.data,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {
|
|
3
|
+
* JSXAttribute,
|
|
4
|
+
* JSXOpeningElement,
|
|
5
|
+
* JSXSpreadAttribute,
|
|
6
|
+
* RuleModule
|
|
7
|
+
* } from "../types"
|
|
8
|
+
*/
|
|
9
|
+
const { NO_INVALID_ATTR_VALUE_MESSAGE_IDS } = require("@html-eslint/core");
|
|
10
|
+
const { elementNodeAdapter } = require("./utils/adapter");
|
|
11
|
+
const { noInvalidAttrValue } = require("@html-eslint/core");
|
|
12
|
+
const { AST_NODE_TYPES } = require("../constants/node-types");
|
|
13
|
+
/**
|
|
14
|
+
* @type {RuleModule<
|
|
15
|
+
* [{ allow?: { tag: string; attr: string; valuePattern?: string }[] }]
|
|
16
|
+
* >}
|
|
17
|
+
*/
|
|
18
|
+
module.exports = {
|
|
19
|
+
meta: {
|
|
20
|
+
type: "problem",
|
|
21
|
+
|
|
22
|
+
docs: {
|
|
23
|
+
description:
|
|
24
|
+
"Disallow invalid attribute values according to HTML standards",
|
|
25
|
+
category: "Best Practice",
|
|
26
|
+
recommended: true,
|
|
27
|
+
url: "https://html-eslint.org/docs/react/rules/no-invalid-attr-value",
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
fixable: null,
|
|
31
|
+
schema: [
|
|
32
|
+
{
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
allow: {
|
|
36
|
+
type: "array",
|
|
37
|
+
items: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
tag: {
|
|
41
|
+
type: "string",
|
|
42
|
+
},
|
|
43
|
+
attr: {
|
|
44
|
+
type: "string",
|
|
45
|
+
},
|
|
46
|
+
valuePattern: {
|
|
47
|
+
type: "string",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
required: ["tag", "attr"],
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
messages: {
|
|
59
|
+
[NO_INVALID_ATTR_VALUE_MESSAGE_IDS.invalid]:
|
|
60
|
+
"Invalid value '{{value}}' for attribute '{{attr}}' on <{{element}}>. {{suggestion}}",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
create(context) {
|
|
65
|
+
const options = context.options[0] || {};
|
|
66
|
+
const ruleCore = /**
|
|
67
|
+
* @type {ReturnType<
|
|
68
|
+
* typeof noInvalidAttrValue<
|
|
69
|
+
* JSXOpeningElement,
|
|
70
|
+
* JSXSpreadAttribute | JSXAttribute["name"] | null,
|
|
71
|
+
* JSXAttribute["value"]
|
|
72
|
+
* >
|
|
73
|
+
* >}
|
|
74
|
+
*/ (noInvalidAttrValue(options));
|
|
75
|
+
return {
|
|
76
|
+
JSXOpeningElement(node) {
|
|
77
|
+
if (
|
|
78
|
+
node.name.type !== AST_NODE_TYPES.JSXIdentifier ||
|
|
79
|
+
node.name.name.toLocaleLowerCase() !== node.name.name ||
|
|
80
|
+
node.name.name.includes("-")
|
|
81
|
+
) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const adapter = elementNodeAdapter(node);
|
|
85
|
+
const result = ruleCore.checkAttributes(adapter);
|
|
86
|
+
for (const r of result) {
|
|
87
|
+
context.report({
|
|
88
|
+
node: r.node || undefined,
|
|
89
|
+
messageId: r.messageId,
|
|
90
|
+
data: r.data,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {TSESTree} from "@typescript-eslint/types"
|
|
3
|
+
* @import {RuleModule} from "../types"
|
|
4
|
+
*/
|
|
5
|
+
const {
|
|
6
|
+
noObsoleteAttrs,
|
|
7
|
+
NO_OBSOLETE_ATTRS_MESSAGE_IDS,
|
|
8
|
+
} = require("@html-eslint/core");
|
|
9
|
+
const { elementNodeAdapter } = require("./utils/adapter");
|
|
10
|
+
const { AST_NODE_TYPES } = require("../constants/node-types");
|
|
11
|
+
|
|
12
|
+
/** @type {RuleModule<[]>} */
|
|
13
|
+
module.exports = {
|
|
14
|
+
meta: {
|
|
15
|
+
type: "problem",
|
|
16
|
+
|
|
17
|
+
docs: {
|
|
18
|
+
description: "Disallow use of obsolete attributes in HTML5",
|
|
19
|
+
category: "Best Practice",
|
|
20
|
+
recommended: true,
|
|
21
|
+
url: "https://html-eslint.org/docs/react/rules/no-obsolete-attrs",
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
fixable: null,
|
|
25
|
+
schema: [],
|
|
26
|
+
messages: {
|
|
27
|
+
[NO_OBSOLETE_ATTRS_MESSAGE_IDS.obsolete]:
|
|
28
|
+
"The {{attr}} attribute on <{{element}}> is obsolete. {{suggestion}}",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
create(context) {
|
|
33
|
+
const ruleCore = noObsoleteAttrs();
|
|
34
|
+
return {
|
|
35
|
+
JSXOpeningElement(node) {
|
|
36
|
+
if (
|
|
37
|
+
node.name.type !== AST_NODE_TYPES.JSXIdentifier ||
|
|
38
|
+
node.name.name.toLocaleLowerCase() !== node.name.name ||
|
|
39
|
+
node.name.name.includes("-")
|
|
40
|
+
) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const adapter = elementNodeAdapter(node);
|
|
44
|
+
const result = ruleCore.checkAttributes(adapter);
|
|
45
|
+
for (const r of result) {
|
|
46
|
+
context.report({
|
|
47
|
+
node: r.node || undefined,
|
|
48
|
+
messageId: r.messageId,
|
|
49
|
+
data: r.data,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** @import {RuleModule} from "../types" */
|
|
2
|
+
const {
|
|
3
|
+
noObsoleteTags,
|
|
4
|
+
NO_OBSOLETE_TAGS_MESSAGE_IDS,
|
|
5
|
+
} = require("@html-eslint/core");
|
|
6
|
+
const { elementNodeAdapter } = require("./utils/adapter");
|
|
7
|
+
const { AST_NODE_TYPES } = require("../constants/node-types");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @type {RuleModule<
|
|
11
|
+
* [{ allow?: { tag: string; attr: string; valuePattern?: string }[] }]
|
|
12
|
+
* >}
|
|
13
|
+
*/
|
|
14
|
+
module.exports = {
|
|
15
|
+
meta: {
|
|
16
|
+
type: "code",
|
|
17
|
+
docs: {
|
|
18
|
+
description: "Disallow use of obsolete elements in HTML5",
|
|
19
|
+
category: "Best Practice",
|
|
20
|
+
recommended: true,
|
|
21
|
+
},
|
|
22
|
+
fixable: null,
|
|
23
|
+
schema: [],
|
|
24
|
+
messages: {
|
|
25
|
+
[NO_OBSOLETE_TAGS_MESSAGE_IDS.unexpected]:
|
|
26
|
+
"Unexpected use of obsolete tag <{{tag}}>",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
create(context) {
|
|
31
|
+
const ruleCore = noObsoleteTags();
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
JSXOpeningElement(node) {
|
|
35
|
+
if (
|
|
36
|
+
node.name.type !== AST_NODE_TYPES.JSXIdentifier ||
|
|
37
|
+
node.name.name.toLocaleLowerCase() !== node.name.name ||
|
|
38
|
+
node.name.name.includes("-")
|
|
39
|
+
) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const adapter = elementNodeAdapter(node);
|
|
43
|
+
const result = ruleCore.checkElement(adapter);
|
|
44
|
+
for (const r of result) {
|
|
45
|
+
context.report({
|
|
46
|
+
node: r.node,
|
|
47
|
+
messageId: r.messageId,
|
|
48
|
+
data: r.data,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {
|
|
3
|
+
* JSXAttribute,
|
|
4
|
+
* JSXOpeningElement,
|
|
5
|
+
* JSXSpreadAttribute,
|
|
6
|
+
* RuleModule
|
|
7
|
+
* } from "../types"
|
|
8
|
+
* @typedef {Object} Option
|
|
9
|
+
* @property {"widely" | "newly" | number} Option.available
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { useBaseline, USE_BASELINE_MESSAGE_IDS } = require("@html-eslint/core");
|
|
13
|
+
const { elementNodeAdapter } = require("./utils/adapter");
|
|
14
|
+
const { AST_NODE_TYPES } = require("../constants/node-types");
|
|
15
|
+
|
|
16
|
+
/** @type {RuleModule<[Option]>} */
|
|
17
|
+
module.exports = {
|
|
18
|
+
meta: {
|
|
19
|
+
type: "code",
|
|
20
|
+
docs: {
|
|
21
|
+
description: "Enforce the use of baseline features.",
|
|
22
|
+
recommended: true,
|
|
23
|
+
category: "Best Practice",
|
|
24
|
+
url: "https://html-eslint.org/docs/react/rules/use-baseline",
|
|
25
|
+
},
|
|
26
|
+
fixable: null,
|
|
27
|
+
schema: [
|
|
28
|
+
{
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
available: {
|
|
32
|
+
anyOf: [
|
|
33
|
+
{
|
|
34
|
+
enum: ["widely", "newly"],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
// baseline year
|
|
38
|
+
type: "integer",
|
|
39
|
+
minimum: 2000,
|
|
40
|
+
maximum: new Date().getFullYear(),
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
|
|
49
|
+
messages: {
|
|
50
|
+
[USE_BASELINE_MESSAGE_IDS.noBaselineElement]:
|
|
51
|
+
"Element '{{element}}' is not a {{availability}} available baseline feature.",
|
|
52
|
+
[USE_BASELINE_MESSAGE_IDS.notBaselineElementAttribute]:
|
|
53
|
+
"Attribute '{{attr}}' on '{{element}}' is not a {{availability}} available baseline feature.",
|
|
54
|
+
[USE_BASELINE_MESSAGE_IDS.notBaselineGlobalAttribute]:
|
|
55
|
+
"Attribute '{{attr}}' is not a {{availability}} available baseline feature.",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
create(context) {
|
|
60
|
+
const options = context.options[0] || { available: "widely" };
|
|
61
|
+
const ruleCore = /**
|
|
62
|
+
* @type {ReturnType<
|
|
63
|
+
* typeof useBaseline<
|
|
64
|
+
* JSXOpeningElement,
|
|
65
|
+
* JSXSpreadAttribute | JSXAttribute["name"] | null,
|
|
66
|
+
* JSXAttribute["value"]
|
|
67
|
+
* >
|
|
68
|
+
* >}
|
|
69
|
+
*/ (useBaseline(options));
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
JSXOpeningElement(node) {
|
|
73
|
+
if (
|
|
74
|
+
node.name.type !== AST_NODE_TYPES.JSXIdentifier ||
|
|
75
|
+
node.name.name.toLocaleLowerCase() !== node.name.name ||
|
|
76
|
+
node.name.name.includes("-")
|
|
77
|
+
) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const adapter = elementNodeAdapter(node);
|
|
81
|
+
const result = ruleCore.checkAttributes(adapter);
|
|
82
|
+
for (const r of result) {
|
|
83
|
+
context.report({
|
|
84
|
+
messageId: r.messageId,
|
|
85
|
+
data: r.data,
|
|
86
|
+
node: r.node || undefined,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|