or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-acorn-jsx

JSX parsing plugin for the Acorn JavaScript parser that enables fast parsing of React JSX syntax

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/acorn-jsx@5.3.x

To install, run

npx @tessl/cli install tessl/npm-acorn-jsx@5.3.0

index.mddocs/

Acorn JSX

Acorn JSX is a high-performance JSX parsing plugin for the Acorn JavaScript parser. It extends Acorn's capabilities to handle JSX syntax commonly used in React applications, providing fast and efficient parsing of JSX elements, attributes, and expressions.

Package Information

  • Package Name: acorn-jsx
  • Package Type: npm
  • Language: JavaScript (with TypeScript definitions)
  • Installation: npm install acorn-jsx
  • Peer Dependency: acorn ^6.0.0 || ^7.0.0 || ^8.0.0

Core Imports

const acorn = require("acorn");
const jsx = require("acorn-jsx");

For TypeScript:

import { Parser } from 'acorn';
import jsx from 'acorn-jsx';

Basic Usage

const acorn = require("acorn");
const jsx = require("acorn-jsx");

// Create an extended parser with JSX support
const Parser = acorn.Parser.extend(jsx());

// Parse JSX code
const ast = Parser.parse("const element = <div>Hello World</div>;", {
  ecmaVersion: 2020,
  sourceType: "module"
});

// Parse with custom options
const ParserWithOptions = acorn.Parser.extend(jsx({
  allowNamespaces: false,        // Disable XML namespaces (React-style)
  allowNamespacedObjects: true   // Enable deprecated namespace+object syntax
}));

const ast2 = ParserWithOptions.parse("const el = <MyComponent.Button />;");

Architecture

Acorn JSX is built around a plugin architecture that extends Acorn's core parser:

  • Plugin Function: The main export creates a parser extension that adds JSX-specific tokens and parsing rules
  • Token System: Custom JSX token types (jsxName, jsxText, jsxTagStart, jsxTagEnd) integrated with Acorn's token system
  • Context Switching: Dynamic parsing contexts that switch between JavaScript expression parsing and JSX content parsing
  • AST Generation: Produces JSX-specific AST nodes that integrate seamlessly with standard JavaScript AST structures
  • Configurable Behavior: Options to control namespace support and deprecated syntax compatibility

The plugin uses a WeakMap to cache token definitions per Acorn instance, ensuring efficient reuse while maintaining compatibility across different Acorn versions.

Capabilities

Plugin Function

Creates an Acorn parser plugin that adds JSX parsing capabilities.

/**
 * Creates an Acorn parser plugin for JSX parsing
 * @param {Object} options - Configuration options
 * @param {boolean} [options.allowNamespaces=true] - Enable XML namespace support (e.g., <ns:Component>)
 * @param {boolean} [options.allowNamespacedObjects=false] - Enable deprecated namespace+object syntax (e.g., <ns:Object.Property>)
 * @returns {Function} Plugin function that extends Acorn Parser
 */
function jsx(options) { /* extends Parser */ }

JSX Token Types

Access JSX-specific token types for advanced parser usage.

/**
 * JSX token types exposed for advanced usage
 * @type {Object}
 * @property {TokenType} jsxName - JSX identifier tokens
 * @property {TokenType} jsxText - JSX text content tokens  
 * @property {TokenType} jsxTagStart - JSX opening tag start tokens (<)
 * @property {TokenType} jsxTagEnd - JSX tag end tokens (>)
 */
jsx.tokTypes;

Usage Example:

const jsx = require("acorn-jsx");
const tokens = jsx.tokTypes;

// Access specific token types
console.log(tokens.jsxName);      // JSX identifier token type
console.log(tokens.jsxText);      // JSX text token type
console.log(tokens.jsxTagStart);  // JSX tag start token type
console.log(tokens.jsxTagEnd);    // JSX tag end token type

Extended Parser Methods

When you extend Acorn with the JSX plugin, the resulting parser includes additional methods for JSX parsing:

JSX Token Reading

class ExtendedParser extends Parser {
  /**
   * Read JSX content tokens (text, tags, expressions)
   */
  jsx_readToken();
  
  /**
   * Read and normalize newlines in JSX content
   * @param {boolean} normalizeCRLF - Whether to normalize CRLF to LF
   */
  jsx_readNewLine(normalizeCRLF);
  
  /**
   * Read JSX string literals with HTML entity support
   * @param {number} quote - Quote character code (34 for ", 39 for ')
   */
  jsx_readString(quote);
  
  /**
   * Parse HTML/XML entities in JSX content (e.g., &amp;, &#123;, &#x1F4A9;)
   */
  jsx_readEntity();
  
  /**
   * Read JSX identifiers (optimized for JSX naming rules)
   */
  jsx_readWord();
}

JSX Parsing Methods

class ExtendedParser extends Parser {
  /**
   * Parse JSX identifiers (tag names, attribute names)
   */
  jsx_parseIdentifier();
  
  /**
   * Parse namespaced JSX names (e.g., ns:component)
   */
  jsx_parseNamespacedName();
  
  /**
   * Parse JSX element names (identifiers, namespaced, or member expressions)
   */
  jsx_parseElementName();
  
  /**
   * Parse JSX attribute values (strings, expressions, or JSX elements)
   */
  jsx_parseAttributeValue();
  
  /**
   * Parse empty JSX expressions ({})
   */
  jsx_parseEmptyExpression();
  
  /**
   * Parse JSX expression containers ({expression})
   */
  jsx_parseExpressionContainer();
  
  /**
   * Parse JSX attributes and spread attributes
   */
  jsx_parseAttribute();
  
  /**
   * Parse JSX opening elements (<Component prop="value">)
   * @param {number} startPos - Start position
   * @param {Object} startLoc - Start location object
   */
  jsx_parseOpeningElementAt(startPos, startLoc);
  
  /**
   * Parse JSX closing elements (</Component>)
   * @param {number} startPos - Start position  
   * @param {Object} startLoc - Start location object
   */
  jsx_parseClosingElementAt(startPos, startLoc);
  
  /**
   * Parse complete JSX elements with children
   * @param {number} startPos - Start position
   * @param {Object} startLoc - Start location object
   */
  jsx_parseElementAt(startPos, startLoc);
  
  /**
   * Parse JSX text nodes
   */
  jsx_parseText();
  
  /**
   * Parse JSX elements from current parser position
   */
  jsx_parseElement();
}

Static Properties

class ExtendedParser extends Parser {
  /**
   * Access to JSX tokens and contexts for advanced usage
   * @type {Object}
   * @property {Object} tokTypes - JSX token types
   * @property {Object} tokContexts - JSX parsing contexts
   */
  static get acornJsx();
}

Configuration Options

allowNamespaces

Controls whether XML namespaces are allowed in JSX element names.

// Enable namespaces (default: true)
const Parser1 = acorn.Parser.extend(jsx({ allowNamespaces: true }));
Parser1.parse('<ns:Component />');  // ✓ Valid

// Disable namespaces (React-style JSX)
const Parser2 = acorn.Parser.extend(jsx({ allowNamespaces: false }));
Parser2.parse('<ns:Component />');  // ✗ Throws error

allowNamespacedObjects

Controls whether the deprecated namespace+object syntax is allowed.

// Disable namespaced objects (default: false)
const Parser1 = acorn.Parser.extend(jsx({ allowNamespacedObjects: false }));
Parser1.parse('<ns:Object.Property />');  // ✗ Throws error

// Enable namespaced objects (deprecated feature)
const Parser2 = acorn.Parser.extend(jsx({ allowNamespacedObjects: true }));
Parser2.parse('<ns:Object.Property />');  // ✓ Valid (deprecated)

JSX AST Node Types

The parser generates these JSX-specific AST node types:

JSX Elements

interface JSXElement {
  type: "JSXElement";
  openingElement: JSXOpeningElement;
  closingElement: JSXClosingElement | null;
  children: Array<JSXElement | JSXText | JSXExpressionContainer>;
}

interface JSXFragment {
  type: "JSXFragment";
  openingFragment: JSXOpeningFragment;
  closingFragment: JSXClosingFragment;
  children: Array<JSXElement | JSXText | JSXExpressionContainer>;
}

JSX Opening/Closing Elements

interface JSXOpeningElement {
  type: "JSXOpeningElement";
  name: JSXIdentifier | JSXNamespacedName | JSXMemberExpression;
  attributes: Array<JSXAttribute | JSXSpreadAttribute>;
  selfClosing: boolean;
}

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

interface JSXOpeningFragment {
  type: "JSXOpeningFragment";
}

interface JSXClosingFragment {
  type: "JSXClosingFragment";
}

JSX Names

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

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

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

JSX Attributes

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

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

JSX Expressions and Text

interface JSXExpressionContainer {
  type: "JSXExpressionContainer";
  expression: Expression | JSXEmptyExpression;
}

interface JSXEmptyExpression {
  type: "JSXEmptyExpression";
}

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

Error Handling

The parser throws descriptive errors for invalid JSX syntax:

try {
  Parser.parse('<div>unclosed');
} catch (error) {
  console.log(error.message); // "Unterminated JSX contents"
}

try {
  Parser.parse('<div></span>');
} catch (error) {
  console.log(error.message); // "Expected corresponding JSX closing tag for <div>"
}

try {
  Parser.parse('<>text<');
} catch (error) {
  console.log(error.message); // "Adjacent JSX elements must be wrapped in an enclosing tag"
}

TypeScript Support

Full TypeScript definitions are included:

declare const jsx: (options?: jsx.Options) => (BaseParser: typeof Parser) => typeof Parser;

declare namespace jsx {
  interface Options {
    allowNamespacedObjects?: boolean;
    allowNamespaces?: boolean;
  }
}

export = jsx;

Usage in TypeScript:

import { Parser } from 'acorn';
import jsx from 'acorn-jsx';

const ExtendedParser = Parser.extend(jsx({
  allowNamespaces: false
}));

const ast = ExtendedParser.parse('<div>Hello</div>', {
  ecmaVersion: 2020,
  sourceType: 'module'
});