@autotracer/inject-react18
v1.0.0-alpha.35
Published
Shared AST transform logic for auto-injecting useReactTracer
Readme
@autotracer/inject-react18
Core AST transformation engine for automatic React component instrumentation.
This package provides the shared transformation logic used by all ReactTracer build plugins. It analyzes your React components and automatically injects tracing hooks—you typically don't use this directly unless building custom tooling.
What You Get
- AST-based transformation - Safe, non-destructive code injection
- Component detection - Identifies React components automatically
- Hook labeling - Extracts variable names from
useState,useReducer, custom hooks - Pragma support - Function-level control (
@trace,@trace-disableper component) - Plugin foundation - Powers Vite and Babel plugins
Who This Is For
This is a low-level library for build tool authors. If you're using Vite or Next.js, use the dedicated plugins instead:
- Vite: @autotracer/plugin-vite-react18
- Next.js/Babel: @autotracer/plugin-babel-react18
Overview
This package provides the core Babel AST transformation functionality used by the Auto Tracer ecosystem to automatically inject tracing hooks into React components. It analyzes TypeScript/JSX code, identifies React components, and injects useReactTracer initialization along with labelState calls for configured hooks.
Features
- Automatic Component Detection: Identifies React function components, arrow functions, and HOC-wrapped components
- Hook Labeling: Injects
labelStatecalls foruseState,useReducer, and configured custom hooks - Function-Level Pragma Support: Respects per-component tracing control pragmas (
@trace,@trace-disable) - TypeScript Support: Full TypeScript and JSX parsing support
- Safe AST Manipulation: Uses Babel's traversal API for safe, non-destructive code transformations
Component Detection Strategy
ReactTracer uses a multi-layer defense system to accurately identify React components while avoiding false positives. This ensures only genuine components receive instrumentation.
Detection Layers
Layer 1: Rejection - Explicit Non-Components
Functions in these contexts are never treated as components:
React.lazy(() => import(...))- Module loaderspromise.then(() => ...)- Promise callbacksarray.map((item) => ...)- Array method callbackssetTimeout(() => ...)- Timing callbacksaddEventListener(() => ...)- Event handlers- ALL async functions - Server Components, data loaders, utilities (hooks don't work in async)
Layer 2: Signal Detection - Component Indicators
At least ONE of these signals is required:
- Returns JSX elements (
<div>...</div>) or fragments (<>...</>) - Calls React hooks (
useState,useEffect, custom hooks) - Contains JSX anywhere in function body
Layer 3: Validation - Structure
Additional requirements:
- Top-level declarations (not nested functions)
- Function declaration, arrow function, or function expression
- No naming restrictions - Both PascalCase and camelCase accepted
Supported Patterns
// ✅ Function declarations with JSX
function Button() {
return <button>Click</button>;
}
// ✅ Arrow functions with JSX
const Card = () => <div>Card</div>;
// ✅ Components with hooks
const Form = () => {
const [value, setValue] = useState("");
return <input value={value} />;
};
// ✅ HOC-wrapped components
const MemoButton = memo(() => <button>Memo</button>);
const RefInput = forwardRef((props, ref) => <input ref={ref} />);
// ✅ Conditional JSX
function Conditional({ show }) {
if (show) return <div>Visible</div>;
return null;
}
// ✅ camelCase components with JSX
const renderHeader = () => <header>Title</header>;
// ✅ camelCase components with hooks
const useCustomComponent = () => {
const [state] = useState();
return <div>{state}</div>;
};Rejected Patterns
// ❌ React.lazy() loader (not a component)
const Lazy = lazy(() => import("./Component"));
// ❌ Promise callbacks
fetch("/api").then((res) => res.json());
// ❌ Array methods (even with JSX)
items.map((item) => <li key={item.id}>{item.name}</li>);
// ❌ ALL async functions (even with JSX - hooks don't work in async)
async function ServerData() {
const data = await fetchData();
return <div>{data}</div>;
}
// ❌ Async utilities
async function fetchUserData(id) {
return await fetch(`/api/users/${id}`).then((r) => r.json());
}
// ❌ Utilities without JSX or hooks
const parseJSON = (str) => JSON.parse(str);
const calculateTotal = (items) => items.reduce((sum, i) => sum + i.price, 0);
// ❌ Event handlers
setTimeout(() => console.log("tick"), 1000);Manual Override
If detection fails for your component, use pragmas:
// Force enable (opt-in mode)
// @trace
const unusualComponent = customWrapper(() => <div />);
// Force disable (even if has JSX/hooks)
// @trace-disable
const utilityWithJSX = () => <div>Template</div>;Architecture
The transformation pipeline processes your source code through several stages:
graph TD
A[Source Code] --> B[Babel Parser]
B --> C[AST Analysis]
C --> D[Component Detection]
D --> E[Hook Scanning]
E --> F[AST Injection]
F --> G[Code Generation]
G --> H[Transformed Code]
D --> I{Pragma Check}
I --> J[Skip if disabled]
I --> K[Continue if enabled]
E --> L[useState/useReducer]
E --> M[Configured Hooks]
E --> N[Custom Patterns]
F --> O[useReactTracer injection]
F --> P[labelState calls]Want to dive deeper? See Architecture Details for implementation specifics.
Usage
Basic Transformation
import { transform } from "@autotracer/inject-react18";
const sourceCode = `
function MyComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
return <div>{count} {name}</div>;
}
`;
const result = transform(sourceCode, {
filename: "MyComponent.tsx",
config: {
mode: "opt-out",
importSource: "@autotracer/react18",
labelHooks: ["useState"],
},
});
console.log(result.code);
// Output:
// import { useReactTracer } from '@autotracer/react18';
// function MyComponent() {
// const __reactTracer = useReactTracer({ name: "MyComponent" });
// const [count, setCount] = useState(0);
// __reactTracer.labelState("count", 0);
// const [name, setName] = useState('');
// __reactTracer.labelState("name", 1);
// return <div>{count} {name}</div>;
// }Configuration Options
interface TransformConfig {
/** Controls how components are selected for injection. */
mode: "opt-in" | "opt-out";
/** Include filters for files and components. */
include?: {
/** Glob patterns for files to include. */
paths?: string[];
/** Component name patterns to include (exact string, glob-like string, or RegExp). */
components?: Array<string | RegExp>;
};
/** Exclude filters for files and components. */
exclude?: {
/** Glob patterns for files to exclude. */
paths?: string[];
/** Component name patterns to exclude (exact string, glob-like string, or RegExp). */
components?: Array<string | RegExp>;
};
/** Enables React Server Components (RSC) safety checks. */
serverComponents?: boolean;
/** Module to import runtime helpers from when injecting. */
importSource?: string;
/** Specific hook names to label. */
labelHooks?: string[];
/** Regex pattern (as a string) for matching additional hook names. */
labelHooksPattern?: string;
}Pragma Control
Control tracing behavior for individual components using special leading comments:
function MyComponent() {
const [state, setState] = useState();
// ...
}Notes:
- Pragmas are always enabled; there is no configuration option to disable pragma handling.
@trace-disablealways wins over@tracewhen both are present.- Class components are not instrumented by this transformer.
Class Components
This transformer injects hook-based tracing (useReactTracer) and therefore does not instrument React class components.
API Reference
transform(code, context)
The main transformation function.
Parameters:
code: string- Source code to transformcontext: TransformContext- Transformation context with filename and config
Returns: TransformResult
code: string- Transformed source codeinjected: boolean- Whether tracing was injectedcomponents: ComponentInfo[]- Information about detected components
Hook Labeling Rules
- Built-in Hooks:
useStateanduseReducerare always labeled - Configured Hooks: Hooks listed in
labelHooksare labeled - Pattern Matching: Hooks matching
labelHooksPatternregex are labeled
Component Detection
Automatically detects React components in various forms:
- Function declarations:
function MyComponent() {} - Arrow function expressions:
const MyComponent = () => {} - Function expressions:
const MyComponent = function() {} - HOC-wrapped components:
memo(),forwardRef(), etc.
React Server Components (RSC)
If your app uses React Server Components (e.g., Next.js App Router), enable the RSC safety guard by setting serverComponents: true in the transform config (via the Vite plugin or direct usage). When enabled, a module is considered a Server Module unless it has a top-level directive:
"use client";
Only Client Components receive injections; Server Modules are returned unchanged. This avoids injecting client-only hooks into server code.
Key points:
- Default is
serverComponents: falseto avoid surprising changes in non-RSC setups. - With
serverComponents: true, normalmode/include/exclude/pragmarules apply only to files with the "use client" directive. - The check is conservative and literal: the directive must be exactly "use client" at the top level.
License
MIT
