or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

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

index.mddocs/

jsx-ast-utils

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.

Package Information

  • Package Name: jsx-ast-utils
  • Package Type: npm
  • Language: JavaScript (with TypeScript definitions)
  • Installation: npm install jsx-ast-utils

Core Imports

import { hasProp, getProp, getPropValue, elementType, eventHandlers } from "jsx-ast-utils";

For CommonJS:

const { hasProp, getProp, getPropValue, elementType, eventHandlers } = require("jsx-ast-utils");

Individual imports:

import hasProp from "jsx-ast-utils/hasProp";
import getProp from "jsx-ast-utils/getProp";
// Or equivalently:
const hasProp = require("jsx-ast-utils/hasProp");

Basic Usage

import { hasProp, getProp, elementType } from "jsx-ast-utils";

// In an ESLint rule or AST traversal
module.exports = context => ({
  JSXOpeningElement: node => {
    // Check if element has specific prop
    const hasOnChange = hasProp(node.attributes, 'onChange');
    
    // Get element tag name
    const tagName = elementType(node);
    
    // Get specific prop for further analysis
    const onChangeProp = getProp(node.attributes, 'onChange');
    
    if (hasOnChange && tagName === 'input') {
      // Analyze the onChange prop...
    }
  }
});

Capabilities

Property Existence Checking

Functions for checking whether specific props exist on JSX elements.

/**
 * Returns boolean indicating whether a prop exists on JSX element attributes
 * @param props - Array of JSXAttribute nodes (usually node.attributes), defaults to empty array
 * @param prop - String name of the prop to check for, defaults to empty string
 * @param options - Configuration options, defaults to { ignoreCase: true, spreadStrict: true }
 * @returns boolean indicating prop existence
 */
function hasProp(props: JSXAttribute[], prop: string, options: HasPropOptions): boolean;

/**
 * Returns boolean indicating if any of the specified props exist on the node
 * @param nodeProps - Array of JSXAttribute nodes, defaults to empty array
 * @param props - Array of prop names or space-separated string, defaults to empty array
 * @param options - Configuration options, defaults to { ignoreCase: true, spreadStrict: true }
 * @returns boolean indicating if any prop exists
 */
function hasAnyProp(nodeProps: JSXAttribute[], props: string[] | string, options: HasPropOptions): boolean;

/**
 * Returns boolean indicating if all of the specified props exist on the node
 * @param nodeProps - Array of JSXAttribute nodes, defaults to empty array
 * @param props - Array of prop names or space-separated string, defaults to empty array
 * @param options - Configuration options, defaults to { ignoreCase: true, spreadStrict: true }
 * @returns boolean indicating if all props exist
 */
function hasEveryProp(nodeProps: JSXAttribute[], props: string[] | string, options: HasPropOptions): boolean;

interface HasPropOptions {
  /** Case insensitive matching (default: true) */
  ignoreCase?: boolean;
  /** Strict spread handling - assumes prop not in spread (default: true) */
  spreadStrict?: boolean;
}

Property Retrieval

Functions for retrieving JSX attributes and their associated data.

/**
 * Returns the JSXAttribute itself or undefined if prop is not present
 * @param props - Array of JSXAttribute nodes, defaults to empty array
 * @param prop - String name of the prop to retrieve, defaults to empty string
 * @param options - Configuration options, defaults to { ignoreCase: true }
 * @returns JSXAttribute node or undefined
 */
function getProp(props: JSXAttribute[], prop: string, options: GetPropOptions): JSXAttribute | undefined;

interface GetPropOptions {
  /** Case insensitive matching (default: true) */
  ignoreCase?: boolean;
}

Value Extraction

Functions for extracting values from JSX attributes, handling various AST node types.

/**
 * Returns the value of a JSXAttribute, extracting from various AST node types
 * This function returns the most closely associated value with JSX intention
 * @param attribute - JSXAttribute node from AST parser
 * @returns Extracted value (any type depending on attribute content)
 */
function getPropValue(attribute: JSXAttribute): any;

/**
 * Returns only literal values from JSXAttribute (strings, numbers, booleans, etc.)
 * Returns undefined for complex expressions that cannot be statically analyzed
 * @param attribute - JSXAttribute node from AST parser
 * @returns Literal value or undefined
 */
function getLiteralPropValue(attribute: JSXAttribute): string | number | boolean | null | undefined;

Element Analysis

Functions for analyzing JSX element structure and names.

/**
 * Returns the tagName associated with a JSXElement
 * Handles member expressions (Foo.Bar), namespaced names (ns:name), and fragments
 * @param node - JSXElement, JSXOpeningElement, or JSXOpeningFragment node, defaults to empty object
 * @returns String representation of the element type (returns '<>' for fragments)
 * @throws Error if node is not a valid JSX element
 */
function elementType(node: JSXElement | JSXOpeningElement | JSXOpeningFragment): string;

/**
 * Returns the name of a JSXAttribute, handling namespaced attributes
 * @param prop - JSXAttribute node from AST parser, defaults to empty object
 * @returns String name of the attribute
 * @throws Error if prop is not a JSXAttribute node
 */
function propName(prop: JSXAttribute): string;

Event Handler Collections

Pre-built collections of common JSX event handler names for validation and analysis.

/**
 * Flat array of all common JSX event handler prop names
 * Includes all DOM events: click, change, submit, keyboard, mouse, touch, etc.
 */
const eventHandlers: string[];

/**
 * Event handlers organized by event type for targeted analysis
 */
const eventHandlersByType: {
  clipboard: string[];    // onCopy, onCut, onPaste
  composition: string[];  // onCompositionEnd, onCompositionStart, onCompositionUpdate
  keyboard: string[];     // onKeyDown, onKeyPress, onKeyUp
  focus: string[];        // onFocus, onBlur
  form: string[];         // onChange, onInput, onSubmit
  mouse: string[];        // onClick, onContextMenu, onDblClick, onDoubleClick, onDrag, etc.
  selection: string[];    // onSelect
  touch: string[];        // onTouchCancel, onTouchEnd, onTouchMove, onTouchStart
  ui: string[];          // onScroll
  wheel: string[];       // onWheel
  media: string[];       // onAbort, onCanPlay, onCanPlayThrough, onDurationChange, etc.
  image: string[];       // onLoad, onError
  animation: string[];   // onAnimationStart, onAnimationEnd, onAnimationIteration
  transition: string[];  // onTransitionEnd
};

Types

interface JSXAttribute {
  type: 'JSXAttribute';
  name: JSXIdentifier | JSXNamespacedName;
  value?: JSXExpressionContainer | Literal | JSXElement | JSXFragment | null;
}

interface JSXIdentifier {
  type: 'JSXIdentifier';
  name: string;
}

interface JSXNamespacedName {
  type: 'JSXNamespacedName';
  namespace: JSXIdentifier;
  name: JSXIdentifier;
}

interface JSXExpressionContainer {
  type: 'JSXExpressionContainer';
  expression: Expression;
}

interface JSXElement {
  type: 'JSXElement';
  openingElement: JSXOpeningElement;
  closingElement?: JSXClosingElement;
  children: (JSXText | JSXElement | JSXFragment | JSXExpressionContainer)[];
}

interface JSXFragment {
  type: 'JSXFragment';
  openingFragment: JSXOpeningFragment;
  closingFragment: JSXClosingFragment;
  children: (JSXText | JSXElement | JSXFragment | JSXExpressionContainer)[];
}

interface JSXClosingFragment {
  type: 'JSXClosingFragment';
}

interface JSXText {
  type: 'JSXText';
  value: string;
  raw: string;
}

interface JSXOpeningElement {
  type: 'JSXOpeningElement';
  name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName;
  attributes: (JSXAttribute | JSXSpreadAttribute)[];
  selfClosing: boolean;
}

interface JSXOpeningFragment {
  type: 'JSXOpeningFragment';
}

interface JSXClosingElement {
  type: 'JSXClosingElement';
  name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName;
}

interface JSXMemberExpression {
  type: 'JSXMemberExpression';
  object: JSXIdentifier | JSXMemberExpression;
  property: JSXIdentifier;
}

interface JSXSpreadAttribute {
  type: 'JSXSpreadAttribute';
  argument: Expression;
}

interface Literal {
  type: 'Literal';
  value: string | number | boolean | null | RegExp;
  raw: string;
}

// Expression is a union of all possible JavaScript expression AST nodes
type Expression = any;

Advanced Usage Examples

Creating ESLint Rules

import { hasProp, getPropValue, elementType, eventHandlers } from "jsx-ast-utils";

// Rule: Require alt prop on img elements
const requireAltRule = {
  create(context) {
    return {
      JSXOpeningElement(node) {
        if (elementType(node) === 'img') {
          if (!hasProp(node.attributes, 'alt')) {
            context.report({
              node,
              message: 'img elements must have an alt prop.'
            });
          }
        }
      }
    };
  }
};

// Rule: Check for event handler prop values
const checkEventHandlers = {
  create(context) {
    return {
      JSXOpeningElement(node) {
        node.attributes.forEach(attr => {
          const propName = propName(attr);
          if (eventHandlers.includes(propName)) {
            const value = getPropValue(attr);
            if (typeof value === 'string') {
              context.report({
                node: attr,
                message: `Event handler ${propName} should not be a string.`
              });
            }
          }
        });
      }
    };
  }
};

Analyzing JSX Structures

import { elementType, hasProp, getProp, getLiteralPropValue } from "jsx-ast-utils";

function analyzeJSXElement(node) {
  const tagName = elementType(node);
  const hasClassName = hasProp(node.attributes, 'className');
  const classNameProp = getProp(node.attributes, 'className');
  const classNameValue = getLiteralPropValue(classNameProp);
  
  return {
    tagName,
    hasClassName,
    classNameValue,
    isInteractive: node.attributes.some(attr => 
      eventHandlers.includes(propName(attr))
    )
  };
}

Working with Event Handler Categories

import { eventHandlersByType, hasProp, hasAnyProp } from "jsx-ast-utils";

function checkElementInteractivity(node) {
  // Check for mouse interactions
  const hasMouseEvents = hasAnyProp(node.attributes, eventHandlersByType.mouse);
  
  // Check for keyboard interactions
  const hasKeyboardEvents = hasAnyProp(node.attributes, eventHandlersByType.keyboard);
  
  // Check for form interactions
  const hasFormEvents = hasAnyProp(node.attributes, eventHandlersByType.form);
  
  return {
    isClickable: hasMouseEvents,
    isKeyboardAccessible: hasKeyboardEvents,
    isFormElement: hasFormEvents
  };
}