Comprehensive examples demonstrating Regrafter's APIs in real-world scenarios.
Move a JSX element from one location to another within the same file.
import { move, Move, isOk } from 'regrafter';
const files = [{
path: 'App.tsx',
content: `
function App() {
const [count, setCount] = useState(0);
return (
<div>
<Header />
<Counter value={count} onChange={setCount} />
<Footer />
</div>
);
}
`
}];
// Move <Counter /> inside <Header />
const result = move(
files,
{ file: 'App.tsx', line: 7, column: 11 }, // from: Counter element
{ file: 'App.tsx', line: 6, column: 11 }, // to: Header element
Move.Inside
);
if (isOk(result)) {
console.log('Transformed code:', result.value[0].content);
// Header now contains Counter with automatic prop threading
}Moving an element that has local dependencies automatically hoists them.
import { move, Move, isOk } from 'regrafter';
const files = [{
path: 'Dashboard.tsx',
content: `
function Dashboard() {
const [data, setData] = useState([]);
const [filter, setFilter] = useState('');
const filteredData = useMemo(() =>
data.filter(item => item.name.includes(filter)),
[data, filter]
);
return (
<div>
<Sidebar />
<MainContent>
<DataTable data={filteredData} />
</MainContent>
</div>
);
}
`
}];
// Move DataTable to Sidebar - dependencies will be hoisted
const result = move(
files,
{ file: 'Dashboard.tsx', line: 14, column: 13 }, // DataTable
{ file: 'Dashboard.tsx', line: 12, column: 11 }, // Sidebar
Move.Inside
);
if (isOk(result)) {
// filteredData, data, setData, filter, setFilter are automatically
// made available to Sidebar via props
console.log(result.value[0].content);
}Move elements between files with automatic import management.
import { move, Move, isOk } from 'regrafter';
const files = [
{
path: 'Dashboard.tsx',
content: `
import { UserProfile } from './UserProfile';
function Dashboard() {
return (
<div>
<UserProfile userId="123" />
<MainContent />
</div>
);
}
`
},
{
path: 'Sidebar.tsx',
content: `
function Sidebar() {
return <nav>Navigation</nav>;
}
`
}
];
// Move UserProfile from Dashboard to Sidebar
const result = move(
files,
{ file: 'Dashboard.tsx', line: 6, column: 13 }, // UserProfile
{ file: 'Sidebar.tsx', line: 3, column: 16 }, // Sidebar nav
Move.Before
);
if (isOk(result)) {
// Sidebar.tsx now imports UserProfile automatically
// Dashboard.tsx import is updated/removed if unused
const sidebar = result.value.find(f => f.file === 'Sidebar.tsx');
console.log('Sidebar with import:', sidebar?.content);
}Move text content between elements.
import { move, Move, isOk } from 'regrafter';
const files = [{
path: 'Component.tsx',
content: `
function Component() {
return (
<div>
<h1>Hello World</h1>
<p>Welcome</p>
</div>
);
}
`
}];
// Move "Hello World" text from h1 to p
const result = move(
files,
{ file: 'Component.tsx', line: 5, column: 15 }, // "Hello World" text
{ file: 'Component.tsx', line: 6, column: 14 }, // <p> element
Move.Inside
);
if (isOk(result)) {
console.log(result.value[0].content);
// <p> now contains "Hello World"
// <h1> is empty or removed
}Move expression containers between elements.
import { move, Move, isOk } from 'regrafter';
const files = [{
path: 'Component.tsx',
content: `
function Component() {
const name = "World";
const greeting = "Hello";
return (
<div>
<h1>{greeting}</h1>
<p>{name}</p>
</div>
);
}
`
}];
// Move {name} expression from p to h1
const result = move(
files,
{ file: 'Component.tsx', line: 9, column: 14 }, // {name}
{ file: 'Component.tsx', line: 8, column: 15 }, // {greeting} in h1
Move.After
);
if (isOk(result)) {
// <h1> now contains both expressions
console.log(result.value[0].content);
}Extract JSX into a reusable component.
import { extract, isOk } from 'regrafter';
const files = [{
path: 'App.tsx',
content: `
function App() {
const [user, setUser] = useState(null);
return (
<div>
<header>
<img src={user?.avatar} alt={user?.name} />
<h1>{user?.name}</h1>
<p>{user?.email}</p>
</header>
<main>Content</main>
</div>
);
}
`
}];
// Extract user profile section
const result = extract(
files,
{ file: 'App.tsx', line: 7, column: 13 }, // Select <img> to <p>
'UserProfile',
{ exportComponent: true }
);
if (isOk(result)) {
console.log(result.value[0].content);
// Creates UserProfile component with user prop
// Replaces selected JSX with <UserProfile user={user} />
}Extract component to a new file.
import { extract, isOk } from 'regrafter';
const files = [{
path: 'Dashboard.tsx',
content: `
function Dashboard() {
const stats = useStats();
return (
<div>
<div className="stats-card">
<h2>{stats.title}</h2>
<p className="value">{stats.value}</p>
<p className="change">{stats.change}%</p>
</div>
</div>
);
}
`
}];
// Extract to new file
const result = extract(
files,
{ file: 'Dashboard.tsx', line: 7, column: 11 },
'StatsCard',
{
targetFile: 'components/StatsCard.tsx',
exportComponent: true,
memo: true // Wrap in React.memo
}
);
if (isOk(result)) {
const codes = result.value;
// Find new file
const newFile = codes.find(c => c.file === 'components/StatsCard.tsx');
if (newFile) {
console.log('Created StatsCard.tsx:', newFile.content);
}
// Original file updated with import
const dashboard = codes.find(c => c.file === 'Dashboard.tsx');
console.log('Updated Dashboard.tsx:', dashboard?.content);
}Extract component that needs ref forwarding.
import { extract, isOk } from 'regrafter';
const files = [{
path: 'Form.tsx',
content: `
function Form() {
const inputRef = useRef(null);
return (
<form>
<label>Name:</label>
<input
ref={inputRef}
type="text"
placeholder="Enter name"
/>
</form>
);
}
`
}];
// Extract input with ref forwarding
const result = extract(
files,
{ file: 'Form.tsx', line: 8, column: 11 },
'TextInput',
{
exportComponent: true,
forwardRef: true // Enable ref forwarding
}
);
if (isOk(result)) {
console.log(result.value[0].content);
// Creates TextInput with forwardRef
// Original ref is forwarded to the component
}Inline a simple component at all call sites.
import { inline, isOk } from 'regrafter';
const files = [{
path: 'components.tsx',
content: `
function Button({ label, onClick }) {
return (
<button className="btn" onClick={onClick}>
{label}
</button>
);
}
function App() {
return (
<div>
<Button label="Click me" onClick={() => alert('Hi')} />
<Button label="Submit" onClick={handleSubmit} />
</div>
);
}
`
}];
// Inline Button component
const result = inline(
files,
{ file: 'components.tsx', name: 'Button' }
);
if (isOk(result)) {
const { codes, inlinedCallSites, removedDefinition } = result.value;
console.log(`Inlined ${inlinedCallSites} call sites`);
console.log(`Definition removed: ${removedDefinition}`);
console.log('Result:', codes[0].content);
// Both <Button /> calls are replaced with inline <button> elements
// Button definition is removed
}Inline only specific call sites.
import { inline, isOk } from 'regrafter';
const files = [{
path: 'App.tsx',
content: `
function SmallComponent({ text }) {
return <span className="small">{text}</span>;
}
function App() {
return (
<div>
<SmallComponent text="One" />
<SmallComponent text="Two" />
<SmallComponent text="Three" />
</div>
);
}
`
}];
// Inline only first and third call sites
const result = inline(
files,
'SmallComponent',
{
callSites: [
{ file: 'App.tsx', line: 9, column: 11 }, // First
{ file: 'App.tsx', line: 11, column: 11 } // Third
],
preserveDefinition: true // Keep component definition
}
);
if (isOk(result)) {
console.log('Inlined 2 out of 3 call sites');
console.log('Component definition preserved');
}Handle components with complex prop transformations.
import { inline, isOk } from 'regrafter';
const files = [{
path: 'App.tsx',
content: `
function Card({ title, children, footer }) {
return (
<div className="card">
<div className="card-header">
<h2>{title}</h2>
</div>
<div className="card-body">
{children}
</div>
{footer && (
<div className="card-footer">
{footer}
</div>
)}
</div>
);
}
function App() {
return (
<Card
title="User Profile"
footer={<button>Save</button>}
>
<p>Content here</p>
</Card>
);
}
`
}];
// Inline Card component
const result = inline(
files,
'Card',
{ simplifyJSX: true } // Simplify resulting JSX
);
if (isOk(result)) {
console.log(result.value.codes[0].content);
// Props are substituted correctly:
// - title becomes "User Profile"
// - children becomes <p>Content here</p>
// - footer becomes <button>Save</button>
}Validate before expensive operations.
import { canMove, move, Move, isOk } from 'regrafter';
// Quick check (fast)
if (canMove(files, from, to, Move.Inside)) {
// Safe to proceed
const result = move(files, from, to, Move.Inside);
if (isOk(result)) {
console.log('Move successful');
}
} else {
console.log('Move not possible');
}Get detailed information about dependencies and hoisting.
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('Move is possible');
console.log('Total dependencies:', analysis.stats.totalDeps);
console.log('Dependencies to hoist:', analysis.stats.hoistedCount);
// Show hook dependencies
const hooks = analysis.dependencies.filter(d => d.type === 'Hook');
console.log('Hooks:', hooks.map(h => h.symbol));
// Show variable dependencies
const vars = analysis.dependencies.filter(d => d.type === 'Variable');
console.log('Variables:', vars.map(v => v.symbol));
// Show which dependencies will be hoisted
console.log('Will hoist:', analysis.hoistedDeps.map(d => d.symbol));
} else {
console.log('Cannot move:', analysis.reason);
// Show suggested fixes
if (analysis.suggestedFixes) {
for (const fix of analysis.suggestedFixes) {
console.log(`Suggestion: ${fix.description}`);
}
}
}
}Analyze what will be extracted before extraction.
import { analyzeExtract, isOk } from 'regrafter';
const result = analyzeExtract(
files,
{ file: 'App.tsx', line: 10, column: 5 },
'MyComponent'
);
if (isOk(result)) {
const analysis = result.value;
if (analysis.canExtract) {
console.log('Component props:', analysis.props.map(p => p.name));
console.log('Dependencies:', analysis.dependencies.map(d => d.symbol));
console.log('Hooks used:', analysis.hooks);
} else {
console.log('Cannot extract:', analysis.reason);
}
}Optimize code after performing multiple transformations.
import { move, optimize, Move, isOk, flatMap } from 'regrafter';
// Perform multiple moves
let codes = files;
const result1 = move(codes, from1, to1, Move.Inside);
if (isOk(result1)) {
codes = result1.value;
}
const result2 = move(codes, from2, to2, Move.After);
if (isOk(result2)) {
codes = result2.value;
}
const result3 = move(codes, from3, to3, Move.Before);
if (isOk(result3)) {
codes = result3.value;
}
// Optimize to sink dependencies to minimal scopes
const optimized = optimize(codes, { aggressive: true });
if (isOk(optimized)) {
console.log('Optimized code:', optimized.value);
}Chain operations using Result monad.
import { move, optimize, Move, flatMap, isOk } from 'regrafter';
const result = flatMap(
move(files, from, to, Move.Inside),
codes => optimize(codes, { aggressive: false })
);
if (isOk(result)) {
console.log('Moved and optimized:', result.value);
} else {
console.error('Operation failed:', result.error);
}Process multiple moves efficiently with batch processing.
import { processBatch, move, Move } from 'regrafter';
const moves = [
{
from: { file: 'App.tsx', line: 10, column: 5 },
to: { file: 'App.tsx', line: 20, column: 9 }
},
{
from: { file: 'Dashboard.tsx', line: 15, column: 7 },
to: { file: 'Sidebar.tsx', line: 10, column: 5 }
},
{
from: { file: 'Form.tsx', line: 25, column: 11 },
to: { file: 'Form.tsx', line: 30, column: 13 }
}
];
const batchResult = processBatch(moves, ({ from, to }) =>
move(files, from, to, Move.Inside)
);
console.log(`Successful: ${batchResult.successCount}`);
console.log(`Failed: ${batchResult.failureCount}`);
// Process successful moves
for (const codes of batchResult.successful) {
console.log('Move succeeded:', codes[0].file);
}
// Handle failures
for (const failure of batchResult.failed) {
console.error(`Move ${failure.index} failed:`, failure.error.message);
}Handle different error types appropriately.
import {
move,
Move,
isErr,
isValidationError,
isDependencyError,
isSelectorError
} from 'regrafter';
const result = move(files, from, to, Move.Inside);
if (isErr(result)) {
const error = result.error;
if (isSelectorError(error)) {
console.error('Element not found or invalid selection');
console.error('File:', error.context?.file);
console.error('Position:', error.context?.line, error.context?.column);
} else if (isValidationError(error)) {
console.error('Move violates constraints');
console.error('Reason:', error.message);
// Show suggested fixes
for (const fix of error.suggestions) {
console.log('Try:', fix.description);
}
} else if (isDependencyError(error)) {
console.error('Cannot resolve dependencies');
console.error('Affected:', error.context?.symbols);
}
}Attempt automatic recovery from errors.
import { move, Move, isErr, isRecoverable, attemptRecovery } from 'regrafter';
const result = move(files, from, to, Move.Inside);
if (isErr(result)) {
if (isRecoverable(result.error)) {
console.log('Attempting automatic recovery...');
const recovery = await attemptRecovery(result.error);
if (recovery.success) {
console.log('Recovery successful!');
console.log('Result:', recovery.result);
if (recovery.warnings) {
console.warn('Warnings:', recovery.warnings);
}
} else {
console.error('Recovery failed:', recovery.reason);
}
} else {
console.error('Error is not recoverable');
}
}See Advanced Usage for:
- Custom dependency analysis
- Hoisting strategy control
- Atomic unit detection
- AST path selectors
- Custom transformations