JSX AST Utilities
jsx-ast-utils is a dedicated utility module designed for the static analysis of JSX Abstract Syntax Tree (AST) nodes. It provides functions to query, validate, and extract information from JSX elements, particularly focusing on prop existence and values. Currently stable at version 3.3.5, it offers a consistent API for working with JSX ASTs, making it an invaluable tool for authors of linting rules, code transformers, or other static analysis tools. Originally extracted from eslint-plugin-jsx-a11y, its primary differentiator is its focused scope on JSX syntax, providing robust and tested utilities that account for various JSX complexities including spread attributes and case insensitivity.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'attributes')
cause This error occurs when attempting to access the `attributes` property on an AST node that is not a `JSXOpeningElement`. The `attributes` property is specific to JSX opening tags.fixBefore accessing `node.attributes` or passing a node to `jsx-ast-utils` functions expecting JSX attributes, ensure that `node.type === 'JSXOpeningElement'` (or a similar check for relevant JSX node types). -
ReferenceError: require is not defined in ES module scope
cause This typically happens when you are using CommonJS `require()` syntax within an ECMAScript module (ESM) environment (e.g., in a Node.js project with `"type": "module"` in `package.json` or in a modern browser context).fixSwitch to ESM import syntax: `import { hasProp } from 'jsx-ast-utils';` for named exports. -
TypeError: hasProp is not a function
cause This error often indicates an incorrect import or destructuring when using CommonJS. Common mistakes include `require('jsx-ast-utils')` without subsequently accessing `.hasProp`, or attempting `require('jsx-ast-utils/hasProp')` assuming a default export from a subpath.fixFor CommonJS, ensure you are correctly destructuring the named export: `const { hasProp } = require('jsx-ast-utils');`.
Warnings
- breaking In `v2.0.0`, the internal `propName` utility (and consequently functions like `getProp`) was changed to always return a value, where previously it might have returned `undefined` or thrown an error for certain cases. If your code relied on this previous behavior, it constitutes a breaking change.
- gotcha The `hasProp`, `hasAnyProp`, and `hasEveryProp` utilities default `spreadStrict` to `true`. This means if a prop is supplied via a JSX spread attribute (`{...props}`), the utility will report that the specific prop does NOT exist.
- gotcha The `hasProp`, `hasAnyProp`, and `hasEveryProp` utilities default `ignoreCase` to `true`. This means they will find a prop like 'onClick' if you search for 'onclick'.
- breaking Support for TypeScript AST node types was officially added in `v2.1.0`. Older versions might not correctly parse or analyze TSX/TypeScript-specific AST nodes, leading to incorrect results or runtime errors when processing TypeScript codebases.
Install
-
npm install jsx-ast-utils -
yarn add jsx-ast-utils -
pnpm add jsx-ast-utils
Imports
- hasProp
const hasProp = require('jsx-ast-utils'); // Missing .hasProp after requireimport { hasProp } from 'jsx-ast-utils'; - getProp
const getProp = require('jsx-ast-utils/getProp'); // Direct file imports are discouraged and may breakimport { getProp } from 'jsx-ast-utils'; - hasAnyProp
import hasAnyProp from 'jsx-ast-utils'; // No default export from the main package
import { hasAnyProp } from 'jsx-ast-utils';
Quickstart
import { hasProp } from 'jsx-ast-utils';
// This example mimics an ESLint rule structure to demonstrate usage.
// In a real ESLint rule, `context` and `node` would be provided by ESLint.
const mockContext = {
report: ({ node, message }) => console.log(`Report at node ${node.type}: ${message}`)
};
const mockJSXOpeningElementNode = {
type: 'JSXOpeningElement',
attributes: [
{ type: 'JSXAttribute', name: { type: 'JSXIdentifier', name: 'id' }, value: null },
{ type: 'JSXAttribute', name: { type: 'JSXIdentifier', name: 'className' }, value: null },
{ type: 'JSXSpreadAttribute', argument: { type: 'Identifier', name: 'props' } }
]
};
// Example usage within a mock ESLint rule visitor
function JSXOpeningElementVisitor(node, context) {
const hasIdProp = hasProp(node.attributes, 'id');
const hasOnChange = hasProp(node.attributes, 'onChange', { spreadStrict: false }); // Look within spreads
const hasDataAttr = hasProp(node.attributes, 'data-testId', { ignoreCase: true }); // Case-insensitive search
if (!hasIdProp) {
context.report({ node, message: `JSX element is missing 'id' prop.` });
}
if (hasOnChange) {
context.report({ node, message: `JSX element has an 'onChange' prop, which might be a concern.` });
}
if (hasDataAttr) {
context.report({ node, message: `JSX element has a data-testid attribute.` });
}
}
// Simulate ESLint calling the visitor
JSXOpeningElementVisitor(mockJSXOpeningElementNode, mockContext);