@elastic/eslint-plugin-eui 2.15.0 → 2.16.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 +125 -0
- package/lib/cjs/index.js +10 -1
- package/lib/cjs/rules/a11y/no_nested_copy_tooltip.d.ts +3 -0
- package/lib/cjs/rules/a11y/no_nested_copy_tooltip.d.ts.map +1 -0
- package/lib/cjs/rules/a11y/no_nested_copy_tooltip.js +84 -0
- package/lib/cjs/rules/a11y/tooltip_button_icon_wrap.d.ts.map +1 -1
- package/lib/cjs/rules/a11y/tooltip_button_icon_wrap.js +29 -1
- package/lib/cjs/rules/a11y/tooltip_no_interactive_content.d.ts.map +1 -1
- package/lib/cjs/rules/a11y/tooltip_no_interactive_content.js +17 -45
- package/lib/cjs/rules/button_group_no_invalid_children.d.ts +3 -0
- package/lib/cjs/rules/button_group_no_invalid_children.d.ts.map +1 -0
- package/lib/cjs/rules/button_group_no_invalid_children.js +159 -0
- package/lib/cjs/rules/callout_prefer_props_for_content.d.ts.map +1 -1
- package/lib/cjs/rules/callout_prefer_props_for_content.js +93 -156
- package/lib/cjs/rules/no_deprecated_icon_aliases.d.ts +174 -0
- package/lib/cjs/rules/no_deprecated_icon_aliases.d.ts.map +1 -0
- package/lib/cjs/rules/no_deprecated_icon_aliases.js +302 -0
- package/lib/cjs/utils/flat_map.d.ts +2 -0
- package/lib/cjs/utils/flat_map.d.ts.map +1 -0
- package/lib/cjs/utils/flat_map.js +24 -0
- package/lib/cjs/utils/has_meaningful_attr.d.ts +20 -0
- package/lib/cjs/utils/has_meaningful_attr.d.ts.map +1 -0
- package/lib/cjs/utils/has_meaningful_attr.js +61 -0
- package/lib/cjs/utils/is_fragment.d.ts +8 -0
- package/lib/cjs/utils/is_fragment.d.ts.map +1 -0
- package/lib/cjs/utils/is_fragment.js +26 -0
- package/lib/cjs/utils/walk_jsx_children.d.ts +30 -0
- package/lib/cjs/utils/walk_jsx_children.d.ts.map +1 -0
- package/lib/cjs/utils/walk_jsx_children.js +210 -0
- package/lib/esm/index.js +9 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/rules/a11y/no_nested_copy_tooltip.d.ts +2 -0
- package/lib/esm/rules/a11y/no_nested_copy_tooltip.js +84 -0
- package/lib/esm/rules/a11y/no_nested_copy_tooltip.js.map +1 -0
- package/lib/esm/rules/a11y/tooltip_button_icon_wrap.js +31 -0
- package/lib/esm/rules/a11y/tooltip_button_icon_wrap.js.map +1 -1
- package/lib/esm/rules/a11y/tooltip_no_interactive_content.js +19 -42
- package/lib/esm/rules/a11y/tooltip_no_interactive_content.js.map +1 -1
- package/lib/esm/rules/button_group_no_invalid_children.d.ts +2 -0
- package/lib/esm/rules/button_group_no_invalid_children.js +198 -0
- package/lib/esm/rules/button_group_no_invalid_children.js.map +1 -0
- package/lib/esm/rules/callout_prefer_props_for_content.js +75 -112
- package/lib/esm/rules/callout_prefer_props_for_content.js.map +1 -1
- package/lib/esm/rules/no_deprecated_icon_aliases.d.ts +173 -0
- package/lib/esm/rules/no_deprecated_icon_aliases.js +307 -0
- package/lib/esm/rules/no_deprecated_icon_aliases.js.map +1 -0
- package/lib/esm/utils/flat_map.d.ts +1 -0
- package/lib/esm/utils/flat_map.js +22 -0
- package/lib/esm/utils/flat_map.js.map +1 -0
- package/lib/esm/utils/has_meaningful_attr.d.ts +19 -0
- package/lib/esm/utils/has_meaningful_attr.js +55 -0
- package/lib/esm/utils/has_meaningful_attr.js.map +1 -0
- package/lib/esm/utils/is_fragment.d.ts +7 -0
- package/lib/esm/utils/is_fragment.js +26 -0
- package/lib/esm/utils/is_fragment.js.map +1 -0
- package/lib/esm/utils/walk_jsx_children.d.ts +29 -0
- package/lib/esm/utils/walk_jsx_children.js +213 -0
- package/lib/esm/utils/walk_jsx_children.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NoDeprecatedIconAliases = exports.DEPRECATED_ICON_ALIASES = exports.DEPRECATED_ICONS_WITHOUT_REPLACEMENT = void 0;
|
|
7
|
+
var _utils = require("@typescript-eslint/utils");
|
|
8
|
+
/*
|
|
9
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
10
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
11
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
12
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
13
|
+
* Side Public License, v 1.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const DEPRECATED_ICON_ALIASES = exports.DEPRECATED_ICON_ALIASES = {
|
|
17
|
+
alert: 'warning',
|
|
18
|
+
anomalyChart: 'chartAnomaly',
|
|
19
|
+
apmTrace: 'chartWaterfall',
|
|
20
|
+
arrowDown: 'chevronSingleDown',
|
|
21
|
+
arrowEnd: 'chevronLimitRight',
|
|
22
|
+
arrowLeft: 'chevronSingleLeft',
|
|
23
|
+
arrowRight: 'chevronSingleRight',
|
|
24
|
+
arrowStart: 'chevronLimitLeft',
|
|
25
|
+
arrowUp: 'chevronSingleUp',
|
|
26
|
+
beaker: 'flask',
|
|
27
|
+
boxesHorizontal: 'ellipsis',
|
|
28
|
+
boxesVertical: 'ellipsis',
|
|
29
|
+
changePointDetection: 'chartChangePoint',
|
|
30
|
+
checkInCircleFilled: 'checkCircleFill',
|
|
31
|
+
cheer: 'popper',
|
|
32
|
+
color: 'paintBucket',
|
|
33
|
+
compute: 'processor',
|
|
34
|
+
console: 'commandLine',
|
|
35
|
+
contrastHigh: 'contrastFill',
|
|
36
|
+
controlsHorizontal: 'controls',
|
|
37
|
+
controlsVertical: 'controls',
|
|
38
|
+
copyClipboard: 'copy',
|
|
39
|
+
crossInCircle: 'crossCircle',
|
|
40
|
+
crosshairs: 'crosshair',
|
|
41
|
+
currency: 'money',
|
|
42
|
+
cut: 'scissors',
|
|
43
|
+
desktop: 'display',
|
|
44
|
+
diff: 'compare',
|
|
45
|
+
discuss: 'comment',
|
|
46
|
+
documentEdit: 'pencil',
|
|
47
|
+
documents: 'document',
|
|
48
|
+
doubleArrowLeft: 'chevronDoubleLeft',
|
|
49
|
+
doubleArrowRight: 'chevronDoubleRight',
|
|
50
|
+
editorAlignCenter: 'textAlignCenter',
|
|
51
|
+
editorAlignLeft: 'textAlignLeft',
|
|
52
|
+
editorAlignRight: 'textAlignRight',
|
|
53
|
+
editorBold: 'textBold',
|
|
54
|
+
editorChecklist: 'listCheck',
|
|
55
|
+
editorCodeBlock: 'code',
|
|
56
|
+
editorComment: 'comment',
|
|
57
|
+
editorDistributeHorizontal: 'distributeHorizontal',
|
|
58
|
+
editorDistributeVertical: 'distributeVertical',
|
|
59
|
+
editorHeading: 'textHeading',
|
|
60
|
+
editorItalic: 'textItalic',
|
|
61
|
+
editorItemAlignBottom: 'alignBottom',
|
|
62
|
+
editorItemAlignCenter: 'alignCenterHorizontal',
|
|
63
|
+
editorItemAlignLeft: 'alignLeft',
|
|
64
|
+
editorItemAlignMiddle: 'alignCenterVertical',
|
|
65
|
+
editorItemAlignRight: 'alignRight',
|
|
66
|
+
editorItemAlignTop: 'alignTop',
|
|
67
|
+
editorLink: 'link',
|
|
68
|
+
editorOrderedList: 'listNumber',
|
|
69
|
+
editorPositionBottomLeft: 'alignBottomLeft',
|
|
70
|
+
editorPositionBottomRight: 'alignBottomRight',
|
|
71
|
+
editorPositionTopLeft: 'alignTopLeft',
|
|
72
|
+
editorPositionTopRight: 'alignTopRight',
|
|
73
|
+
editorRedo: 'redo',
|
|
74
|
+
editorStrike: 'textStrike',
|
|
75
|
+
editorTable: 'table',
|
|
76
|
+
editorUnderline: 'textUnderline',
|
|
77
|
+
editorUndo: 'undo',
|
|
78
|
+
editorUnorderedList: 'listBullet',
|
|
79
|
+
email: 'mail',
|
|
80
|
+
eql: 'query',
|
|
81
|
+
errorFilled: 'errorFill',
|
|
82
|
+
exit: 'logOut',
|
|
83
|
+
expand: 'maximize',
|
|
84
|
+
expandMini: 'maximize',
|
|
85
|
+
export: 'upload',
|
|
86
|
+
exportAction: 'upload',
|
|
87
|
+
eyeClosed: 'eyeSlash',
|
|
88
|
+
fieldStatistics: 'tableInfo',
|
|
89
|
+
filterInCircle: 'filter',
|
|
90
|
+
folderCheck: 'check',
|
|
91
|
+
folderOpened: 'folderOpen',
|
|
92
|
+
glasses: 'readOnly',
|
|
93
|
+
grab: 'dragVertical',
|
|
94
|
+
grabHorizontal: 'dragHorizontal',
|
|
95
|
+
grabOmnidirectional: 'drag',
|
|
96
|
+
heatmap: 'chartHeatmap',
|
|
97
|
+
importAction: 'download',
|
|
98
|
+
indexClose: 'tableCross',
|
|
99
|
+
indexEdit: 'tablePencil',
|
|
100
|
+
indexFlush: 'chartThreshold',
|
|
101
|
+
indexMapping: 'mapping',
|
|
102
|
+
indexOpen: 'tablePlus',
|
|
103
|
+
indexRuntime: 'tablePlay',
|
|
104
|
+
indexSettings: 'tableGear',
|
|
105
|
+
indexTemporary: 'tableTime',
|
|
106
|
+
invert: 'contrast',
|
|
107
|
+
kqlField: 'queryField',
|
|
108
|
+
kqlOperand: 'queryOperand',
|
|
109
|
+
kqlSelector: 'querySelector',
|
|
110
|
+
kqlValue: 'queryValue',
|
|
111
|
+
kubernetesPod: 'cube',
|
|
112
|
+
launch: 'rocket',
|
|
113
|
+
lettering: 'text',
|
|
114
|
+
lineDashed: 'lineDash',
|
|
115
|
+
lineDotted: 'lineDot',
|
|
116
|
+
list: 'listBullet',
|
|
117
|
+
listAdd: 'plusCircle',
|
|
118
|
+
logPatternAnalysis: 'pattern',
|
|
119
|
+
logRateAnalysis: 'chartBarVertical',
|
|
120
|
+
logstashIf: 'if',
|
|
121
|
+
logstashQueue: 'queue',
|
|
122
|
+
magnifyWithExclamation: 'magnifyExclamation',
|
|
123
|
+
magnifyWithMinus: 'magnifyMinus',
|
|
124
|
+
magnifyWithPlus: 'magnifyPlus',
|
|
125
|
+
mapMarker: 'waypoint',
|
|
126
|
+
minusInCircle: 'minusCircle',
|
|
127
|
+
minusInCircleFilled: 'minusCircle',
|
|
128
|
+
minusInSquare: 'minusSquare',
|
|
129
|
+
newChat: 'plusCircle',
|
|
130
|
+
node: 'vectorTriangle',
|
|
131
|
+
offline: 'wifiSlash',
|
|
132
|
+
online: 'wifi',
|
|
133
|
+
pagesSelect: 'documentsCheck',
|
|
134
|
+
pinFilled: 'pinFill',
|
|
135
|
+
pipeBreaks: 'lineBreak',
|
|
136
|
+
pipeNoBreaks: 'lineBreakSlash',
|
|
137
|
+
playFilled: 'play',
|
|
138
|
+
plusInCircle: 'plusCircle',
|
|
139
|
+
plusInCircleFilled: 'plusCircle',
|
|
140
|
+
plusInSquare: 'plusSquare',
|
|
141
|
+
popout: 'external',
|
|
142
|
+
productRobot: 'productAgent',
|
|
143
|
+
push: 'send',
|
|
144
|
+
returnKey: 'return',
|
|
145
|
+
search: 'magnify',
|
|
146
|
+
securitySignal: 'radar',
|
|
147
|
+
starEmpty: 'star',
|
|
148
|
+
starFilled: 'starFill',
|
|
149
|
+
starFilledSpace: 'starFillSpace',
|
|
150
|
+
starMinusFilled: 'starMinusFill',
|
|
151
|
+
starPlusFilled: 'starPlusFill',
|
|
152
|
+
stopFilled: 'stopFill',
|
|
153
|
+
streamsClassic: 'productStreamsClassic',
|
|
154
|
+
streamsWired: 'productStreamsWired',
|
|
155
|
+
submodule: 'merge',
|
|
156
|
+
tableDensityCompact: 'tableDensityHigh',
|
|
157
|
+
tableDensityExpanded: 'tableDensityLow',
|
|
158
|
+
tableDensityNormal: 'table',
|
|
159
|
+
temperature: 'thermometer',
|
|
160
|
+
timeRefresh: 'refreshTime',
|
|
161
|
+
timelineWithArrow: 'timelinePointer',
|
|
162
|
+
timeslider: 'clockControl',
|
|
163
|
+
tokenDenseVector: 'tokenVectorDense',
|
|
164
|
+
training: 'presentation',
|
|
165
|
+
unlink: 'linkSlash',
|
|
166
|
+
userAvatar: 'user',
|
|
167
|
+
vector: 'vectorSquare',
|
|
168
|
+
visArea: 'chartArea',
|
|
169
|
+
visAreaStacked: 'chartAreaStack',
|
|
170
|
+
visBarHorizontal: 'chartBarHorizontal',
|
|
171
|
+
visBarHorizontalStacked: 'chartBarHorizontalStack',
|
|
172
|
+
visBarVertical: 'chartBarVertical',
|
|
173
|
+
visBarVerticalStacked: 'chartBarVerticalStack',
|
|
174
|
+
visGauge: 'chartGauge',
|
|
175
|
+
visLine: 'chartLine',
|
|
176
|
+
visMapCoordinate: 'waypoint',
|
|
177
|
+
visMapRegion: 'map',
|
|
178
|
+
visMetric: 'chartMetric',
|
|
179
|
+
visPie: 'chartPie',
|
|
180
|
+
visTable: 'table',
|
|
181
|
+
visTagCloud: 'chartTagCloud',
|
|
182
|
+
visText: 'text',
|
|
183
|
+
visVega: 'code',
|
|
184
|
+
warningFilled: 'warningFill'
|
|
185
|
+
};
|
|
186
|
+
const DEPRECATED_ICONS_WITHOUT_REPLACEMENT = exports.DEPRECATED_ICONS_WITHOUT_REPLACEMENT = new Set(['mobile']);
|
|
187
|
+
const getComponentName = node => node.name.type === 'JSXIdentifier' ? node.name.name : undefined;
|
|
188
|
+
const COMPONENT_ICON_ATTRIBUTES = {
|
|
189
|
+
EuiCommentTimeline: new Set(['timelineAvatar']),
|
|
190
|
+
EuiFormAppend: new Set(['iconLeft', 'iconRight']),
|
|
191
|
+
EuiFormAppendPrepend: new Set(['iconLeft', 'iconRight']),
|
|
192
|
+
EuiFormPrepend: new Set(['iconLeft', 'iconRight']),
|
|
193
|
+
EuiLoadingLogo: new Set(['logo']),
|
|
194
|
+
EuiTimelineItem: new Set(['icon'])
|
|
195
|
+
};
|
|
196
|
+
const isIconAttribute = (attributeName, importedComponentName) => {
|
|
197
|
+
const normalizedName = attributeName.toLowerCase();
|
|
198
|
+
return normalizedName.endsWith('icontype') || COMPONENT_ICON_ATTRIBUTES[importedComponentName]?.has(attributeName) === true || normalizedName === 'type' && (importedComponentName === 'EuiIcon' || importedComponentName === 'EuiIconTip');
|
|
199
|
+
};
|
|
200
|
+
const getStaticStringNode = node => {
|
|
201
|
+
if (node.value?.type === 'Literal' && typeof node.value.value === 'string') {
|
|
202
|
+
return node.value;
|
|
203
|
+
}
|
|
204
|
+
if (node.value?.type !== 'JSXExpressionContainer') {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const {
|
|
208
|
+
expression
|
|
209
|
+
} = node.value;
|
|
210
|
+
if (expression.type === 'Literal' && typeof expression.value === 'string') {
|
|
211
|
+
return expression;
|
|
212
|
+
}
|
|
213
|
+
if (expression.type === 'TemplateLiteral' && expression.expressions.length === 0) {
|
|
214
|
+
return expression;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const getStaticStringValue = node => {
|
|
218
|
+
if (node.type === 'Literal') {
|
|
219
|
+
return typeof node.value === 'string' ? node.value : undefined;
|
|
220
|
+
}
|
|
221
|
+
return node.quasis[0]?.value.cooked ?? undefined;
|
|
222
|
+
};
|
|
223
|
+
const isDeprecatedIconAlias = value => value in DEPRECATED_ICON_ALIASES;
|
|
224
|
+
const NoDeprecatedIconAliases = exports.NoDeprecatedIconAliases = _utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
225
|
+
create(context) {
|
|
226
|
+
const euiComponentImports = new Map();
|
|
227
|
+
return {
|
|
228
|
+
ImportDeclaration(node) {
|
|
229
|
+
if (typeof node.source.value !== 'string' || node.source.value !== '@elastic/eui' && !node.source.value.startsWith('@elastic/eui/')) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
for (const specifier of node.specifiers) {
|
|
233
|
+
if (specifier.type !== 'ImportSpecifier') {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const importedName = specifier.imported.type === 'Identifier' ? specifier.imported.name : specifier.imported.value;
|
|
237
|
+
if (importedName.startsWith('Eui')) {
|
|
238
|
+
euiComponentImports.set(specifier.local.name, importedName);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
JSXAttribute(node) {
|
|
243
|
+
if (node.name.type !== 'JSXIdentifier') {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const openingElement = node.parent;
|
|
247
|
+
const localComponentName = openingElement.type === 'JSXOpeningElement' ? getComponentName(openingElement) : undefined;
|
|
248
|
+
const importedComponentName = localComponentName ? euiComponentImports.get(localComponentName) : undefined;
|
|
249
|
+
if (!importedComponentName || !isIconAttribute(node.name.name, importedComponentName)) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const staticStringNode = getStaticStringNode(node);
|
|
253
|
+
if (!staticStringNode) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const iconType = getStaticStringValue(staticStringNode);
|
|
257
|
+
if (!iconType) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (!isDeprecatedIconAlias(iconType)) {
|
|
261
|
+
if (DEPRECATED_ICONS_WITHOUT_REPLACEMENT.has(iconType)) {
|
|
262
|
+
context.report({
|
|
263
|
+
node: staticStringNode,
|
|
264
|
+
messageId: 'deprecatedIcon',
|
|
265
|
+
data: {
|
|
266
|
+
iconType
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const replacement = DEPRECATED_ICON_ALIASES[iconType];
|
|
273
|
+
context.report({
|
|
274
|
+
node: staticStringNode,
|
|
275
|
+
messageId: 'deprecatedIconAlias',
|
|
276
|
+
data: {
|
|
277
|
+
alias: iconType,
|
|
278
|
+
replacement
|
|
279
|
+
},
|
|
280
|
+
fix(fixer) {
|
|
281
|
+
const sourceText = context.sourceCode.getText(staticStringNode);
|
|
282
|
+
const quote = sourceText[0];
|
|
283
|
+
return fixer.replaceText(staticStringNode, `${quote}${replacement}${quote}`);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
meta: {
|
|
290
|
+
type: 'suggestion',
|
|
291
|
+
docs: {
|
|
292
|
+
description: 'Disallow deprecated EUI icons in JSX icon props.'
|
|
293
|
+
},
|
|
294
|
+
fixable: 'code',
|
|
295
|
+
schema: [],
|
|
296
|
+
messages: {
|
|
297
|
+
deprecatedIcon: 'Icon `{{iconType}}` is deprecated and has no direct replacement.',
|
|
298
|
+
deprecatedIconAlias: 'Icon alias `{{alias}}` is deprecated. Use `{{replacement}}` instead.'
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
defaultOptions: []
|
|
302
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flat_map.d.ts","sourceRoot":"","sources":["../../../src/utils/flat_map.ts"],"names":[],"mappings":"AAUA,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,CAO1E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.flatMap = flatMap;
|
|
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
|
+
// `Array.prototype.flatMap` requires ES2019 lib; this shim keeps rules that
|
|
16
|
+
// need it within the package's es5 lib setting without widening it for others.
|
|
17
|
+
function flatMap(arr, fn) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const item of arr) {
|
|
20
|
+
const items = fn(item);
|
|
21
|
+
for (const i of items) out.push(i);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether a JSX opening element has an attribute whose value is
|
|
4
|
+
* statically "meaningful" — present and not a statically-known empty/falsy
|
|
5
|
+
* value (`""`, `{''}`, `{0}`, `{false}`, `{null}`, `{undefined}`).
|
|
6
|
+
*
|
|
7
|
+
* Dynamic or otherwise non-statically-analyzable values (variables, JSX,
|
|
8
|
+
* template literals, calls, …) are treated as meaningful, since their runtime
|
|
9
|
+
* value is unknown. Boolean shorthand (`<El attr />`) is also meaningful.
|
|
10
|
+
*
|
|
11
|
+
* Useful for rules that must distinguish an attribute that actually renders
|
|
12
|
+
* something from one that is present but effectively empty.
|
|
13
|
+
*
|
|
14
|
+
* @param openingElement - The `JSXOpeningElement` node (ESTree).
|
|
15
|
+
* @param attrName - The attribute name to look up.
|
|
16
|
+
* @returns `true` if the attribute is present with a non-empty/non-falsy or
|
|
17
|
+
* dynamic value; otherwise `false`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function hasMeaningfulAttr(openingElement: TSESTree.JSXOpeningElement, attrName: string): boolean;
|
|
20
|
+
//# sourceMappingURL=has_meaningful_attr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has_meaningful_attr.d.ts","sourceRoot":"","sources":["../../../src/utils/has_meaningful_attr.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,QAAQ,CAAC,iBAAiB,EAC1C,QAAQ,EAAE,MAAM,GACf,OAAO,CAkCT"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.hasMeaningfulAttr = hasMeaningfulAttr;
|
|
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
|
+
/**
|
|
16
|
+
* Checks whether a JSX opening element has an attribute whose value is
|
|
17
|
+
* statically "meaningful" — present and not a statically-known empty/falsy
|
|
18
|
+
* value (`""`, `{''}`, `{0}`, `{false}`, `{null}`, `{undefined}`).
|
|
19
|
+
*
|
|
20
|
+
* Dynamic or otherwise non-statically-analyzable values (variables, JSX,
|
|
21
|
+
* template literals, calls, …) are treated as meaningful, since their runtime
|
|
22
|
+
* value is unknown. Boolean shorthand (`<El attr />`) is also meaningful.
|
|
23
|
+
*
|
|
24
|
+
* Useful for rules that must distinguish an attribute that actually renders
|
|
25
|
+
* something from one that is present but effectively empty.
|
|
26
|
+
*
|
|
27
|
+
* @param openingElement - The `JSXOpeningElement` node (ESTree).
|
|
28
|
+
* @param attrName - The attribute name to look up.
|
|
29
|
+
* @returns `true` if the attribute is present with a non-empty/non-falsy or
|
|
30
|
+
* dynamic value; otherwise `false`.
|
|
31
|
+
*/
|
|
32
|
+
function hasMeaningfulAttr(openingElement, attrName) {
|
|
33
|
+
const attr = openingElement.attributes.find(a => a.type === 'JSXAttribute' && a.name.type === 'JSXIdentifier' && a.name.name === attrName);
|
|
34
|
+
if (!attr) return false;
|
|
35
|
+
|
|
36
|
+
// Boolean shorthand: `<El attr />` → present and truthy.
|
|
37
|
+
if (attr.value == null) return true;
|
|
38
|
+
|
|
39
|
+
// String literal: `attr="…"`.
|
|
40
|
+
if (attr.value.type === 'Literal') {
|
|
41
|
+
return Boolean(attr.value.value);
|
|
42
|
+
}
|
|
43
|
+
if (attr.value.type === 'JSXExpressionContainer') {
|
|
44
|
+
const {
|
|
45
|
+
expression
|
|
46
|
+
} = attr.value;
|
|
47
|
+
|
|
48
|
+
// Statically-known literals: `{''}`, `{0}`, `{false}`, `{null}`.
|
|
49
|
+
if (expression.type === 'Literal') {
|
|
50
|
+
return Boolean(expression.value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// `{undefined}` (an identifier, not a literal).
|
|
54
|
+
if (expression.type === 'Identifier' && expression.name === 'undefined') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Dynamic / unknown value → treat as present.
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the opening element is a React fragment: `<Fragment>` or
|
|
4
|
+
* `<React.Fragment>`. The `<>` shorthand is represented as a `JSXFragment`
|
|
5
|
+
* node (not a `JSXOpeningElement`) and is handled separately by callers.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isFragment(opening: TSESTree.JSXOpeningElement): boolean;
|
|
8
|
+
//# sourceMappingURL=is_fragment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is_fragment.d.ts","sourceRoot":"","sources":["../../../src/utils/is_fragment.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,iBAAiB,GAAG,OAAO,CAUvE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isFragment = isFragment;
|
|
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
|
+
/**
|
|
16
|
+
* Returns true if the opening element is a React fragment: `<Fragment>` or
|
|
17
|
+
* `<React.Fragment>`. The `<>` shorthand is represented as a `JSXFragment`
|
|
18
|
+
* node (not a `JSXOpeningElement`) and is handled separately by callers.
|
|
19
|
+
*/
|
|
20
|
+
function isFragment(opening) {
|
|
21
|
+
const {
|
|
22
|
+
name
|
|
23
|
+
} = opening;
|
|
24
|
+
if (name.type === 'JSXIdentifier' && name.name === 'Fragment') return true;
|
|
25
|
+
return name.type === 'JSXMemberExpression' && name.object.type === 'JSXIdentifier' && name.object.name === 'React' && name.property.type === 'JSXIdentifier' && name.property.name === 'Fragment';
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type TSESTree, type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Recursively walks JSX children, expanding structural nodes transparently
|
|
4
|
+
* and calling `visit` for each content node.
|
|
5
|
+
*
|
|
6
|
+
* Always expanded (structural nodes):
|
|
7
|
+
* - `<>...</>`, `<Fragment>`, `<React.Fragment>` (transparent grouping)
|
|
8
|
+
* - `{expr}` (JSXExpressionContainer)
|
|
9
|
+
* - `{a && b}`, `{a || b}`, `{a ?? b}` (LogicalExpression → both sides)
|
|
10
|
+
* - `{c ? a : b}` (ConditionalExpression → both branches)
|
|
11
|
+
* - `{[a, b]}` (ArrayExpression → each element)
|
|
12
|
+
* - `{(arg) => <El />}` (ArrowFunctionExpression)
|
|
13
|
+
* - `{arr.map(fn)}` (CallExpression `.map()` with arrow-fn callback)
|
|
14
|
+
*
|
|
15
|
+
* With `options.sourceCode`: Identifiers and local arrow-fn components are
|
|
16
|
+
* also expanded via scope lookup when resolvable.
|
|
17
|
+
*
|
|
18
|
+
* `options.shouldSkip` - when provided, called for each JSXElement reached
|
|
19
|
+
* (after fragment and local-component checks). Returning true skips the
|
|
20
|
+
* element itself and recurses into its children instead. Use this to make
|
|
21
|
+
* layout wrappers or non-interactive shells transparent to the visitor.
|
|
22
|
+
*
|
|
23
|
+
* `visit` is called for leaf nodes: JSXElement, JSXText, Literal,
|
|
24
|
+
* TemplateLiteral, MemberExpression.
|
|
25
|
+
*/
|
|
26
|
+
export declare function walkJsxChildren(node: TSESTree.Node, visit: (node: TSESTree.Node) => void, options?: {
|
|
27
|
+
sourceCode?: TSESLint.SourceCode;
|
|
28
|
+
shouldSkip?: (el: TSESTree.JSXElement) => boolean;
|
|
29
|
+
}): void;
|
|
30
|
+
//# sourceMappingURL=walk_jsx_children.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk_jsx_children.d.ts","sourceRoot":"","sources":["../../../src/utils/walk_jsx_children.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAmMxE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,CAAC,IAAI,EACnB,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,KAAK,IAAI,EACpC,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC,UAAU,KAAK,OAAO,CAAC;CAC9C,GACL,IAAI,CAQN"}
|