@autotracer/plugin-babel-flow
v1.0.0-alpha.33
Published
Babel plugin for automatic function flow tracing with try/catch/finally injection
Readme
@autotracer/plugin-babel-flow
Babel plugin for automatic function flow tracing with try/catch/finally injection.
Automatically instruments your functions to track entry, exit, parameters, return values, and exceptions. Works with any Babel-based build system including Next.js, Create React App, and custom Webpack/Rollup setups.
What You Get
- Zero-config function instrumentation - Automatically wraps functions with tracing code
- Selective targeting - Choose which functions to instrument via patterns
- Try/catch/finally injection - Safe exception tracking without breaking error handling
- Return value tracing - Captures and logs function return values
- Dormant mode support - Integrates with
@autotracer/flowruntime control - Build-time transformation - Zero runtime overhead for transformation logic
Who This Is For
If you're using Next.js, Create React App, or any Babel-based build, this is your plugin.
For Vite, use @autotracer/plugin-vite-flow instead.
Installation
npm install --save-dev @autotracer/plugin-babel-flow
npm install @autotracer/flow @autotracer/loggerNote: @autotracer/logger is a required dependency of @autotracer/flow. @babel/core is a peer dependency (usually already installed in Babel-based projects).
For pnpm users: Add auto-install-peers=true to your .npmrc file to automatically install peer dependencies:
# .npmrc
auto-install-peers=trueQuickstart
Next.js (App Router or Pages Router)
// babel.config.js or .babelrc
module.exports = {
plugins: [
["@autotracer/plugin-babel-flow", {
include: {
paths: ["**/src/**", "**/app/**"],
functions: ["handle*", "on*", "fetch*"]
}
}]
]
};Then import the runtime in your app:
// app/layout.tsx (App Router) or pages/_app.tsx (Pages Router)
"use client"; // App Router only
import "@autotracer/flow/runtime";
export default function RootLayout({ children }) {
return children;
}Create React App
// .babelrc or babel.config.js
{
"plugins": [
["@autotracer/plugin-babel-flow", {
"include": {
"paths": ["src/**"],
"functions": ["*"] // Instrument all functions in src/
}
}]
]
}Import the runtime in your entry point:
// src/index.tsx
import "@autotracer/flow/runtime";
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);Custom Babel Setup
// babel.config.js
module.exports = {
presets: ["@babel/preset-env", "@babel/preset-typescript"],
plugins: [
["@autotracer/plugin-babel-flow", {
tracerName: "__flowTracer",
logExceptions: true,
exceptionLogLevel: "debug",
include: {
paths: ["src/**/*.ts", "src/**/*.tsx"],
functions: ["handle*", "process*", "calculate*"]
},
exclude: {
paths: ["**/*.test.ts", "**/*.spec.ts"],
functions: ["render*"] // Don't instrument React render methods
}
}]
]
};Configuration Options
interface BabelPluginFlowConfig {
/** Name of the global tracer variable (default: "__flowTracer") */
tracerName?: string;
/** Log exceptions to console when caught (default: true) */
logExceptions?: boolean;
/** Log level for exception logging (default: "debug") */
exceptionLogLevel?: "debug" | "warn" | "error";
/** Files and functions to include */
include?: {
/** Glob patterns for file paths */
paths?: string[];
/** Function name patterns (glob or regex, e.g. "handle*", /^on[A-Z]/) */
functions?: Array<string | RegExp>;
};
/** Files and functions to exclude (takes precedence over include) */
exclude?: {
/** Glob patterns for file paths */
paths?: string[];
/** Function name patterns (glob or regex) */
functions?: Array<string | RegExp>;
};
}Default Configuration
{
tracerName: "__flowTracer",
logExceptions: true,
exceptionLogLevel: "debug",
include: {}, // No filters - all functions instrumented
exclude: {} // Nothing excluded
}Note: The Babel plugin doesn't have enabled or runtimeControlled options. To conditionally enable the plugin, use Babel's environment-based configuration or simply add/remove it from your plugins array. For runtime control, manually import @autotracer/flow/runtime in your app.
How It Works
Before Transformation
function calculateTotal(prices: number[]): number {
const sum = prices.reduce((acc, price) => acc + price, 0);
return sum;
}After Transformation
function calculateTotal(prices: number[]): number {
const __h0 = __flowTracer.enter("calculateTotal", prices);
try {
const sum = prices.reduce((acc, price) => acc + price, 0);
const __returnValue = sum;
__flowTracer.exit(__h0, __returnValue);
return __returnValue;
} catch (__error) {
__flowTracer.exit(__h0);
throw __error;
}
}What Gets Instrumented
The plugin instruments:
- ✅ Named function declarations
- ✅ Arrow function expressions (when assigned to variables)
- ✅ Function expressions
- ✅ Object method shorthand
- ✅ Class methods (instance and static)
- ❌ Anonymous inline callbacks (too noisy)
- ❌ IIFE expressions (immediately invoked)
Filtering Logic
Functions are instrumented if they match ALL of these:
- Path matches
include.pathsAND doesn't matchexclude.paths - Function name matches
include.functionsAND doesn't matchexclude.functions
Pattern Examples
{
include: {
paths: ["src/**"], // All files in src/
functions: ["handle*", "on*"] // handleClick, onClick, etc.
},
exclude: {
paths: ["src/**/*.test.ts"], // Skip test files
functions: ["handleError"] // Skip specific function
}
}Usage Patterns
Event Handlers Only
{
include: {
paths: ["src/**/*.tsx"],
functions: ["handle*", "on*"] // handleClick, onSubmit, etc.
}
}Business Logic Only
{
include: {
paths: ["src/services/**", "src/utils/**"],
functions: ["*"] // All functions in these folders
},
exclude: {
functions: ["render*", "use*"] // Skip React render/hooks
}
}Specific File + Specific Functions
{
include: {
paths: ["src/auth/**"],
functions: ["login*", "logout*", "verify*"]
}
}Development Only
To only instrument in development, conditionally add the plugin:
// babel.config.js
module.exports = {
plugins: [
...(process.env.NODE_ENV === 'development' ? [
["@autotracer/plugin-babel-flow", {
include: {
paths: ["src/**"],
functions: ["*"]
}
}]
] : [])
]
};Runtime Control Integration
Dormant Mode (Recommended for TEST/QA)
The Babel plugin always instruments functions. To use dormant mode (tracing disabled until activated), import the runtime control:
// Your app entry point
import "@autotracer/flow/runtime";
// Now developers can activate in browser console:
// window.startFlowTracing()This is safe for TEST/QA environments because:
- Functions are instrumented but produce no output by default
- Logger is set to "off" (zero console overhead)
- Only activates when developer explicitly enables it
Always Active (Development)
If you don't import the runtime, functions produce immediate output:
// Your app entry point
import "@autotracer/flow"; // Direct import, not /runtime
// All instrumented functions immediately log to consoleException Logging
The plugin wraps function bodies in try/catch/finally to safely log exceptions:
// Your code
function divide(a: number, b: number): number {
if (b === 0) throw new Error("Division by zero");
return a / b;
}
// Transformed (simplified)
function divide(a: number, b: number): number {
const __h0 = __flowTracer.enter("divide", a, b);
try {
if (b === 0) throw new Error("Division by zero");
const __returnValue = a / b;
__flowTracer.exit(__h0, __returnValue);
return __returnValue;
} catch (__error) {
// Exception is logged here before re-throwing
__flowTracer.exit(__h0);
throw __error;
}
}Console output when exception occurs:
divide
params: 10 0
💥 Exception in divide: Error: Division by zero
at divide (math.ts:23:15)
...
divide (elapsed: 0.8ms)The exception is logged and then re-thrown, preserving your app's error handling.
Performance Considerations
Build Time
- First build: Adds ~100-500ms depending on codebase size
- Subsequent builds: Cached, minimal overhead
- Filtering helps: Only instrumenting targeted functions reduces transformation time
Runtime Overhead
Dormant mode (runtimeControlled: true):
- Function call overhead: ~0.1μs (just enter/exit calls)
- No console I/O
- Production-safe
Active mode (runtimeControlled: false or activated):
- Console.group/log overhead: ~50-100μs per function
- Not recommended for production
Recommendations
- Production: Use dormant mode, instrument broadly
- Development: Either always-active or dormant mode
- Filter aggressively: Only instrument what you need to debug
Integration with TypeScript
The plugin works seamlessly with TypeScript:
// babel.config.js
module.exports = {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-typescript"
],
plugins: [
["@autotracer/plugin-babel-flow", {
include: {
paths: ["src/**/*.ts", "src/**/*.tsx"]
}
}]
]
};TypeScript types are preserved in the transformation.
Troubleshooting
Plugin not running
- Check Babel is configured correctly (
babel.config.jsor.babelrc) - Verify
enabled: truein plugin options - Check your build tool is using Babel (some tools skip it)
Functions not instrumented
- Check file path matches
include.pathspatterns - Check function name matches
include.functionspatterns - Verify function isn't in
excludepatterns - Anonymous functions and IIFEs aren't instrumented
No console output
If runtimeControlled: true:
- Import the runtime:
import "@autotracer/flow/runtime" - Activate in browser console:
window.startFlowTracing()
If runtimeControlled: false:
- Check browser console for errors
- Verify
@autotracer/flowis installed - Check logger isn't set to "off"
Build errors
"__flowTracer is not defined":
- Add
import "@autotracer/flow"to your entry point - Or enable
runtimeControlled: truefor automatic injection
Babel transform errors:
- Check your Babel preset configuration
- Ensure TypeScript preset runs before this plugin
- Check for syntax errors in your source files
Next.js specific issues
App Router: Make sure runtime import is in a "use client" component:
// app/layout.tsx
"use client";
import "@autotracer/flow/runtime";Pages Router: Import in pages/_app.tsx:
// pages/_app.tsx
import "@autotracer/flow/runtime";Advanced Usage
Conditional Instrumentation
// babel.config.js
const isDevelopment = process.env.NODE_ENV === "development";
const isDebugBuild = process.env.DEBUG === "true";
module.exports = {
plugins: [
// Only include plugin in dev or debug builds
...(isDevelopment || isDebugBuild ? [
["@autotracer/plugin-babel-flow", {
include: {
paths: isDevelopment
? ["src/**"] // All files in development
: ["src/services/**"] // Only services in debug builds
}
}]
] : [])
]
};Multiple Configurations
You can use different plugin configs per environment:
module.exports = {
env: {
development: {
plugins: [
["@autotracer/plugin-babel-flow", {
include: { paths: ["src/**"], functions: ["*"] }
}]
]
},
test: {
plugins: [
["@autotracer/plugin-babel-flow", {
include: { paths: ["src/services/**"], functions: ["fetch*", "process*"] }
}]
]
},
production: {
plugins: [] // No flow tracing in production
}
}
};Comparison with Vite Plugin
| Feature | Babel Plugin | Vite Plugin | |---------|-------------|-------------| | Build System | Babel-based (Next.js, CRA, Webpack) | Vite only | | Transformation | AST traversal | Babel via Vite | | Runtime Injection | Manual import | Automatic HTML injection | | Configuration | Babel config | Vite config | | Performance | Slower (more comprehensive) | Faster (optimized for Vite) |
Use Babel plugin for Next.js/CRA, Vite plugin for Vite-based apps.
Architecture
graph TD
A[Source Code] --> B[Babel Parser]
B --> C[AST Traversal]
C --> D{Match Filters?}
D -->|Yes| E[Inject Instrumentation]
D -->|No| F[Skip]
E --> G[Wrap in try/catch/finally]
G --> H[Add enter/exit calls]
H --> I[Transform return statements]
I --> J[Code Generation]
F --> J
J --> K[Instrumented Code]License
MIT
