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

react-easy-debug-source

v0.1.1

Published

AI-native debugging bridge for React - click any UI element to generate precise code location + runtime state context for AI assistants

Readme

react-easy-debug-source

AI-native debugging bridge for React -- click any UI element, get precise source location + component name for AI assistants.

React 的 AI 原生调试桥接库 -- 点击任意 UI 元素,获取精确的源码位置 + 组件名,供 AI 助手使用。

Features

  • Babel Plugin (compile-time) -- injects data-ai-source and data-ai-component attributes into all JSX elements
  • Runtime Inspector (browser) -- keyboard shortcut triggers inspect mode, hover to highlight, click to copy source location
  • Two-Tier Lookup -- DOM attribute walk + React Fiber tree fallback, works with MUI / Ant Design / any component library
  • Zero production overhead -- Babel plugin returns empty visitor in production mode
  • ESM + CJS -- dual format output, works with any bundler

Install

# pnpm (recommended)
pnpm add react-easy-debug-source

# npm
npm install react-easy-debug-source

# yarn
yarn add react-easy-debug-source

Peer dependencies: react >= 16.8.0, react-dom >= 16.8.0

Quick Start

Step 1: Configure the Babel Plugin

Add the Babel plugin to your vite.config.ts:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { fileURLToPath } from 'url'
import { dirname, resolve } from 'path'

const __dirname = dirname(fileURLToPath(import.meta.url))

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          [
            'react-easy-debug-source/babel-plugin',
            {
              // Option 1 (recommended): project name prefix + monorepo root
              projectName: 'my-app',
              root: resolve(__dirname, '../..'),

              // Option 2: use absolute paths
              // useAbsolutePath: true,
            },
          ],
        ],
      },
    }),
  ],
})

Babel Plugin Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | root | string | state.cwd \|\| process.cwd() | Root directory for computing relative file paths | | projectName | string | -- | Path prefix, e.g. 'example' produces example:src/App.tsx:10:20 | | useAbsolutePath | boolean | false | Use absolute paths instead of relative paths |

Injected Attributes

The plugin injects two attributes on all JSX elements (native HTML + React components like MUI):

  • data-ai-source="[projectName:]path/to/file.tsx:startLine:endLine" -- source file and line range
  • data-ai-component="ComponentName" -- enclosing React component name

Skip rules:

  • Production mode (NODE_ENV === 'production') -- no injection
  • <Fragment> -- skipped
  • JSXMemberExpression (e.g. <React.Fragment>) -- skipped
  • Elements already having data-ai-source -- no duplicate injection

Step 2: Initialize the Runtime Inspector

Simple Approach (in main.tsx)

// main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'

// Initialize only in development mode
if (import.meta.env.DEV) {
  import('react-easy-debug-source').then(({ initAIDebug }) => {
    initAIDebug({
      shortcut: 'Ctrl+Shift+I',  // optional, default 'Alt+Shift+D'
      enabled: true,              // optional, default true
    })
  })
}

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

Advanced Approach: boot.ts Pattern (recommended)

For full Fiber-based fallback support (Tier 2 lookup), installDevToolsHook() must be called before React loads. Use a separate boot file as the entry point:

// src/boot.ts -- application entry point
// IMPORTANT: this file must NOT contain any static React imports

if (import.meta.env.DEV) {
  import('react-easy-debug-source').then(async ({ initAIDebug, installDevToolsHook }) => {
    // Phase 1: install DevTools hook before React loads
    await installDevToolsHook();

    // Phase 2: initialize the inspector
    initAIDebug({
      shortcut: 'Ctrl+Shift+I',
    })
  })
}

// React and app code load after hook installation
import('./main')
// src/main.tsx -- React entry (loaded dynamically by boot.ts)
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

Update index.html to load boot.ts instead of main.tsx:

<!-- index.html -->
<script type="module" src="/src/boot.ts"></script>

Why the boot.ts pattern? installDevToolsHook() patches window.__REACT_DEVTOOLS_GLOBAL_HOOK__ so React registers itself when it loads. This enables the Tier 2 Fiber-based lookup for elements that don't have data-ai-* DOM attributes (e.g. MUI internal sub-elements, Portals). If you skip this step, Tier 1 DOM attribute lookup still works for most elements.

pnpm note: In a pnpm workspace, react-devtools-inline (a dependency of react-easy-debug-source) has a peer dependency on react. Due to pnpm strict isolation, you may need to add react-devtools-inline as a direct dependency of your app, or configure .npmrc with public-hoist-pattern[]=react.

Step 3: Use the Inspector

  1. Press the keyboard shortcut (default Alt+Shift+D) to enter inspect mode
  2. Hover over elements -- highlighted with a blue semi-transparent box, label shows ComponentName -> <tagName> WxH
  3. Click an element -- source info is copied to clipboard in format ComponentName - path/to/file.tsx:startLine:endLine
  4. Press ESC to exit inspect mode (without copying)

API Reference

Exports from react-easy-debug-source

import { initAIDebug, installDevToolsHook } from 'react-easy-debug-source'
import type { AIDebugOptions, InspectModeOptions } from 'react-easy-debug-source'

initAIDebug(options?: AIDebugOptions): () => void

Initialize the inspector. Waits for DOM ready, creates an InspectMode instance, and mounts it to window.__inspectMode.

interface AIDebugOptions {
  /** Keyboard shortcut, default 'Alt+Shift+D' */
  shortcut?: string
  /** Enable inspector, default true */
  enabled?: boolean
}

Returns: cleanup function -- call it to destroy the inspector and remove all event listeners.

installDevToolsHook(): Promise<void>

Install React DevTools Hook. Must be called before React loads. Internally calls react-devtools-inline/backend's initialize(window).

Note: The current Tier 2 lookup uses direct Fiber access (__reactFiber$), so this function is optional. It is retained as a public API for advanced use cases.

InspectMode class (via window.__inspectMode)

class InspectMode {
  toggle(): void      // Toggle inspect mode on/off
  start(): void       // Enter inspect mode
  stop(): void        // Exit inspect mode
  isActive(): boolean // Whether inspect mode is active
  destroy(): void     // Destroy instance, remove all event listeners
}

Export from react-easy-debug-source/babel-plugin

Default export is a Babel plugin. Used as a plugin specifier string in build tool configuration (see Step 1).

Keyboard Shortcut Format

Connect modifier keys and a regular key with +, case-insensitive:

'Alt+Shift+D'       -- Alt + Shift + D (default)
'Ctrl+Shift+I'      -- Ctrl + Shift + I
'Meta+K'            -- Cmd + K (macOS)
'Ctrl+Alt+Shift+X'  -- Ctrl + Alt + Shift + X

Supported modifiers: Alt, Shift, Ctrl/Control, Meta/Cmd

Advanced Usage

Monorepo Setup

In a monorepo, configure the Babel plugin in each React app with a unique projectName:

// packages/app-a/vite.config.ts
['react-easy-debug-source/babel-plugin', {
  projectName: 'app-a',
  root: resolve(__dirname, '../..'),
}]

// packages/app-b/vite.config.ts
['react-easy-debug-source/babel-plugin', {
  projectName: 'app-b',
  root: resolve(__dirname, '../..'),
}]

Generated attributes:

  • data-ai-source="app-a:packages/app-a/src/App.tsx:10:20"
  • data-ai-source="app-b:packages/app-b/src/Login.tsx:5:15"

Programmatic Toggle

// Toggle via button (no keyboard shortcut needed)
button.addEventListener('click', () => {
  window.__inspectMode?.toggle()
})

// Or in React
<button onClick={() => (window as any).__inspectMode?.toggle()}>
  Toggle Inspect Mode
</button>

You can also toggle from the browser console:

window.__inspectMode.toggle()

Working with Component Libraries (MUI / Ant Design / etc.)

The Babel plugin injects attributes on all JSX elements, including component library usage:

// Your code
<Box sx={{ display: 'flex' }}>
  <Typography>Hello</Typography>
</Box>

// After Babel compilation
<Box data-ai-source="app:src/App.tsx:10:12" data-ai-component="MyComp" sx={{ display: 'flex' }}>
  <Typography data-ai-source="app:src/App.tsx:11:11" data-ai-component="MyComp">Hello</Typography>
</Box>

Most MUI components forward data-* attributes to the DOM, so Tier 1 lookup works directly. For MUI internal sub-elements (e.g. MuiTabs-indicator, MuiTabs-scroller) where DOM attributes are not reachable, Tier 2 walks the React Fiber tree to find the nearest ancestor with data-ai-source.

Working with React Compiler

This plugin works alongside babel-plugin-react-compiler:

babel: {
  plugins: [
    ['react-easy-debug-source/babel-plugin', { projectName: 'example' }],
    ['babel-plugin-react-compiler'],
  ],
}

How It Works

Compile time:                           Runtime:
<Box sx={...}>        Babel Plugin      <div data-ai-source="example:src/App.tsx:59:98"
  ...               ──────────>              data-ai-component="AppContent"
</Box>                                       class="MuiBox-root">
                                        
                                      Inspector click -> copy "AppContent - example:src/App.tsx:59:98"

Two-Tier Lookup

When the user clicks/hovers a DOM element:

  1. Tier 1 (DOM walk): Walk up the DOM tree looking for data-ai-source attribute. Works for native HTML elements and components that forward data-* attributes.
  2. Tier 2 (Fiber walk, fallback): Access the React Fiber tree via __reactFiber$ on the DOM element, walk fiber.return chain and fiber._debugOwner chain looking for memoizedProps['data-ai-source']. Works for component library internals, Portals, and elements where DOM attributes are unreachable.

Component Name Resolution (Babel Plugin)

The plugin walks up the AST to find the enclosing component:

  1. FunctionDeclaration -- function MyComponent() {}
  2. ArrowFunctionExpression / FunctionExpression assigned to VariableDeclarator -- const MyComponent = () => {}
  3. ClassDeclaration -- class MyComponent extends React.Component {}
  4. ClassMethod -- walks up to the parent ClassDeclaration
  5. Fallback: derives name from the filename (strips extension)

Project Structure

packages/react-easy-debug-source/
├── src/
│   ├── index.ts                    # Entry: initAIDebug, installDevToolsHook
│   ├── inspector.ts                # InspectMode class: UI + two-tier lookup
│   ├── babel-plugin.ts             # Babel plugin: inject data-ai-* attributes
│   ├── react-devtools-inline.d.ts  # Type declaration for react-devtools-inline
│   └── babel-helper-plugin-utils.d.ts  # Type declaration for @babel/helper-plugin-utils
├── dist/                           # Build output (tsup)
│   ├── index.mjs / index.cjs      # Runtime (ESM / CJS)
│   ├── index.d.ts / index.d.cts   # Type declarations
│   ├── babel-plugin.mjs / .cjs    # Babel plugin (ESM / CJS)
│   └── babel-plugin.d.ts / .d.cts # Babel plugin type declarations
├── tsup.config.ts                  # Build config (dual entry)
├── tsconfig.json                   # TypeScript config
└── package.json                    # Package config

Build

# Build the library
pnpm --filter react-easy-debug-source build

# Watch mode
pnpm --filter react-easy-debug-source dev

Troubleshooting

Keyboard shortcut not working?

Some OS / browser combinations reserve certain shortcuts. Try a different combination:

initAIDebug({
  shortcut: 'Ctrl+Shift+I',  // recommended
})

Suggested alternatives: Ctrl+Shift+I, Ctrl+Shift+K, Ctrl+Shift+E, F9, Alt+Shift+K

Avoid: Alt+Shift+D or Alt+K -- may conflict with system shortcuts on some keyboard layouts.

Manual toggle from console

window.__inspectMode.toggle()

pnpm strict isolation issues

If react-devtools-inline fails to resolve react in a pnpm workspace, add to your root .npmrc:

public-hoist-pattern[]=react
public-hoist-pattern[]=react-dom

Or add react-devtools-inline as a direct dependency of your app package.

License

MIT