0
# jsx-ast-utils
1
2
jsx-ast-utils is an AST utility module for statically analyzing JSX syntax. Originally extracted from eslint-plugin-jsx-a11y, it provides essential functions for examining JSX elements and their properties, making it ideal for creating linting rules and static analysis tools for JSX code.
3
4
## Package Information
5
6
- **Package Name**: jsx-ast-utils
7
- **Package Type**: npm
8
- **Language**: JavaScript (with TypeScript definitions)
9
- **Installation**: `npm install jsx-ast-utils`
10
11
## Core Imports
12
13
```javascript
14
import { hasProp, getProp, getPropValue, elementType, eventHandlers } from "jsx-ast-utils";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { hasProp, getProp, getPropValue, elementType, eventHandlers } = require("jsx-ast-utils");
21
```
22
23
Individual imports:
24
25
```javascript
26
import hasProp from "jsx-ast-utils/hasProp";
27
import getProp from "jsx-ast-utils/getProp";
28
// Or equivalently:
29
const hasProp = require("jsx-ast-utils/hasProp");
30
```
31
32
## Basic Usage
33
34
```javascript
35
import { hasProp, getProp, elementType } from "jsx-ast-utils";
36
37
// In an ESLint rule or AST traversal
38
module.exports = context => ({
39
JSXOpeningElement: node => {
40
// Check if element has specific prop
41
const hasOnChange = hasProp(node.attributes, 'onChange');
42
43
// Get element tag name
44
const tagName = elementType(node);
45
46
// Get specific prop for further analysis
47
const onChangeProp = getProp(node.attributes, 'onChange');
48
49
if (hasOnChange && tagName === 'input') {
50
// Analyze the onChange prop...
51
}
52
}
53
});
54
```
55
56
## Capabilities
57
58
### Property Existence Checking
59
60
Functions for checking whether specific props exist on JSX elements.
61
62
```javascript { .api }
63
/**
64
* Returns boolean indicating whether a prop exists on JSX element attributes
65
* @param props - Array of JSXAttribute nodes (usually node.attributes), defaults to empty array
66
* @param prop - String name of the prop to check for, defaults to empty string
67
* @param options - Configuration options, defaults to { ignoreCase: true, spreadStrict: true }
68
* @returns boolean indicating prop existence
69
*/
70
function hasProp(props: JSXAttribute[], prop: string, options: HasPropOptions): boolean;
71
72
/**
73
* Returns boolean indicating if any of the specified props exist on the node
74
* @param nodeProps - Array of JSXAttribute nodes, defaults to empty array
75
* @param props - Array of prop names or space-separated string, defaults to empty array
76
* @param options - Configuration options, defaults to { ignoreCase: true, spreadStrict: true }
77
* @returns boolean indicating if any prop exists
78
*/
79
function hasAnyProp(nodeProps: JSXAttribute[], props: string[] | string, options: HasPropOptions): boolean;
80
81
/**
82
* Returns boolean indicating if all of the specified props exist on the node
83
* @param nodeProps - Array of JSXAttribute nodes, defaults to empty array
84
* @param props - Array of prop names or space-separated string, defaults to empty array
85
* @param options - Configuration options, defaults to { ignoreCase: true, spreadStrict: true }
86
* @returns boolean indicating if all props exist
87
*/
88
function hasEveryProp(nodeProps: JSXAttribute[], props: string[] | string, options: HasPropOptions): boolean;
89
90
interface HasPropOptions {
91
/** Case insensitive matching (default: true) */
92
ignoreCase?: boolean;
93
/** Strict spread handling - assumes prop not in spread (default: true) */
94
spreadStrict?: boolean;
95
}
96
```
97
98
### Property Retrieval
99
100
Functions for retrieving JSX attributes and their associated data.
101
102
```javascript { .api }
103
/**
104
* Returns the JSXAttribute itself or undefined if prop is not present
105
* @param props - Array of JSXAttribute nodes, defaults to empty array
106
* @param prop - String name of the prop to retrieve, defaults to empty string
107
* @param options - Configuration options, defaults to { ignoreCase: true }
108
* @returns JSXAttribute node or undefined
109
*/
110
function getProp(props: JSXAttribute[], prop: string, options: GetPropOptions): JSXAttribute | undefined;
111
112
interface GetPropOptions {
113
/** Case insensitive matching (default: true) */
114
ignoreCase?: boolean;
115
}
116
```
117
118
### Value Extraction
119
120
Functions for extracting values from JSX attributes, handling various AST node types.
121
122
```javascript { .api }
123
/**
124
* Returns the value of a JSXAttribute, extracting from various AST node types
125
* This function returns the most closely associated value with JSX intention
126
* @param attribute - JSXAttribute node from AST parser
127
* @returns Extracted value (any type depending on attribute content)
128
*/
129
function getPropValue(attribute: JSXAttribute): any;
130
131
/**
132
* Returns only literal values from JSXAttribute (strings, numbers, booleans, etc.)
133
* Returns undefined for complex expressions that cannot be statically analyzed
134
* @param attribute - JSXAttribute node from AST parser
135
* @returns Literal value or undefined
136
*/
137
function getLiteralPropValue(attribute: JSXAttribute): string | number | boolean | null | undefined;
138
```
139
140
### Element Analysis
141
142
Functions for analyzing JSX element structure and names.
143
144
```javascript { .api }
145
/**
146
* Returns the tagName associated with a JSXElement
147
* Handles member expressions (Foo.Bar), namespaced names (ns:name), and fragments
148
* @param node - JSXElement, JSXOpeningElement, or JSXOpeningFragment node, defaults to empty object
149
* @returns String representation of the element type (returns '<>' for fragments)
150
* @throws Error if node is not a valid JSX element
151
*/
152
function elementType(node: JSXElement | JSXOpeningElement | JSXOpeningFragment): string;
153
154
/**
155
* Returns the name of a JSXAttribute, handling namespaced attributes
156
* @param prop - JSXAttribute node from AST parser, defaults to empty object
157
* @returns String name of the attribute
158
* @throws Error if prop is not a JSXAttribute node
159
*/
160
function propName(prop: JSXAttribute): string;
161
```
162
163
### Event Handler Collections
164
165
Pre-built collections of common JSX event handler names for validation and analysis.
166
167
```javascript { .api }
168
/**
169
* Flat array of all common JSX event handler prop names
170
* Includes all DOM events: click, change, submit, keyboard, mouse, touch, etc.
171
*/
172
const eventHandlers: string[];
173
174
/**
175
* Event handlers organized by event type for targeted analysis
176
*/
177
const eventHandlersByType: {
178
clipboard: string[]; // onCopy, onCut, onPaste
179
composition: string[]; // onCompositionEnd, onCompositionStart, onCompositionUpdate
180
keyboard: string[]; // onKeyDown, onKeyPress, onKeyUp
181
focus: string[]; // onFocus, onBlur
182
form: string[]; // onChange, onInput, onSubmit
183
mouse: string[]; // onClick, onContextMenu, onDblClick, onDoubleClick, onDrag, etc.
184
selection: string[]; // onSelect
185
touch: string[]; // onTouchCancel, onTouchEnd, onTouchMove, onTouchStart
186
ui: string[]; // onScroll
187
wheel: string[]; // onWheel
188
media: string[]; // onAbort, onCanPlay, onCanPlayThrough, onDurationChange, etc.
189
image: string[]; // onLoad, onError
190
animation: string[]; // onAnimationStart, onAnimationEnd, onAnimationIteration
191
transition: string[]; // onTransitionEnd
192
};
193
```
194
195
## Types
196
197
```javascript { .api }
198
interface JSXAttribute {
199
type: 'JSXAttribute';
200
name: JSXIdentifier | JSXNamespacedName;
201
value?: JSXExpressionContainer | Literal | JSXElement | JSXFragment | null;
202
}
203
204
interface JSXIdentifier {
205
type: 'JSXIdentifier';
206
name: string;
207
}
208
209
interface JSXNamespacedName {
210
type: 'JSXNamespacedName';
211
namespace: JSXIdentifier;
212
name: JSXIdentifier;
213
}
214
215
interface JSXExpressionContainer {
216
type: 'JSXExpressionContainer';
217
expression: Expression;
218
}
219
220
interface JSXElement {
221
type: 'JSXElement';
222
openingElement: JSXOpeningElement;
223
closingElement?: JSXClosingElement;
224
children: (JSXText | JSXElement | JSXFragment | JSXExpressionContainer)[];
225
}
226
227
interface JSXFragment {
228
type: 'JSXFragment';
229
openingFragment: JSXOpeningFragment;
230
closingFragment: JSXClosingFragment;
231
children: (JSXText | JSXElement | JSXFragment | JSXExpressionContainer)[];
232
}
233
234
interface JSXClosingFragment {
235
type: 'JSXClosingFragment';
236
}
237
238
interface JSXText {
239
type: 'JSXText';
240
value: string;
241
raw: string;
242
}
243
244
interface JSXOpeningElement {
245
type: 'JSXOpeningElement';
246
name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName;
247
attributes: (JSXAttribute | JSXSpreadAttribute)[];
248
selfClosing: boolean;
249
}
250
251
interface JSXOpeningFragment {
252
type: 'JSXOpeningFragment';
253
}
254
255
interface JSXClosingElement {
256
type: 'JSXClosingElement';
257
name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName;
258
}
259
260
interface JSXMemberExpression {
261
type: 'JSXMemberExpression';
262
object: JSXIdentifier | JSXMemberExpression;
263
property: JSXIdentifier;
264
}
265
266
interface JSXSpreadAttribute {
267
type: 'JSXSpreadAttribute';
268
argument: Expression;
269
}
270
271
interface Literal {
272
type: 'Literal';
273
value: string | number | boolean | null | RegExp;
274
raw: string;
275
}
276
277
// Expression is a union of all possible JavaScript expression AST nodes
278
type Expression = any;
279
```
280
281
## Advanced Usage Examples
282
283
### Creating ESLint Rules
284
285
```javascript
286
import { hasProp, getPropValue, elementType, eventHandlers } from "jsx-ast-utils";
287
288
// Rule: Require alt prop on img elements
289
const requireAltRule = {
290
create(context) {
291
return {
292
JSXOpeningElement(node) {
293
if (elementType(node) === 'img') {
294
if (!hasProp(node.attributes, 'alt')) {
295
context.report({
296
node,
297
message: 'img elements must have an alt prop.'
298
});
299
}
300
}
301
}
302
};
303
}
304
};
305
306
// Rule: Check for event handler prop values
307
const checkEventHandlers = {
308
create(context) {
309
return {
310
JSXOpeningElement(node) {
311
node.attributes.forEach(attr => {
312
const propName = propName(attr);
313
if (eventHandlers.includes(propName)) {
314
const value = getPropValue(attr);
315
if (typeof value === 'string') {
316
context.report({
317
node: attr,
318
message: `Event handler ${propName} should not be a string.`
319
});
320
}
321
}
322
});
323
}
324
};
325
}
326
};
327
```
328
329
### Analyzing JSX Structures
330
331
```javascript
332
import { elementType, hasProp, getProp, getLiteralPropValue } from "jsx-ast-utils";
333
334
function analyzeJSXElement(node) {
335
const tagName = elementType(node);
336
const hasClassName = hasProp(node.attributes, 'className');
337
const classNameProp = getProp(node.attributes, 'className');
338
const classNameValue = getLiteralPropValue(classNameProp);
339
340
return {
341
tagName,
342
hasClassName,
343
classNameValue,
344
isInteractive: node.attributes.some(attr =>
345
eventHandlers.includes(propName(attr))
346
)
347
};
348
}
349
```
350
351
### Working with Event Handler Categories
352
353
```javascript
354
import { eventHandlersByType, hasProp, hasAnyProp } from "jsx-ast-utils";
355
356
function checkElementInteractivity(node) {
357
// Check for mouse interactions
358
const hasMouseEvents = hasAnyProp(node.attributes, eventHandlersByType.mouse);
359
360
// Check for keyboard interactions
361
const hasKeyboardEvents = hasAnyProp(node.attributes, eventHandlersByType.keyboard);
362
363
// Check for form interactions
364
const hasFormEvents = hasAnyProp(node.attributes, eventHandlersByType.form);
365
366
return {
367
isClickable: hasMouseEvents,
368
isKeyboardAccessible: hasKeyboardEvents,
369
isFormElement: hasFormEvents
370
};
371
}
372
```