JSX parsing plugin for the Acorn JavaScript parser that enables fast parsing of React JSX syntax
npx @tessl/cli install tessl/npm-acorn-jsx@5.3.0Acorn 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.
npm install acorn-jsxconst acorn = require("acorn");
const jsx = require("acorn-jsx");For TypeScript:
import { Parser } from 'acorn';
import jsx from 'acorn-jsx';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 />;");Acorn JSX is built around a plugin architecture that extends Acorn's core parser:
jsxName, jsxText, jsxTagStart, jsxTagEnd) integrated with Acorn's token systemThe plugin uses a WeakMap to cache token definitions per Acorn instance, ensuring efficient reuse while maintaining compatibility across different Acorn versions.
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 */ }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 typeWhen you extend Acorn with the JSX plugin, the resulting parser includes additional methods for JSX parsing:
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., &, {, 💩)
*/
jsx_readEntity();
/**
* Read JSX identifiers (optimized for JSX naming rules)
*/
jsx_readWord();
}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();
}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();
}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 errorControls 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)The parser generates these JSX-specific AST node types:
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>;
}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";
}interface JSXIdentifier {
type: "JSXIdentifier";
name: string;
}
interface JSXNamespacedName {
type: "JSXNamespacedName";
namespace: JSXIdentifier;
name: JSXIdentifier;
}
interface JSXMemberExpression {
type: "JSXMemberExpression";
object: JSXIdentifier | JSXMemberExpression;
property: JSXIdentifier;
}interface JSXAttribute {
type: "JSXAttribute";
name: JSXIdentifier | JSXNamespacedName;
value: Literal | JSXExpressionContainer | JSXElement | null;
}
interface JSXSpreadAttribute {
type: "JSXSpreadAttribute";
argument: Expression;
}interface JSXExpressionContainer {
type: "JSXExpressionContainer";
expression: Expression | JSXEmptyExpression;
}
interface JSXEmptyExpression {
type: "JSXEmptyExpression";
}
interface JSXText {
type: "JSXText";
value: string;
raw: string;
}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"
}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'
});