or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdjsx.mdparsing.mdtokenization.md
tile.json

jsx.mddocs/

JSX Support

Extended parsing capabilities for JSX syntax used in React applications. JSX combines JavaScript with XML-like syntax for describing user interface components, and Esprima can parse JSX when the appropriate option is enabled.

Capabilities

JSX Parsing

Enable JSX syntax support by setting the jsx option to true in any parsing function.

// JSX parsing using the main parse functions
const jsxAst = parse(jsxCode, { jsx: true });
const jsxScript = parseScript(jsxCode, { jsx: true });
const jsxModule = parseModule(jsxCode, { jsx: true });

// JSX tokenization also supported
const jsxTokens = tokenize(jsxCode, { jsx: true });

Usage Examples:

import { parse, parseModule } from "esprima";

// Basic JSX element
const simpleJSX = parse("<div>Hello World</div>", { jsx: true });

// JSX with expressions
const jsxWithExpr = parse(`
  <div className="container">
    <h1>{title}</h1>
    <p>Count: {count + 1}</p>
  </div>
`, { jsx: true });

// JSX in a React component
const componentJSX = parseModule(`
  import React from 'react';
  
  function Welcome({ name }) {
    return <h1>Hello, {name}!</h1>;
  }
  
  export default Welcome;
`, { jsx: true });

// JSX with event handlers
const interactiveJSX = parse(`
  <button onClick={() => setCount(count + 1)}>
    Click me
  </button>
`, { jsx: true });

JSX AST Node Types

JSX introduces additional AST node types beyond standard JavaScript nodes.

// JSX Element - complete JSX tag with opening, closing, and children
interface JSXElement {
  type: 'JSXElement';
  openingElement: JSXOpeningElement;
  closingElement: JSXClosingElement | null; // null for self-closing elements
  children: JSXChild[];
  range?: [number, number];
  loc?: SourceLocation;
}

// JSX Opening Element - <div className="test">
interface JSXOpeningElement {
  type: 'JSXOpeningElement';
  name: JSXElementName;
  attributes: JSXAttribute[];
  selfClosing: boolean;
}

// JSX Closing Element - </div>
interface JSXClosingElement {
  type: 'JSXClosingElement';
  name: JSXElementName;
}

// JSX Element Name - can be identifier, namespaced, or member expression
type JSXElementName = JSXIdentifier | JSXNamespacedName | JSXMemberExpression;

// JSX Identifier - simple element name like "div"
interface JSXIdentifier {
  type: 'JSXIdentifier';
  name: string;
}

// JSX Namespaced Name - like "svg:path"
interface JSXNamespacedName {
  type: 'JSXNamespacedName';
  namespace: JSXIdentifier;
  name: JSXIdentifier;
}

// JSX Member Expression - like "React.Fragment"
interface JSXMemberExpression {
  type: 'JSXMemberExpression';
  object: JSXIdentifier | JSXMemberExpression;
  property: JSXIdentifier;
}

JSX Attributes

JSX attributes can be simple name-value pairs or spread attributes.

// JSX Attribute - name="value" or name={expression}
interface JSXAttribute {
  type: 'JSXAttribute';
  name: JSXIdentifier | JSXNamespacedName;
  value: JSXExpressionContainer | Literal | null;
}

// JSX Spread Attribute - {...props}
interface JSXSpreadAttribute {
  type: 'JSXSpreadAttribute';
  argument: Expression;
}

// JSX Expression Container - {expression}
interface JSXExpressionContainer {
  type: 'JSXExpressionContainer';
  expression: Expression | JSXEmptyExpression;
}

// JSX Empty Expression - {}
interface JSXEmptyExpression {
  type: 'JSXEmptyExpression';
}

Usage Examples:

import { parse } from "esprima";

// Various JSX attribute types
const jsxWithAttrs = parse(`
  <Component
    id="test"
    className={styles.container}
    onClick={handleClick}
    disabled={true}
    aria-label="Close button"
    {...otherProps}
  />
`, { jsx: true });

// Access JSX attributes in AST
const element = jsxWithAttrs.body[0].expression;
element.openingElement.attributes.forEach(attr => {
  if (attr.type === 'JSXAttribute') {
    console.log(`Attribute: ${attr.name.name}`);
  } else if (attr.type === 'JSXSpreadAttribute') {
    console.log('Spread attribute');
  }
});

JSX Children

JSX elements can contain various types of child content.

// JSX Child - can be element, text, or expression
type JSXChild = JSXElement | JSXText | JSXExpressionContainer;

// JSX Text - plain text content
interface JSXText {
  type: 'JSXText';
  value: string;
  raw: string;
}

Usage Examples:

import { parse } from "esprima";

// JSX with different child types
const jsxChildren = parse(`
  <div>
    <h1>Title</h1>
    Some plain text
    {dynamicContent}
    <p>Paragraph with <strong>bold</strong> text</p>
  </div>
`, { jsx: true });

// Access JSX children
const divElement = jsxChildren.body[0].expression;
divElement.children.forEach((child, index) => {
  console.log(`Child ${index}: ${child.type}`);
  if (child.type === 'JSXText') {
    console.log(`  Text: "${child.value}"`);
  }
});

JSX Fragment Support

JSX fragments allow grouping elements without a wrapper element.

import { parse } from "esprima";

// JSX Fragment syntax
const fragmentJSX = parse(`
  <>
    <h1>Title</h1>
    <p>Content</p>
  </>
`, { jsx: true });

// React.Fragment syntax
const reactFragmentJSX = parse(`
  <React.Fragment>
    <h1>Title</h1>
    <p>Content</p>
  </React.Fragment>
`, { jsx: true });

JSX with TypeScript

When parsing TypeScript JSX (TSX), additional type information may be present.

import { parse } from "esprima";

// JSX with TypeScript generics and props
const tsxCode = parse(`
  interface Props {
    title: string;
    count: number;
  }
  
  const component = <MyComponent<Props> title="Hello" count={42} />;
`, { jsx: true });

Advanced JSX Features

Conditional JSX

JSX expressions can include conditional rendering patterns.

import { parse } from "esprima";

const conditionalJSX = parse(`
  <div>
    {isVisible && <p>Visible content</p>}
    {user ? <Welcome user={user} /> : <Login />}
    {items.map(item => <Item key={item.id} data={item} />)}
  </div>
`, { jsx: true });

JSX Event Handlers

JSX commonly includes event handler functions.

import { parse } from "esprima";

const eventHandlerJSX = parse(`
  <form onSubmit={handleSubmit}>
    <input
      type="text"
      value={value}
      onChange={(e) => setValue(e.target.value)}
      onFocus={() => setFocused(true)}
      onBlur={() => setFocused(false)}
    />
    <button type="submit">Submit</button>
  </form>
`, { jsx: true });

JSX Composition Patterns

Complex JSX structures with component composition.

import { parse } from "esprima";

const compositionJSX = parse(`
  <Layout>
    <Header>
      <Navigation items={navItems} />
    </Header>
    <Main>
      <Article>
        <h1>{post.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: post.content }} />
      </Article>
      <Sidebar>
        <Widget type="recent" />
        <Widget type="popular" />
      </Sidebar>
    </Main>
    <Footer />
  </Layout>
`, { jsx: true });

JSX Tokenization

JSX also affects tokenization, introducing JSX-specific token types.

import { tokenize } from "esprima";

// JSX tokenization includes special tokens
const jsxTokens = tokenize("<div className='test'>Hello</div>", {
  jsx: true,
  range: true
});

// JSX-specific token types
// - JSXIdentifier: JSX element and attribute names
// - JSXText: Text content within JSX elements
// Plus standard JavaScript tokens for expressions

Error Handling with JSX

JSX parsing follows the same error handling patterns as regular JavaScript parsing.

import { parse } from "esprima";

// JSX syntax errors
try {
  const invalidJSX = parse("<div><span></div>", { jsx: true }); // Mismatched tags
} catch (error) {
  console.log(error.message); // JSX-specific error message
}

// Tolerant JSX parsing
const tolerantJSX = parse("<div><span></div>", { 
  jsx: true, 
  tolerant: true 
});
console.log(tolerantJSX.errors); // Array of JSX parsing errors