Helper function to build React JSX elements within Babel transformation workflows
npx @tessl/cli install tessl/npm-babel--helper-builder-react-jsx@7.27.0A Babel helper function that creates visitors for transforming JSX elements and fragments into function calls. This package provides the core transformation logic used by React JSX Babel plugins to convert JSX syntax into JavaScript function calls with configurable options for different React patterns and optimization strategies.
npm install @babel/helper-builder-react-jsximport builderReactJsx from "@babel/helper-builder-react-jsx";
import type { Options } from "@babel/helper-builder-react-jsx";
import type { PluginPass, Visitor } from "@babel/core";
import * as t from "@babel/types";For CommonJS:
const builderReactJsx = require("@babel/helper-builder-react-jsx");import builderReactJsx from "@babel/helper-builder-react-jsx";
import type { PluginPass, Visitor } from "@babel/core";
import * as t from "@babel/types";
// Create a basic JSX transformation visitor
const visitor: Visitor = builderReactJsx({
throwIfNamespace: true,
useSpread: false,
useBuiltIns: false
});
// Use in a Babel plugin (following test patterns)
export default function myJsxPlugin() {
return {
visitor: {
...builderReactJsx({
// Transform JSX elements with custom pre/post processing
pre(state, pass) {
// Called before JSX element transformation
console.log(`Transforming ${state.tagName}`);
},
post(state, pass) {
// Called after JSX element transformation
if (state.pure) {
console.log("Element marked as pure");
}
}
}),
Program(path, state) {
// Set up plugin state as shown in tests
state.set("jsxIdentifier", () => t.identifier("React.createElement"));
}
}
};
}The helper follows Babel's visitor pattern architecture:
The transformation process follows this flow:
Creates a Babel visitor object that handles JSX element and fragment transformations.
/**
* Creates a Babel visitor for transforming JSX elements and fragments into function calls
* @param opts - Configuration options for JSX transformation behavior
* @returns Babel visitor object with JSX transformation methods
*/
export default function builderReactJsx(opts: Options): Visitor<PluginPass<Options>>;Usage Example:
import builderReactJsx from "@babel/helper-builder-react-jsx";
// Basic transformation
const visitor = builderReactJsx({
throwIfNamespace: true,
useSpread: false
});
// Advanced transformation with hooks
const advancedVisitor = builderReactJsx({
filter: (node, pass) => {
// Only transform JSX elements with specific attributes
return node.type === "JSXElement" &&
node.openingElement.attributes.length > 0;
},
pre(state, pass) {
// Mark React components as pure for optimization
if (state.tagName && /^[A-Z]/.test(state.tagName)) {
state.pure = true;
}
},
useSpread: true,
throwIfNamespace: false
});Configuration interface for customizing JSX transformation behavior.
export interface Options {
/** Optional filter function to determine which JSX nodes to transform */
filter?: (node: t.Node, pass: PluginPass) => boolean;
/** Optional pre-processing hook called before JSX element transformation */
pre?: (state: ElementState, pass: PluginPass) => void;
/** Optional post-processing hook called after JSX element transformation */
post?: (state: ElementState, pass: PluginPass) => void;
/** Enable React DOM compatibility mode for legacy JSX patterns */
compat?: boolean;
/** Optional pure annotation string for optimization markers */
pure?: string;
/** Whether to throw error on JSX namespace tags (default: undefined, allows namespaces) */
throwIfNamespace?: boolean;
/** Use spread operator for attributes instead of Object.assign */
useSpread?: boolean;
/** Use native Object.assign instead of Babel helper */
useBuiltIns?: boolean;
}Configuration Examples:
// React compatibility mode (legacy)
const compatVisitor = builderReactJsx({
compat: true, // Disables JSX fragments
throwIfNamespace: true
});
// Modern React with optimization
const modernVisitor = builderReactJsx({
useSpread: true, // Use {...props} instead of Object.assign
pre(state, pass) {
// Mark functional components as pure
if (state.tagName && /^[A-Z]/.test(state.tagName)) {
state.pure = true;
}
}
});
// Custom filtering
const filteredVisitor = builderReactJsx({
filter: (node, pass) => {
// Only transform JSX elements, skip fragments
return node.type === "JSXElement";
},
useBuiltIns: true // Use native Object.assign
});State object passed to pre and post hooks during JSX element transformation.
interface ElementState {
/** The transformed tag expression (e.g., React.createElement first argument) */
tagExpr: t.Expression;
/** Raw string tag name extracted from JSX element */
tagName: string | undefined | null;
/** Array of call arguments for the transformed function call */
args: Array<any>;
/** Optional call property to override the generated call expression */
call?: any;
/** Whether the element can be marked with pure annotation for optimization */
pure: boolean;
/** Optional custom callee to use for the call expression */
callee?: any;
}
interface PluginPass<T = any> {
/** Get a value from plugin state */
get(key: string): any;
/** Set a value in plugin state */
set(key: string, value: any): void;
/** Plugin options */
opts: T;
/** Add a helper function to the program */
addHelper(name: string): t.Expression;
}
interface Visitor<T = PluginPass> {
/** Visitor method for JSX elements */
JSXElement?: {
exit?(path: NodePath<t.JSXElement>, state: T): void;
};
/** Visitor method for JSX fragments */
JSXFragment?: {
exit?(path: NodePath<t.JSXFragment>, state: T): void;
};
/** Visitor method for JSX namespaced names */
JSXNamespacedName?(path: NodePath<t.JSXNamespacedName>, state: T): void;
/** Visitor method for JSX spread children */
JSXSpreadChild?(path: NodePath<t.JSXSpreadChild>, state: T): void;
/** Additional visitor methods */
[key: string]: any;
}
interface NodePath<T = t.Node> {
/** The AST node */
node: T;
/** Replace this node with another */
replaceWith(node: t.Node): void;
/** Build a code frame error */
buildCodeFrameError(message: string): Error;
/** Get a child path */
get(key: string): NodePath;
}State Usage Example:
const visitor = builderReactJsx({
pre(state, pass) {
// Log transformation details
console.log(`Transforming: ${state.tagName}`);
console.log(`Arguments: ${state.args.length}`);
// Mark components as pure for optimization
if (state.tagName && /^[A-Z]/.test(state.tagName)) {
state.pure = true;
}
// Override call expression for custom JSX handling
if (state.tagName === "Fragment") {
state.callee = t.memberExpression(
t.identifier("React"),
t.identifier("Fragment")
);
}
},
post(state, pass) {
// Post-process the generated call
if (state.call && state.pure) {
console.log("Pure component transformed");
}
}
});The returned visitor object contains these methods for handling different JSX constructs:
Transforms JSX elements (<div>, <Component>) into function calls.
visitor.JSXElement = {
exit(path: NodePath<t.JSXElement>, state: PluginPass<Options>): void;
}Transforms JSX fragments (<>...</>) into function calls. Throws error in compatibility mode.
visitor.JSXFragment = {
exit(path: NodePath<t.JSXFragment>, state: PluginPass<Options>): void;
}Handles JSX namespace tags (<ns:tag>). Throws error if throwIfNamespace option is true.
visitor.JSXNamespacedName = function(path: NodePath<t.JSXNamespacedName>, state: PluginPass<Options>): void;Handles JSX spread children syntax. Always throws error as React doesn't support spread children.
visitor.JSXSpreadChild = function(path: NodePath<t.JSXSpreadChild>, state: PluginPass<Options>): void;The package throws errors in these cases:
throwIfNamespace: true and namespace tags like <ns:tag> are encountered{...children} syntaxcompat: true and JSX fragments <>...</> are useduseSpread: true and useBuiltIns: true are both set// Error examples
builderReactJsx({ throwIfNamespace: true }); // Throws on <ns:tag>
builderReactJsx({ compat: true }); // Throws on <></>
builderReactJsx({ useSpread: true, useBuiltIns: true }); // Throws on conflicting optionsconst visitor = builderReactJsx({
filter(node, pass) {
// Only transform JSX elements with className
return node.type === "JSXElement" &&
node.openingElement.attributes.some(attr =>
attr.type === "JSXAttribute" &&
attr.name.name === "className"
);
},
pre(state, pass) {
// Add development mode warnings
if (process.env.NODE_ENV === "development") {
state.args.push(
t.objectExpression([
t.objectProperty(
t.identifier("__source"),
t.objectExpression([])
)
])
);
}
}
});const optimizedVisitor = builderReactJsx({
pre(state, pass) {
// Mark functional components as pure for better performance
const isComponent = state.tagName && /^[A-Z]/.test(state.tagName);
const hasSimpleProps = state.args.length <= 2;
if (isComponent && hasSimpleProps) {
state.pure = true;
}
},
useSpread: true, // Modern attribute handling
throwIfNamespace: true // Strict JSX compliance
});const legacyVisitor = builderReactJsx({
compat: true, // Disable fragments for React < 16
useBuiltIns: false, // Use Babel helpers for broader compatibility
post(state, pass) {
// Add displayName for debugging in legacy environments
if (state.tagName && /^[A-Z]/.test(state.tagName)) {
// Custom post-processing for legacy React patterns
console.log(`Legacy transform: ${state.tagName}`);
}
}
});@babel/types - AST node types and utilities for JavaScript transformations@babel/helper-annotate-as-pure - Helper for adding pure annotations to function calls@babel/core - Core Babel types including PluginPass, NodePath, and Visitor interfacesThe package integrates deeply with Babel's AST manipulation:
// Used internally for AST construction
import {
callExpression,
identifier,
memberExpression,
objectExpression,
stringLiteral,
// ... other AST node constructors
} from "@babel/types";