@elastic/eslint-plugin-eui 2.6.0 → 2.8.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/README.md +15 -0
- package/lib/cjs/index.js +33 -24
- package/lib/cjs/rules/a11y/badge_accessibility_rules.d.ts +3 -0
- package/lib/cjs/rules/a11y/badge_accessibility_rules.d.ts.map +1 -0
- package/lib/cjs/rules/a11y/badge_accessibility_rules.js +82 -0
- package/lib/cjs/rules/a11y/icon_accessibility_rules.d.ts +3 -0
- package/lib/cjs/rules/a11y/icon_accessibility_rules.d.ts.map +1 -0
- package/lib/cjs/rules/a11y/icon_accessibility_rules.js +82 -0
- package/lib/cjs/rules/no_css_color.d.ts.map +1 -1
- package/lib/cjs/rules/no_css_color.js +6 -4
- package/lib/cjs/rules/no_static_z_index.d.ts +3 -0
- package/lib/cjs/rules/no_static_z_index.d.ts.map +1 -0
- package/lib/cjs/rules/no_static_z_index.js +361 -0
- package/lib/cjs/utils/get_property_name.d.ts +3 -0
- package/lib/cjs/utils/get_property_name.d.ts.map +1 -0
- package/lib/cjs/utils/get_property_name.js +28 -0
- package/lib/cjs/utils/remove_attr.d.ts +26 -0
- package/lib/cjs/utils/remove_attr.d.ts.map +1 -0
- package/lib/cjs/utils/remove_attr.js +39 -0
- package/lib/esm/index.js +33 -24
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/rules/a11y/badge_accessibility_rules.d.ts +2 -0
- package/lib/esm/rules/a11y/badge_accessibility_rules.js +84 -0
- package/lib/esm/rules/a11y/badge_accessibility_rules.js.map +1 -0
- package/lib/esm/rules/a11y/icon_accessibility_rules.d.ts +2 -0
- package/lib/esm/rules/a11y/icon_accessibility_rules.js +87 -0
- package/lib/esm/rules/a11y/icon_accessibility_rules.js.map +1 -0
- package/lib/esm/rules/no_css_color.js +10 -7
- package/lib/esm/rules/no_css_color.js.map +1 -1
- package/lib/esm/rules/no_static_z_index.d.ts +2 -0
- package/lib/esm/rules/no_static_z_index.js +394 -0
- package/lib/esm/rules/no_static_z_index.js.map +1 -0
- package/lib/esm/utils/get_property_name.d.ts +2 -0
- package/lib/esm/utils/get_property_name.js +27 -0
- package/lib/esm/utils/get_property_name.js.map +1 -0
- package/lib/esm/utils/remove_attr.d.ts +25 -0
- package/lib/esm/utils/remove_attr.js +34 -0
- package/lib/esm/utils/remove_attr.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NoStaticZIndex = void 0;
|
|
7
|
+
var _utils = require("@typescript-eslint/utils");
|
|
8
|
+
var _get_property_name = require("../utils/get_property_name");
|
|
9
|
+
/*
|
|
10
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
11
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
12
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
13
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
14
|
+
* Side Public License, v 1.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const propertiesCheckingZIndex = ['zIndex', 'z-index'];
|
|
18
|
+
const zIndexDeclarationRegex = RegExp(String.raw`(${propertiesCheckingZIndex.join('|')})`);
|
|
19
|
+
|
|
20
|
+
// Regex to find z-index declarations in CSS strings
|
|
21
|
+
// Matches:
|
|
22
|
+
// 1. z-index property name (case insensitive)
|
|
23
|
+
// 2. colon
|
|
24
|
+
// 3. value (captured group 1) until semicolon, closing brace, or !important
|
|
25
|
+
const cssZIndexRegex = /z-index\s*:\s*([^;!}]+)/gi;
|
|
26
|
+
const checkPropertySpecifiesInvalidZIndex = (property, value) => {
|
|
27
|
+
if (!property || value === undefined || value === null) return false;
|
|
28
|
+
const normalizedProperty = property.trim();
|
|
29
|
+
const isZIndex = propertiesCheckingZIndex.includes(normalizedProperty);
|
|
30
|
+
if (!isZIndex) return false;
|
|
31
|
+
return isInvalidZIndexValue(value);
|
|
32
|
+
};
|
|
33
|
+
const isInvalidZIndexValue = value => {
|
|
34
|
+
const allowedCssKeywords = ['auto', 'inherit', 'initial', 'unset', 'revert', 'revert-layer'];
|
|
35
|
+
const stringValue = String(value).trim().toLowerCase();
|
|
36
|
+
if (allowedCssKeywords.includes(stringValue)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if it's a number (positive, negative, or zero)
|
|
41
|
+
// This regex allows integers. z-index only accepts integers.
|
|
42
|
+
if (/^-?\d+$/.test(stringValue)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
};
|
|
47
|
+
const raiseReportIfPropertyHasInvalidZIndex = (context, propertyNode, messageToReport) => {
|
|
48
|
+
let didReport = false;
|
|
49
|
+
const propertyName = (0, _get_property_name.getPropertyName)(propertyNode);
|
|
50
|
+
if (!propertyName || !zIndexDeclarationRegex.test(propertyName)) {
|
|
51
|
+
return didReport;
|
|
52
|
+
}
|
|
53
|
+
const visitNode = node => {
|
|
54
|
+
// Handle Literal values: zIndex: 10, 'z-index': '10'
|
|
55
|
+
if (node.type === 'Literal') {
|
|
56
|
+
if (checkPropertySpecifiesInvalidZIndex(propertyName, node.value)) {
|
|
57
|
+
didReport = true;
|
|
58
|
+
context.report({
|
|
59
|
+
...messageToReport,
|
|
60
|
+
loc: node.loc
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Handle Identifier values: zIndex: someVar
|
|
65
|
+
else if (node.type === 'Identifier') {
|
|
66
|
+
const identifierName = node.name;
|
|
67
|
+
const identifierDeclaration = context.sourceCode.getScope(node).variables.find(variable => variable.name === identifierName);
|
|
68
|
+
const identifierDeclarationInit = identifierDeclaration?.defs[0]?.node.type === 'VariableDeclarator' ? identifierDeclaration.defs[0].node.init : undefined;
|
|
69
|
+
if (identifierDeclarationInit?.type === 'Literal' && checkPropertySpecifiesInvalidZIndex(propertyName, identifierDeclarationInit.value)) {
|
|
70
|
+
didReport = true;
|
|
71
|
+
context.report({
|
|
72
|
+
loc: node.loc,
|
|
73
|
+
messageId: 'noStaticZIndexSpecificDeclaredVariable',
|
|
74
|
+
data: {
|
|
75
|
+
property: propertyName,
|
|
76
|
+
line: String(node.loc.start.line),
|
|
77
|
+
variableName: node.name
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
} else if (node.type === 'ConditionalExpression') {
|
|
82
|
+
visitNode(node.consequent);
|
|
83
|
+
visitNode(node.alternate);
|
|
84
|
+
} else if (node.type === 'LogicalExpression') {
|
|
85
|
+
visitNode(node.left);
|
|
86
|
+
visitNode(node.right);
|
|
87
|
+
} else if (node.type === 'TSAsExpression') {
|
|
88
|
+
visitNode(node.expression);
|
|
89
|
+
} else if (node.type === 'UnaryExpression') {
|
|
90
|
+
if (node.operator === '-') {
|
|
91
|
+
if (node.argument.type === 'Literal' && typeof node.argument.value === 'number') {
|
|
92
|
+
if (checkPropertySpecifiesInvalidZIndex(propertyName, -node.argument.value)) {
|
|
93
|
+
didReport = true;
|
|
94
|
+
context.report({
|
|
95
|
+
...messageToReport,
|
|
96
|
+
loc: node.loc
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
visitNode(node.argument);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
if (propertyNode.value) {
|
|
106
|
+
visitNode(propertyNode.value);
|
|
107
|
+
}
|
|
108
|
+
return didReport;
|
|
109
|
+
};
|
|
110
|
+
const handleObjectProperties = (context, propertyParentNode, property, reportMessage) => {
|
|
111
|
+
if (property.type === 'Property') {
|
|
112
|
+
if (property.value.type === 'ObjectExpression') {
|
|
113
|
+
property.value.properties.forEach(nestedProperty => {
|
|
114
|
+
const nestedReportMessage = {
|
|
115
|
+
...reportMessage,
|
|
116
|
+
loc: nestedProperty.loc
|
|
117
|
+
};
|
|
118
|
+
if (nestedReportMessage.data) {
|
|
119
|
+
const newData = {
|
|
120
|
+
...nestedReportMessage.data,
|
|
121
|
+
property: (0, _get_property_name.getPropertyName)(nestedProperty) || 'unknown'
|
|
122
|
+
};
|
|
123
|
+
if ('line' in newData) {
|
|
124
|
+
newData.line = String(nestedProperty.loc.start.line);
|
|
125
|
+
}
|
|
126
|
+
nestedReportMessage.data = newData;
|
|
127
|
+
}
|
|
128
|
+
handleObjectProperties(context, propertyParentNode, nestedProperty, nestedReportMessage);
|
|
129
|
+
});
|
|
130
|
+
} else {
|
|
131
|
+
raiseReportIfPropertyHasInvalidZIndex(context, property, reportMessage);
|
|
132
|
+
}
|
|
133
|
+
} else if (property.type === 'SpreadElement') {
|
|
134
|
+
if (property.argument.type !== 'Identifier') {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const spreadElementIdentifierName = property.argument.name;
|
|
138
|
+
let scopeNode = propertyParentNode;
|
|
139
|
+
if (propertyParentNode.type === 'JSXAttribute' && propertyParentNode.value?.type === 'JSXExpressionContainer') {
|
|
140
|
+
scopeNode = propertyParentNode.value.expression;
|
|
141
|
+
}
|
|
142
|
+
const spreadElementDeclaration = context.sourceCode.getScope(scopeNode).references.find(ref => ref.identifier.name === spreadElementIdentifierName)?.resolved;
|
|
143
|
+
if (!spreadElementDeclaration) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const propertyName = (0, _get_property_name.getPropertyName)(property) || 'spread';
|
|
147
|
+
reportMessage = {
|
|
148
|
+
loc: propertyParentNode.loc,
|
|
149
|
+
messageId: 'noStaticZIndexSpecificDeclaredVariable',
|
|
150
|
+
data: {
|
|
151
|
+
property: propertyName,
|
|
152
|
+
variableName: spreadElementIdentifierName,
|
|
153
|
+
line: String(property.loc.start.line)
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const spreadElementDeclarationNode = spreadElementDeclaration.defs[0]?.node.type === 'VariableDeclarator' ? spreadElementDeclaration.defs[0].node.init : undefined;
|
|
157
|
+
if (spreadElementDeclarationNode?.type === 'ObjectExpression') {
|
|
158
|
+
spreadElementDeclarationNode.properties.forEach(spreadProperty => {
|
|
159
|
+
handleObjectProperties(context, propertyParentNode, spreadProperty, reportMessage);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const checkTemplateLiteralForZIndex = (context, node) => {
|
|
165
|
+
for (let i = 0; i < node.quasis.length; i++) {
|
|
166
|
+
const declarationTemplateNode = node.quasis[i];
|
|
167
|
+
const rawValue = declarationTemplateNode.value.raw;
|
|
168
|
+
// Strip comments
|
|
169
|
+
const valueWithoutComments = rawValue.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
170
|
+
let match;
|
|
171
|
+
// reset regex state
|
|
172
|
+
cssZIndexRegex.lastIndex = 0;
|
|
173
|
+
while ((match = cssZIndexRegex.exec(valueWithoutComments)) !== null) {
|
|
174
|
+
const value = match[1].trim();
|
|
175
|
+
if (isInvalidZIndexValue(value)) {
|
|
176
|
+
context.report({
|
|
177
|
+
node: declarationTemplateNode,
|
|
178
|
+
messageId: 'noStaticZIndex'
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
const NoStaticZIndex = exports.NoStaticZIndex = _utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
185
|
+
create(context) {
|
|
186
|
+
return {
|
|
187
|
+
TaggedTemplateExpression(node) {
|
|
188
|
+
if (node.tag.type !== 'Identifier' || node.tag.type === 'Identifier' && node.tag.name !== 'css') {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
checkTemplateLiteralForZIndex(context, node.quasi);
|
|
192
|
+
},
|
|
193
|
+
JSXAttribute(node) {
|
|
194
|
+
if (!(node.name.name === 'style' || node.name.name === 'css')) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (!node.value || node.value.type !== 'JSXExpressionContainer') {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const expression = node.value.expression;
|
|
201
|
+
|
|
202
|
+
// Handle identifier expression: style={someStyle}
|
|
203
|
+
if (expression.type === 'Identifier') {
|
|
204
|
+
const styleVariableName = expression.name;
|
|
205
|
+
const nodeScope = context.sourceCode.getScope(expression);
|
|
206
|
+
const variableDeclarationMatches = nodeScope.references.find(ref => ref.identifier.name === styleVariableName)?.resolved;
|
|
207
|
+
let variableInitializationNode;
|
|
208
|
+
if (variableDeclarationMatches?.defs[0]?.node.type === 'VariableDeclarator' && variableDeclarationMatches.defs[0].node.init) {
|
|
209
|
+
variableInitializationNode = variableDeclarationMatches.defs[0].node.init;
|
|
210
|
+
if (variableInitializationNode.type === 'ObjectExpression') {
|
|
211
|
+
variableInitializationNode.properties.forEach(property => {
|
|
212
|
+
handleObjectProperties(context, node, property, {
|
|
213
|
+
loc: property.loc,
|
|
214
|
+
messageId: 'noStaticZIndexSpecificDeclaredVariable',
|
|
215
|
+
data: {
|
|
216
|
+
property: (0, _get_property_name.getPropertyName)(property) || 'unknown',
|
|
217
|
+
variableName: styleVariableName,
|
|
218
|
+
line: String(property.loc.start.line)
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
} else if (variableInitializationNode.type === 'CallExpression' && variableInitializationNode.callee.type === 'Identifier' && variableInitializationNode.callee.name === 'css') {
|
|
223
|
+
variableInitializationNode.arguments.forEach(argument => {
|
|
224
|
+
if (argument.type === 'ObjectExpression') {
|
|
225
|
+
argument.properties.forEach(property => {
|
|
226
|
+
handleObjectProperties(context, node, property, {
|
|
227
|
+
loc: property.loc,
|
|
228
|
+
messageId: 'noStaticZIndexSpecificDeclaredVariable',
|
|
229
|
+
data: {
|
|
230
|
+
property: (0, _get_property_name.getPropertyName)(property) || 'unknown',
|
|
231
|
+
variableName: styleVariableName,
|
|
232
|
+
line: String(property.loc.start.line)
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Handle inline object: style={{ zIndex: 10 }}
|
|
244
|
+
if (expression.type === 'ObjectExpression') {
|
|
245
|
+
const declarationPropertiesNode = expression.properties;
|
|
246
|
+
declarationPropertiesNode?.forEach(property => {
|
|
247
|
+
handleObjectProperties(context, node, property, {
|
|
248
|
+
loc: property.loc,
|
|
249
|
+
messageId: 'noStaticZIndexSpecific',
|
|
250
|
+
data: {
|
|
251
|
+
property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Handle inline CallExpression: css={css({ zIndex: 10 })}
|
|
259
|
+
if (expression.type === 'CallExpression' && expression.callee.type === 'Identifier' && expression.callee.name === 'css') {
|
|
260
|
+
expression.arguments.forEach(argument => {
|
|
261
|
+
if (argument.type === 'ObjectExpression') {
|
|
262
|
+
argument.properties.forEach(property => {
|
|
263
|
+
handleObjectProperties(context, node, property, {
|
|
264
|
+
loc: node.loc,
|
|
265
|
+
messageId: 'noStaticZIndexSpecific',
|
|
266
|
+
data: {
|
|
267
|
+
property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Handle inline ArrayExpression: css={[...]}
|
|
277
|
+
if (expression.type === 'ArrayExpression') {
|
|
278
|
+
expression.elements.forEach(element => {
|
|
279
|
+
if (!element) return;
|
|
280
|
+
if (element.type === 'ObjectExpression') {
|
|
281
|
+
element.properties.forEach(property => {
|
|
282
|
+
handleObjectProperties(context, node, property, {
|
|
283
|
+
loc: property.loc,
|
|
284
|
+
messageId: 'noStaticZIndexSpecific',
|
|
285
|
+
data: {
|
|
286
|
+
property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
} else if (element.type === 'CallExpression' && element.callee.type === 'Identifier' && element.callee.name === 'css') {
|
|
291
|
+
element.arguments.forEach(argument => {
|
|
292
|
+
if (argument.type === 'ObjectExpression') {
|
|
293
|
+
argument.properties.forEach(property => {
|
|
294
|
+
handleObjectProperties(context, node, property, {
|
|
295
|
+
loc: property.loc,
|
|
296
|
+
messageId: 'noStaticZIndexSpecific',
|
|
297
|
+
data: {
|
|
298
|
+
property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Handle css prop with template literal or function
|
|
309
|
+
if (node.name.name === 'css') {
|
|
310
|
+
// css={`...`}
|
|
311
|
+
if (expression.type === 'TemplateLiteral') {
|
|
312
|
+
checkTemplateLiteralForZIndex(context, expression);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// css={() => ({ ... })} or css={function() { return { ... } }}
|
|
317
|
+
if (expression.type === 'FunctionExpression' || expression.type === 'ArrowFunctionExpression') {
|
|
318
|
+
let declarationPropertiesNode = [];
|
|
319
|
+
if (expression.body.type === 'ObjectExpression') {
|
|
320
|
+
declarationPropertiesNode = expression.body.properties;
|
|
321
|
+
}
|
|
322
|
+
if (expression.body.type === 'BlockStatement') {
|
|
323
|
+
const functionReturnStatementNode = expression.body.body?.find(_node => {
|
|
324
|
+
return _node.type === 'ReturnStatement';
|
|
325
|
+
});
|
|
326
|
+
if (functionReturnStatementNode?.type === 'ReturnStatement' && functionReturnStatementNode.argument?.type === 'ObjectExpression') {
|
|
327
|
+
declarationPropertiesNode = functionReturnStatementNode.argument.properties.filter(property => property.type === 'Property');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (!declarationPropertiesNode.length) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
declarationPropertiesNode.forEach(property => {
|
|
334
|
+
handleObjectProperties(context, node, property, {
|
|
335
|
+
loc: property.loc,
|
|
336
|
+
messageId: 'noStaticZIndexSpecific',
|
|
337
|
+
data: {
|
|
338
|
+
property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
meta: {
|
|
349
|
+
type: 'suggestion',
|
|
350
|
+
docs: {
|
|
351
|
+
description: 'Use `z-index` definitions from `euiTheme` as opposed to static values'
|
|
352
|
+
},
|
|
353
|
+
messages: {
|
|
354
|
+
noStaticZIndexSpecificDeclaredVariable: 'Avoid using a literal z-index value for "{{property}}", use an EUI theme z-index level instead in declared variable {{variableName}} on line {{line}}',
|
|
355
|
+
noStaticZIndexSpecific: 'Avoid using a literal z-index value for "{{property}}", use an EUI theme z-index level instead',
|
|
356
|
+
noStaticZIndex: 'Avoid using a literal z-index value, use an EUI theme z-index level instead'
|
|
357
|
+
},
|
|
358
|
+
schema: []
|
|
359
|
+
},
|
|
360
|
+
defaultOptions: []
|
|
361
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get_property_name.d.ts","sourceRoot":"","sources":["../../../src/utils/get_property_name.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,eAAO,MAAM,eAAe,GAC1B,cAAc,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,aAAa,KACvD,MAAM,GAAG,IAeX,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getPropertyName = void 0;
|
|
7
|
+
/*
|
|
8
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
9
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
10
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
11
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
12
|
+
* Side Public License, v 1.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const getPropertyName = propertyNode => {
|
|
16
|
+
if (propertyNode.type === 'Property') {
|
|
17
|
+
if (propertyNode.key.type === 'Identifier') {
|
|
18
|
+
return propertyNode.key.name;
|
|
19
|
+
}
|
|
20
|
+
if (propertyNode.key.type === 'Literal') {
|
|
21
|
+
return String(propertyNode.key.value);
|
|
22
|
+
}
|
|
23
|
+
} else if (propertyNode.type === 'SpreadElement' && propertyNode.argument.type === 'Identifier') {
|
|
24
|
+
return propertyNode.argument.name;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
exports.getPropertyName = getPropertyName;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type TSESTree, type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Computes the removal range for a JSX attribute, including a preceding space
|
|
4
|
+
* when present, to keep formatting intact after autofix.
|
|
5
|
+
*
|
|
6
|
+
* This helper is useful in ESLint rule fixers when calling `fixer.removeRange(...)`,
|
|
7
|
+
* ensuring that the attribute and its leading space are removed cleanly.
|
|
8
|
+
*
|
|
9
|
+
* @typeParam TContext - An ESLint rule context type extending `TSESLint.RuleContext`.
|
|
10
|
+
* @param context - The current ESLint rule context providing access to `SourceCode`.
|
|
11
|
+
* @param attr - The JSX attribute node to remove.
|
|
12
|
+
* @returns A readonly tuple `[start, end]` representing the inclusive start and exclusive end indexes for removal.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* context.report({
|
|
17
|
+
* node: openingElement,
|
|
18
|
+
* messageId: 'removeAttr',
|
|
19
|
+
* fix: fixer => {
|
|
20
|
+
* const [start, end] = removeAttribute(context, ariaHiddenAttr);
|
|
21
|
+
* return fixer.removeRange([start, end]);
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
**/
|
|
25
|
+
export declare function removeAttribute<TContext extends TSESLint.RuleContext<string, unknown[]>>(context: TContext, attr: TSESTree.JSXAttribute): readonly [number, number];
|
|
26
|
+
//# sourceMappingURL=remove_attr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove_attr.d.ts","sourceRoot":"","sources":["../../../src/utils/remove_attr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;IAsBI;AAEJ,wBAAgB,eAAe,CAC7B,QAAQ,SAAS,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,EAExD,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,QAAQ,CAAC,YAAY,6BAO5B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.removeAttribute = removeAttribute;
|
|
7
|
+
/**
|
|
8
|
+
* Computes the removal range for a JSX attribute, including a preceding space
|
|
9
|
+
* when present, to keep formatting intact after autofix.
|
|
10
|
+
*
|
|
11
|
+
* This helper is useful in ESLint rule fixers when calling `fixer.removeRange(...)`,
|
|
12
|
+
* ensuring that the attribute and its leading space are removed cleanly.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam TContext - An ESLint rule context type extending `TSESLint.RuleContext`.
|
|
15
|
+
* @param context - The current ESLint rule context providing access to `SourceCode`.
|
|
16
|
+
* @param attr - The JSX attribute node to remove.
|
|
17
|
+
* @returns A readonly tuple `[start, end]` representing the inclusive start and exclusive end indexes for removal.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* context.report({
|
|
22
|
+
* node: openingElement,
|
|
23
|
+
* messageId: 'removeAttr',
|
|
24
|
+
* fix: fixer => {
|
|
25
|
+
* const [start, end] = removeAttribute(context, ariaHiddenAttr);
|
|
26
|
+
* return fixer.removeRange([start, end]);
|
|
27
|
+
* },
|
|
28
|
+
* });
|
|
29
|
+
**/
|
|
30
|
+
|
|
31
|
+
function removeAttribute(context, attr) {
|
|
32
|
+
const {
|
|
33
|
+
sourceCode
|
|
34
|
+
} = context;
|
|
35
|
+
const start = attr.range[0];
|
|
36
|
+
const before = sourceCode.text[start - 1];
|
|
37
|
+
const rangeStart = before === ' ' ? start - 1 : start;
|
|
38
|
+
return [rangeStart, attr.range[1]];
|
|
39
|
+
}
|
package/lib/esm/index.js
CHANGED
|
@@ -7,52 +7,61 @@
|
|
|
7
7
|
* Side Public License, v 1.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const accessible_interactive_element_1 = require("./rules/a11y/accessible_interactive_element");
|
|
11
|
+
const callout_announce_on_mount_1 = require("./rules/a11y/callout_announce_on_mount");
|
|
12
|
+
const consistent_is_invalid_props_1 = require("./rules/a11y/consistent_is_invalid_props");
|
|
10
13
|
const href_or_on_click_1 = require("./rules/href_or_on_click");
|
|
11
|
-
const no_restricted_eui_imports_1 = require("./rules/no_restricted_eui_imports");
|
|
12
14
|
const no_css_color_1 = require("./rules/no_css_color");
|
|
15
|
+
const no_restricted_eui_imports_1 = require("./rules/no_restricted_eui_imports");
|
|
16
|
+
const no_static_z_index_1 = require("./rules/no_static_z_index");
|
|
17
|
+
const no_unnamed_interactive_element_1 = require("./rules/a11y/no_unnamed_interactive_element");
|
|
18
|
+
const no_unnamed_radio_group_1 = require("./rules/a11y/no_unnamed_radio_group");
|
|
19
|
+
const prefer_eui_icon_tip_1 = require("./rules/a11y/prefer_eui_icon_tip");
|
|
13
20
|
const require_aria_label_for_modals_1 = require("./rules/a11y/require_aria_label_for_modals");
|
|
14
|
-
const
|
|
21
|
+
const require_table_caption_1 = require("./rules/a11y/require_table_caption");
|
|
15
22
|
const sr_output_disabled_tooltip_1 = require("./rules/a11y/sr_output_disabled_tooltip");
|
|
16
|
-
const prefer_eui_icon_tip_1 = require("./rules/a11y/prefer_eui_icon_tip");
|
|
17
|
-
const no_unnamed_radio_group_1 = require("./rules/a11y/no_unnamed_radio_group");
|
|
18
|
-
const no_unnamed_interactive_element_1 = require("./rules/a11y/no_unnamed_interactive_element");
|
|
19
23
|
const tooltip_focusable_anchor_1 = require("./rules/a11y/tooltip_focusable_anchor");
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const require_table_caption_1 = require("./rules/a11y/require_table_caption");
|
|
24
|
+
const badge_accessibility_rules_1 = require("./rules/a11y/badge_accessibility_rules");
|
|
25
|
+
const icon_accessibility_rules_1 = require("./rules/a11y/icon_accessibility_rules");
|
|
23
26
|
const config = {
|
|
24
27
|
rules: {
|
|
28
|
+
'accessible-interactive-element': accessible_interactive_element_1.AccessibleInteractiveElements,
|
|
29
|
+
'callout-announce-on-mount': callout_announce_on_mount_1.CallOutAnnounceOnMount,
|
|
30
|
+
'consistent-is-invalid-props': consistent_is_invalid_props_1.ConsistentIsInvalidProps,
|
|
25
31
|
'href-or-on-click': href_or_on_click_1.HrefOnClick,
|
|
26
|
-
'no-restricted-eui-imports': no_restricted_eui_imports_1.NoRestrictedEuiImports,
|
|
27
32
|
'no-css-color': no_css_color_1.NoCssColor,
|
|
33
|
+
'no-restricted-eui-imports': no_restricted_eui_imports_1.NoRestrictedEuiImports,
|
|
34
|
+
'no-static-z-index': no_static_z_index_1.NoStaticZIndex,
|
|
35
|
+
'no-unnamed-interactive-element': no_unnamed_interactive_element_1.NoUnnamedInteractiveElement,
|
|
36
|
+
'no-unnamed-radio-group': no_unnamed_radio_group_1.NoUnnamedRadioGroup,
|
|
37
|
+
'prefer-eui-icon-tip': prefer_eui_icon_tip_1.PreferEuiIconTip,
|
|
28
38
|
'require-aria-label-for-modals': require_aria_label_for_modals_1.RequireAriaLabelForModals,
|
|
29
|
-
'
|
|
39
|
+
'require-table-caption': require_table_caption_1.RequireTableCaption,
|
|
30
40
|
'sr-output-disabled-tooltip': sr_output_disabled_tooltip_1.ScreenReaderOutputDisabledTooltip,
|
|
31
|
-
'prefer-eui-icon-tip': prefer_eui_icon_tip_1.PreferEuiIconTip,
|
|
32
|
-
'no-unnamed-radio-group': no_unnamed_radio_group_1.NoUnnamedRadioGroup,
|
|
33
|
-
'callout-announce-on-mount': callout_announce_on_mount_1.CallOutAnnounceOnMount,
|
|
34
|
-
'no-unnamed-interactive-element': no_unnamed_interactive_element_1.NoUnnamedInteractiveElement,
|
|
35
41
|
'tooltip-focusable-anchor': tooltip_focusable_anchor_1.TooltipFocusableAnchor,
|
|
36
|
-
'
|
|
37
|
-
'
|
|
42
|
+
'badge-accessibility-rules': badge_accessibility_rules_1.EuiBadgeAccessibilityRules,
|
|
43
|
+
'icon-accessibility-rules': icon_accessibility_rules_1.EuiIconAccessibilityRules
|
|
38
44
|
},
|
|
39
45
|
configs: {
|
|
40
46
|
recommended: {
|
|
41
47
|
plugins: ['@elastic/eslint-plugin-eui'],
|
|
42
48
|
rules: {
|
|
49
|
+
'@elastic/eui/accessible-interactive-element': 'warn',
|
|
50
|
+
'@elastic/eui/callout-announce-on-mount': 'warn',
|
|
51
|
+
'@elastic/eui/consistent-is-invalid-props': 'warn',
|
|
43
52
|
'@elastic/eui/href-or-on-click': 'warn',
|
|
44
|
-
'@elastic/eui/no-restricted-eui-imports': 'warn',
|
|
45
53
|
'@elastic/eui/no-css-color': 'warn',
|
|
54
|
+
'@elastic/eui/no-restricted-eui-imports': 'warn',
|
|
55
|
+
'@elastic/eui/no-static-z-index': 'warn',
|
|
56
|
+
'@elastic/eui/no-unnamed-interactive-element': 'warn',
|
|
57
|
+
'@elastic/eui/no-unnamed-radio-group': 'warn',
|
|
58
|
+
'@elastic/eui/prefer-eui-icon-tip': 'warn',
|
|
46
59
|
'@elastic/eui/require-aria-label-for-modals': 'warn',
|
|
47
|
-
'@elastic/eui/
|
|
60
|
+
'@elastic/eui/require-table-caption': 'warn',
|
|
48
61
|
'@elastic/eui/sr-output-disabled-tooltip': 'warn',
|
|
49
|
-
'@elastic/eui/prefer-eui-icon-tip': 'warn',
|
|
50
|
-
'@elastic/eui/no-unnamed-radio-group': 'warn',
|
|
51
|
-
'@elastic/eui/callout-announce-on-mount': 'warn',
|
|
52
|
-
'@elastic/eui/no-unnamed-interactive-element': 'warn',
|
|
53
62
|
'@elastic/eui/tooltip-focusable-anchor': 'warn',
|
|
54
|
-
'@elastic/eui/
|
|
55
|
-
'@elastic/eui/
|
|
63
|
+
'@elastic/eui/badge-accessibility-rules': 'warn',
|
|
64
|
+
'@elastic/eui/icon-accessibility-rules': 'warn',
|
|
56
65
|
},
|
|
57
66
|
},
|
|
58
67
|
},
|
package/lib/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAGH,+DAAuD;AACvD,iFAA2E;AAC3E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAGH,gGAA4F;AAC5F,sFAAgF;AAChF,0FAAoF;AACpF,+DAAuD;AACvD,uDAAkD;AAClD,iFAA2E;AAC3E,iEAA2D;AAC3D,gGAA0F;AAC1F,gFAA0E;AAC1E,0EAAoE;AACpE,8FAAuF;AACvF,8EAAyE;AACzE,wFAA4F;AAC5F,oFAA+E;AAC/E,sFAAoF;AACpF,oFAAkF;AAElF,MAAM,MAAM,GAAG;IACb,KAAK,EAAE;QACL,gCAAgC,EAAE,8DAA6B;QAC/D,2BAA2B,EAAE,kDAAsB;QACnD,6BAA6B,EAAE,sDAAwB;QACvD,kBAAkB,EAAE,8BAAW;QAC/B,cAAc,EAAE,yBAAU;QAC1B,2BAA2B,EAAE,kDAAsB;QACnD,mBAAmB,EAAE,kCAAc;QACnC,gCAAgC,EAAE,4DAA2B;QAC7D,wBAAwB,EAAG,4CAAmB;QAC9C,qBAAqB,EAAE,sCAAgB;QACvC,+BAA+B,EAAE,yDAAyB;QAC1D,uBAAuB,EAAE,2CAAmB;QAC5C,4BAA4B,EAAE,8DAAiC;QAC/D,0BAA0B,EAAE,iDAAsB;QAClD,2BAA2B,EAAE,sDAA0B;QACvD,0BAA0B,EAAE,oDAAyB;KACtD;IACD,OAAO,EAAE;QACP,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,4BAA4B,CAAC;YACvC,KAAK,EAAE;gBACL,6CAA6C,EAAE,MAAM;gBACrD,wCAAwC,EAAE,MAAM;gBAChD,0CAA0C,EAAE,MAAM;gBAClD,+BAA+B,EAAE,MAAM;gBACvC,2BAA2B,EAAE,MAAM;gBACnC,wCAAwC,EAAE,MAAM;gBAChD,gCAAgC,EAAE,MAAM;gBACxC,6CAA6C,EAAE,MAAM;gBACrD,qCAAqC,EAAE,MAAM;gBAC7C,kCAAkC,EAAE,MAAM;gBAC1C,4CAA4C,EAAE,MAAM;gBACpD,oCAAoC,EAAE,MAAM;gBAC5C,yCAAyC,EAAE,MAAM;gBACjD,uCAAuC,EAAE,MAAM;gBAC/C,wCAAwC,EAAE,MAAM;gBAChD,uCAAuC,EAAE,MAAM;aAChD;SACF;KACF;CACF,CAAC;AAEF,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EuiBadgeAccessibilityRules = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const BADGE_COMPONENT = 'EuiBadge';
|
|
6
|
+
exports.EuiBadgeAccessibilityRules = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
7
|
+
create(context) {
|
|
8
|
+
return {
|
|
9
|
+
JSXElement(node) {
|
|
10
|
+
const { openingElement } = node;
|
|
11
|
+
if (openingElement.name.type !== 'JSXIdentifier' ||
|
|
12
|
+
openingElement.name.name !== BADGE_COMPONENT) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
let iconOnClick, onClick, iconOnClickAriaLabel, onClickAriaLabel;
|
|
16
|
+
for (const attr of openingElement.attributes) {
|
|
17
|
+
if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier') {
|
|
18
|
+
switch (attr.name.name) {
|
|
19
|
+
case 'iconOnClick':
|
|
20
|
+
iconOnClick = attr;
|
|
21
|
+
break;
|
|
22
|
+
case 'onClick':
|
|
23
|
+
onClick = attr;
|
|
24
|
+
break;
|
|
25
|
+
case 'iconOnClickAriaLabel':
|
|
26
|
+
iconOnClickAriaLabel = attr;
|
|
27
|
+
break;
|
|
28
|
+
case 'onClickAriaLabel':
|
|
29
|
+
onClickAriaLabel = attr;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// 1. iconOnClick and onClick cannot refer to the same callback
|
|
35
|
+
if (iconOnClick &&
|
|
36
|
+
onClick &&
|
|
37
|
+
iconOnClick.value &&
|
|
38
|
+
onClick.value &&
|
|
39
|
+
iconOnClick.value.type === 'JSXExpressionContainer' &&
|
|
40
|
+
onClick.value.type === 'JSXExpressionContainer' &&
|
|
41
|
+
iconOnClick.value.expression.type === 'Identifier' &&
|
|
42
|
+
onClick.value.expression.type === 'Identifier' &&
|
|
43
|
+
iconOnClick.value.expression.name === onClick.value.expression.name) {
|
|
44
|
+
context.report({
|
|
45
|
+
node: iconOnClick,
|
|
46
|
+
messageId: 'sameCallback',
|
|
47
|
+
fix: fixer => fixer.removeRange([iconOnClick.range[0], iconOnClick.range[1]]),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// 2. iconOnClickAriaLabel should be set only if iconOnClick is set
|
|
51
|
+
if (iconOnClickAriaLabel && !iconOnClick) {
|
|
52
|
+
context.report({
|
|
53
|
+
node: iconOnClickAriaLabel,
|
|
54
|
+
messageId: 'iconOnClickAriaLabelWithoutIconOnClick',
|
|
55
|
+
fix: fixer => fixer.removeRange([iconOnClickAriaLabel.range[0], iconOnClickAriaLabel.range[1]]),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// 3. onClickAriaLabel should be set only if onClick is set
|
|
59
|
+
if (onClickAriaLabel && !onClick) {
|
|
60
|
+
context.report({
|
|
61
|
+
node: onClickAriaLabel,
|
|
62
|
+
messageId: 'onClickAriaLabelWithoutOnClick',
|
|
63
|
+
fix: fixer => fixer.removeRange([onClickAriaLabel.range[0], onClickAriaLabel.range[1]]),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
meta: {
|
|
70
|
+
type: 'problem',
|
|
71
|
+
docs: {
|
|
72
|
+
description: `Ensure the ${BADGE_COMPONENT} includes appropriate accessibility attributes`,
|
|
73
|
+
},
|
|
74
|
+
fixable: 'code',
|
|
75
|
+
schema: [],
|
|
76
|
+
messages: {
|
|
77
|
+
sameCallback: '`iconOnClick` and `onClick` should not refer to the same callback. Remove `iconOnClick` and keep only `onClick`.',
|
|
78
|
+
iconOnClickAriaLabelWithoutIconOnClick: '`iconOnClickAriaLabel` should only be set if `iconOnClick` is present. Remove this prop.',
|
|
79
|
+
onClickAriaLabelWithoutOnClick: '`onClickAriaLabel` should only be set if `onClick` is present. Remove this prop.',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
defaultOptions: [],
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=badge_accessibility_rules.js.map
|