or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-jsx-ast-utils

AST utility module for statically analyzing JSX

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/jsx-ast-utils@3.3.x

To install, run

npx @tessl/cli install tessl/npm-jsx-ast-utils@3.3.0

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

```