npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

regrafter

v0.3.0

Published

Programmatic AST transformation library for relocating React elements with automatic dependency management

Readme

Regrafter

npm version npm downloads codecov License: MIT TypeScript Node.js

Programmatic AST transformation library for React/JSX code transformations with automatic dependency management.

Regrafter provides three core APIs for transforming React code:

  • move() - Relocate JSX elements within and across files
  • extract() - Extract JSX into reusable components
  • inline() - Inline components at their call sites

All transformations automatically manage dependencies (hooks, variables, imports, props, context, refs) to ensure code correctness.

Why Regrafter?

  • Safety First: Transformed code always compiles and maintains semantic correctness
  • Fully Automated: Dependencies are analyzed and resolved automatically—no manual tracking needed
  • Type-Safe: Built with TypeScript, returns Result<T, E> instead of throwing exceptions
  • Developer-Friendly: Rich error messages with automatic recovery suggestions

Features

  • Safe Transformations: Move, extract, and inline JSX with dependency management
  • Automatic Dependency Analysis: Tracks hooks, variables, imports, props, context, and refs
  • Smart Hoisting: Automatically hoists dependencies to valid scopes following React rules
  • Cross-File Operations: Transform code across multiple files with import/export management
  • Optimization: Sink over-hoisted dependencies back to minimal scopes
  • Validation: Check operations before execution with canMove(), canExtract()
  • Dry-Run Mode: Preview transformations without modifying files
  • Error Recovery: Structured errors with suggested fixes

Installation

npm install regrafter

Requirements:

  • Node.js ≥18
  • TypeScript ≥4.7.0 (optional peer dependency)

Quick Start

Move API

Relocate JSX elements with automatic dependency management:

import { move, Move, isOk } from "regrafter";

const files = [
  {
    path: "App.tsx",
    content: `
      import { useState } from 'react';
      import { Header } from './components/Header';
      import { Counter } from './components/Counter';

      function App() {
        const [count, setCount] = useState(0);
        return (
          <div>
            <Header />
            <Counter value={count} onChange={setCount} />
          </div>
        );
      }
    `,
  },
];

// Move <Counter /> inside <Header />
const result = move(
  files,
  { file: "App.tsx", line: 11, column: 13 }, // from: Counter
  { file: "App.tsx", line: 10, column: 13 }, // to: Header
  Move.Inside
);

if (isOk(result)) {
  console.log("Transformed:", result.value.codes[0].content);
  /* Dependencies (count, setCount) automatically hoisted and threaded

  Output example:

  Transformed:
  import { useState } from 'react';
  import { Header } from './components/Header';
  import { Counter } from './components/Counter';

  function App() {
    const [count, setCount] = useState(0);
    return (
      <div>
        <Header count={count} setCount={setCount}>
          <Counter value={count} onChange={setCount} />
        </Header>
      </div>
    );
  }
  */
}

Move Directions:

Regrafter supports four move directions:

  • Move.Before - Insert element before the target
  • Move.After - Insert element after the target
  • Move.Inside - Insert element as a child of the target (prepends to children by default)
  • Move.Replace - Replace the target with the element

Control insertion position with insertIndex:

When using Move.Inside, you can control where the element is inserted among the target's children:

// Insert at the beginning (default behavior)
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

// Insert at the end (append)
move(files, from, to, Move.Inside, { insertIndex: -1 });

Note: By default, Move.Inside prepends (inserts at the beginning). Use insertIndex: -1 to append at the end.

Extract API

Extract JSX into a reusable component:

import { extract, isOk } from "regrafter";

const files = [
  {
    path: "App.tsx",
    content: `
    function App() {
      const userName = "John";
      const avatar = "/avatar.jpg";

      return (
        <div>
          <div className="profile">
            <img src={avatar} alt={userName} />
            <h2>{userName}</h2>
          </div>
        </div>
      );
    }
  `,
  },
];

const result = extract(
  files,
  { file: "App.tsx", line: 7, column: 11 }, // Select profile div to extract
  { componentName: "UserProfile" }
);

if (isOk(result)) {
  console.log("Created component:", result.value.component);
  console.log("Generated code:", result.value.codes[0].content);
  console.log("Stats:", result.value.stats);
  /* UserProfile component created with inferred props

  Output example:
  Created component: {
    name: 'UserProfile',
    file: 'App.tsx',
    props: [
      { name: 'userName', type: 'string', optional: false },
      { name: 'avatar', type: 'string', optional: false }
    ]
  }

  Generated code:
  function UserProfile({ userName, avatar }: UserProfileProps) {
    return (
      <div className="profile">
        <img src={avatar} alt={userName} />
        <h2>{userName}</h2>
      </div>
    );
  }

  function App() {
    const userName = "John";
    const avatar = "/avatar.jpg";
    return <UserProfile userName={userName} avatar={avatar} />;
  }

  Stats: {
    nodesExtracted: 5,
    dependenciesFound: 3,
    propsGenerated: 2
  }
  */
}

Inline API

Inline a component at its call sites:

import { inline, isOk } from "regrafter";

const files = [
  {
    path: "components.tsx",
    content: `
      export function Button({ onClick, children }) {
        return <button onClick={onClick}>{children}</button>;
      }
    `,
  },
  {
    path: "App.tsx",
    content: `
      import { Button } from './components';

      function App() {
        const handleClick = () => console.log('Submit');
        const handleCancel = () => console.log('Cancel');
        const handleReset = () => console.log('Reset');

        return (
          <div>
            <Button onClick={handleClick}>Submit</Button>
            <Button onClick={handleCancel}>Cancel</Button>
            <Button onClick={handleReset}>Reset</Button>
          </div>
        );
      }
    `,
  },
];

const result = inline(
  files,
  { file: "components.tsx", name: "Button" } // Component to inline
);

if (isOk(result)) {
  console.log("Result:", result.value);
  console.log(`Inlined ${result.value.inlinedCount} call sites`);

  /* Output example:
  Result: {
    codes: [
      {
        file: 'components.tsx',
        content: '// Button component removed',
        changed: true
      },
      {
        file: 'App.tsx',
        content: `
          function App() {
            const handleClick = () => console.log('Submit');
            const handleCancel = () => console.log('Cancel');
            const handleReset = () => console.log('Reset');

            return (
              <div>
                <button onClick={handleClick}>Submit</button>
                <button onClick={handleCancel}>Cancel</button>
                <button onClick={handleReset}>Reset</button>
              </div>
            );
          }
        `,
        changed: true
      }
    ],
    inlinedCount: 3
  }
  Inlined 3 call sites
  */
}

Core APIs

move()

Relocate JSX elements with automatic dependency management.

move(files, from, to, mode, options?): Result<TransformedCode[], RegraffError>

See: Move API Documentation

extract()

Extract JSX into a reusable component with automatic prop inference.

extract(files, selection, componentName, options?): Result<TransformedCode[], RegraffError>

See: Extract API Documentation

inline()

Inline a component at its call sites.

inline(files, component, options?): Result<InlineResult, RegraffError>

See: Inline API Documentation

Validation & Analysis

canMove(files, from, to, mode): boolean
analyze(files, from, to, mode): Result<MoveAnalysis, RegraffError>
canExtract(files, selection): boolean
analyzeExtract(files, selection, name): Result<ExtractAnalysis, RegraffError>

See: Validation APIs

Optimization

optimize(files, options?): Result<TransformedCode[], RegraffError>

See: Optimization API

Key Concepts

Selectors

Select elements by position or AST path:

// Position (line/column) - IDE-friendly
{ file: 'App.tsx', line: 10, column: 5 }

// AST path - Programmatic control
{ file: 'App.tsx', path: 'Program.body[0].declaration' }

Move Modes

enum Move {
  Inside = "inside", // Insert as child
  Before = "before", // Insert before target
  After = "after", // Insert after target
}

Result Pattern

All APIs return Result<T, E> instead of throwing:

import { move, isOk, isErr } from "regrafter";

const result = move(files, from, to, Move.Inside);

if (isOk(result)) {
  console.log("Success:", result.value);
} else {
  console.error("Error:", result.error);
}

See: Error Handling Guide

Dependency Management

Regrafter automatically tracks and resolves six types of dependencies:

  • Hook - React hooks (useState, useEffect, etc.) → Hoist to valid component
  • Variable - Local variables → Hoist or thread as props
  • Import - Module imports → Add/remove imports automatically
  • Prop - Component props → Thread through component tree
  • Context - React context → Hoist or thread values
  • Ref - React refs → Hoist or implement ref forwarding

See: Dependency Types Guide

Documentation

Development

# Install dependencies
npm install

# Run tests (TDD)
npm run test:watch

# Build
npm run build

# Lint & format
npm run lint:fix
npm run format

See: Contributing Guide for development guidelines

Performance

  • Single file (<1000 lines): <100ms
  • Multi-file (10 files): <500ms
  • Memory: <10x source file size

See: Architecture for technical details

License

MIT © wickedev

Links

  • Repository: https://github.com/wickedev/regrafter
  • Issues: https://github.com/wickedev/regrafter/issues
  • npm: https://www.npmjs.com/package/regrafter