Complete reference for all Regrafter APIs.
Move JSX elements, text nodes, or expressions to a new location with automatic dependency management.
function move(
files: FileInput[],
from: Selector,
to: Selector,
mode: Move,
options?: Options
): Result<TransformedCode[], RegraffError>Parameters:
| Parameter | Type | Description |
|---|---|---|
files |
FileInput[] |
Array of files to transform |
from |
Selector |
Source element location |
to |
Selector |
Target location |
mode |
Move |
Positioning relative to target (Inside, Before, After) |
options |
Options? |
Optional configuration |
Returns: Result<TransformedCode[], RegraffError>
Example:
import { move, Move, isOk } from 'regrafter';
const result = move(
files,
{ file: 'App.tsx', line: 10, column: 5 },
{ file: 'App.tsx', line: 20, column: 5 },
Move.Inside
);
if (isOk(result)) {
console.log('Transformed code:', result.value);
} else {
console.error('Move failed:', result.error);
}Cross-file moves:
const result = move(
files,
{ file: 'Dashboard.tsx', line: 15, column: 9 },
{ file: 'Sidebar.tsx', line: 10, column: 5 },
Move.Inside
);
if (isOk(result)) {
const codes = result.value;
const newFiles = codes.filter(c => c.isNew);
if (newFiles.length > 0) {
console.log('Created shared modules:', newFiles.map(f => f.file));
}
}Move Directions:
The mode parameter controls how the element is positioned relative to the target:
Move.Before- Insert element before the target elementMove.After- Insert element after the target elementMove.Inside- Insert element as a child of the target element (prepends by default)Move.Replace- Replace the target element entirely
Controlling Insertion Position (Move.Inside only):
When using Move.Inside, the element is prepended (inserted as the first child) by default. You can control the insertion position using the insertIndex option:
// Default: prepend (insert at beginning)
move(files, from, to, Move.Inside);
// Insert at a specific position (0-based index)
move(files, from, to, Move.Inside, { insertIndex: 0 }); // First child
move(files, from, to, Move.Inside, { insertIndex: 2 }); // Third child
// Append (insert at end)
move(files, from, to, Move.Inside, { insertIndex: -1 }); // Last childExample: Controlling child order
// Source:
<div>
<p>Existing child</p>
</div>
<span>New element</span>
// Default prepend behavior:
move(files, from, to, Move.Inside);
// Result: <div><span>New element</span><p>Existing child</p></div>
// Append behavior:
move(files, from, to, Move.Inside, { insertIndex: -1 });
// Result: <div><p>Existing child</p><span>New element</span></div>Extract selected JSX into a new reusable component with automatic dependency lifting.
function extract(
files: FileInput[],
selection: Selector,
componentName: string,
options?: ExtractOptions
): Result<TransformedCode[], RegraffError>Parameters:
| Parameter | Type | Description |
|---|---|---|
files |
FileInput[] |
Array of files to transform |
selection |
Selector |
JSX to extract |
componentName |
string |
Name for the new component |
options |
ExtractOptions? |
Optional configuration |
ExtractOptions:
interface ExtractOptions {
targetFile?: string; // Target file (defaults to same file)
insertPosition?: Selector; // Where to insert component (defaults to before current component)
exportComponent?: boolean; // Export the component (default: false)
memo?: boolean; // Wrap in React.memo (default: false)
forwardRef?: boolean; // Use forwardRef (default: false)
}Returns: Result<TransformedCode[], RegraffError>
Example:
import { extract, isOk } from 'regrafter';
// Extract JSX into a new component
const result = extract(
files,
{ file: 'App.tsx', line: 15, column: 9 }, // Selection
'UserProfile', // Component name
{ exportComponent: true, memo: true }
);
if (isOk(result)) {
console.log('Created component UserProfile');
console.log('Transformed code:', result.value);
}Extract to different file:
const result = extract(
files,
{ file: 'Dashboard.tsx', line: 20, column: 5 },
'DashboardCard',
{
targetFile: 'components/DashboardCard.tsx',
exportComponent: true
}
);Inline a component definition, replacing all call sites with the component's implementation.
function inline(
files: FileInput[],
component: Component | string,
options?: InlineOptions
): Result<InlineResult, RegraffError>Parameters:
| Parameter | Type | Description |
|---|---|---|
files |
FileInput[] |
Array of files to transform |
component |
Component | string |
Component to inline (selector or name) |
options |
InlineOptions? |
Optional configuration |
Component Selector:
type Component = {
file: string;
name: string;
} | Selector;InlineOptions:
interface InlineOptions {
callSites?: Selector[]; // Specific call sites to inline (defaults to all)
preserveDefinition?: boolean; // Keep original definition (default: false)
simplifyJSX?: boolean; // Simplify resulting JSX (default: true)
}Returns: Result<InlineResult, RegraffError>
interface InlineResult {
codes: TransformedCode[];
inlinedCallSites: number;
removedDefinition: boolean;
}Example:
import { inline, isOk } from 'regrafter';
// Inline all usages of a component
const result = inline(
files,
{ file: 'components/Button.tsx', name: 'Button' }
);
if (isOk(result)) {
const { codes, inlinedCallSites } = result.value;
console.log(`Inlined ${inlinedCallSites} call sites`);
}Inline specific call sites:
const result = inline(
files,
'SmallComponent', // Component name
{
callSites: [
{ file: 'App.tsx', line: 10, column: 5 },
{ file: 'App.tsx', line: 25, column: 9 }
],
preserveDefinition: true
}
);Quick validation check without executing transformation.
function canMove(
files: FileInput[],
from: Selector,
to: Selector,
mode: Move
): booleanExample:
import { canMove, Move } from 'regrafter';
if (canMove(files, from, to, Move.Inside)) {
// Safe to proceed
const result = move(files, from, to, Move.Inside);
}Detailed dependency analysis without performing transformation.
function analyze(
files: FileInput[],
from: Selector,
to: Selector,
mode: Move
): Result<MoveAnalysis, RegraffError>Returns: MoveAnalysis object
interface MoveAnalysis {
canMove: boolean;
dependencies: Dependency[];
hoistedDeps: Dependency[];
stats: AnalysisStats;
reason?: string;
suggestedFixes?: SuggestedFix[];
}Example:
import { analyze, Move, isOk } from 'regrafter';
const result = analyze(files, from, to, Move.Inside);
if (isOk(result)) {
const analysis = result.value;
if (analysis.canMove) {
console.log('Dependencies:', analysis.dependencies);
console.log('Would hoist:', analysis.hoistedDeps);
console.log('Statistics:', analysis.stats);
} else {
console.log('Cannot move:', analysis.reason);
console.log('Suggested fixes:', analysis.suggestedFixes);
}
}Validate if JSX can be extracted into a component.
function canExtract(
files: FileInput[],
selection: Selector
): booleanDetailed analysis for component extraction.
function analyzeExtract(
files: FileInput[],
selection: Selector,
componentName: string
): Result<ExtractAnalysis, RegraffError>Returns: ExtractAnalysis object
interface ExtractAnalysis {
canExtract: boolean;
props: PropInfo[];
dependencies: Dependency[];
hooks: string[];
reason?: string;
suggestedFixes?: SuggestedFix[];
}Optimize files by sinking over-hoisted dependencies to minimal necessary scopes.
function optimize(
files: FileInput[],
options?: OptimizeOptions
): Result<TransformedCode[], RegraffError>OptimizeOptions:
interface OptimizeOptions {
aggressive?: boolean; // More aggressive optimization (default: false)
}Example:
import { optimize, isOk } from 'regrafter';
const result = optimize(files, { aggressive: true });
if (isOk(result)) {
console.log('Optimized code:', result.value);
}High-level API that wraps move() with validation and optimization. Maintained for backward compatibility.
function regraft(
files: FileInput[],
from: Selector,
to: Selector,
mode: Move,
options?: Options
): Result<RegraftResult, RegraffError>Note: New code should prefer using move(), extract(), or inline() directly for better control.
Element selection using position or AST path.
type Selector = PositionSelector | PathSelector;
interface PositionSelector {
file: string;
line: number;
column: number;
}
interface PathSelector {
file: string;
path: string; // AST path (e.g., 'Program.body[0].declaration')
}Element positioning mode.
enum Move {
Inside = 'inside', // Insert as child
Before = 'before', // Insert before target
After = 'after' // Insert after target
}Configuration options for transformations.
interface Options {
optimize?: boolean; // Run optimization (default: true)
dryRun?: boolean; // Preview only (default: false)
preserveComments?: boolean; // Preserve comments (default: true)
formatOutput?: boolean; // Format with Prettier (default: true)
}Input file specification.
interface FileInput {
path: string; // File path
content: string; // Source code
}Output file with transformation metadata.
interface TransformedCode {
file: string; // File path
content: string; // Transformed code
changed: boolean; // Whether modified
isNew?: boolean; // Whether newly created
original?: string; // Original content if changed
}Dependency information.
interface Dependency {
type: DependencyType;
symbol: string;
scope: string;
locations: Location[];
}
enum DependencyType {
Hook = 'Hook',
Variable = 'Variable',
Import = 'Import',
Prop = 'Prop',
Context = 'Context',
Ref = 'Ref'
}Analysis result for move operations.
interface MoveAnalysis {
canMove: boolean;
dependencies: Dependency[];
hoistedDeps: Dependency[];
stats: AnalysisStats;
reason?: string;
suggestedFixes?: SuggestedFix[];
}
interface AnalysisStats {
totalDeps: number;
hoistedCount: number;
crossFileMove: boolean;
sharedModulesCreated: number;
}Error recovery suggestion.
interface SuggestedFix {
description: string;
action?: string;
automatic: boolean;
}All Regrafter APIs use a Result monad for error handling instead of throwing exceptions.
type Result<T, E> = Ok<T> | Err<E>;
interface Ok<T> {
ok: true;
value: T;
}
interface Err<E> {
ok: false;
error: E;
}Helper functions:
// Type guards
isOk(result): result is Ok<T>
isErr(result): result is Err<E>
// Constructors
ok<T>(value: T): Ok<T>
err<E>(error: E): Err<E>
// Transformations
map<T, U>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>
flatMap<T, U>(result: Result<T, E>, fn: (value: T) => Result<U, E>): Result<U, E>
mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>
// Extraction
unwrap<T>(result: Result<T, E>): T // Throws if Err
unwrapOr<T>(result: Result<T, E>, defaultValue: T): T
unwrapOrElse<T>(result: Result<T, E>, fn: (error: E) => T): T
// Combining
all<T>(results: Result<T, E>[]): Result<T[], E>
any<T>(results: Result<T, E>[]): Result<T, E[]>
// Async support
mapAsync<T, U>(result: Result<T, E>, fn: (value: T) => Promise<U>): Promise<Result<U, E>>
flatMapAsync<T, U>(result: Result<T, E>, fn: (value: T) => Promise<Result<U, E>>): Promise<Result<U, E>>
// Error handling
tryCatch<T>(fn: () => T): Result<T, Error>
tryCatchAsync<T>(fn: () => Promise<T>): Promise<Result<T, Error>>Example usage:
import { move, Move, isOk, isErr, map, unwrapOr } from 'regrafter';
const result = move(files, from, to, Move.Inside);
// Type guard approach
if (isOk(result)) {
console.log('Success:', result.value);
} else {
console.error('Error:', result.error);
}
// Transformation approach
const formatted = map(result, codes =>
codes.map(c => c.content).join('\n')
);
// Default value approach
const codes = unwrapOr(result, []);Process multiple operations efficiently.
interface BatchResult<T, E> {
successful: T[];
failed: Array<{ index: number; error: E }>;
successCount: number;
failureCount: number;
}
function processBatch<T, E>(
items: T[],
processor: (item: T) => Result<T, E>
): BatchResult<T, E>Example:
import { processBatch, move, Move } from 'regrafter';
const moves = [
{ from: selector1, to: target1 },
{ from: selector2, to: target2 },
{ from: selector3, to: target3 }
];
const batchResult = processBatch(moves, ({ from, to }) =>
move(files, from, to, Move.Inside)
);
console.log(`Success: ${batchResult.successCount}`);
console.log(`Failed: ${batchResult.failureCount}`);
for (const failure of batchResult.failed) {
console.error(`Move ${failure.index} failed:`, failure.error);
}