Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@react-dev-inspector/middleware

zthxxx49.3kMIT2.0.1TypeScript support: included

express middleware for react-dev-inspector to launch local IDE

readme

React Dev Inspector

NPM Version NPM Downloads Node.js License

Introduction

This package allows users to jump to local IDE code directly from browser React component by just a simple click, which is similar to Chrome inspector but more advanced.

Preview

online demo: https://react-dev-inspector.zthxxx.me

press hotkey (ctrl⌃ + shift⇧ + commmand⌘ + c), then click the HTML element you wish to inspect.

screen record gif (8M size):

inspector-gif

Installation

npm i react-dev-inspector

Usage


for VSCode only, but simple without any other configuration

Works with almost all react frameworks such as Vite, Next.js, Rspack, Umi4 / Umi3, Create React App, Ice.js,

or any other which use @babel/plugin-transform-react-jsx-source in builtin. Just follow the component code below:

import React from 'react'
import { Inspector, InspectParams } from 'react-dev-inspector'

const isDev = process.env.NODE_ENV === 'development'

export const Layout = () => {
  // ...

  return (
    <>
      <YourComponent />

      {isDev && (
        <Inspector
          // props see docs:
          // https://github.com/zthxxx/react-dev-inspector#inspector-component-props
          keys={['control', 'shift', 'command', 'c']}
          disableLaunchEditor={true}
          onClickElement={({ codeInfo }: InspectParams) => {
            if (!codeInfo?.absolutePath) return
            const { absolutePath, lineNumber, columnNumber } = codeInfo

            window.open(`vscode://file/${absolutePath}:${lineNumber}:${columnNumber}`)

            // you can change the url protocol if you are using another IDE, like for WebStorm:
            // window.open(`webstorm://open?file=${absolutePath}&line=${lineNumber}&column=${columnNumber}`)
          }}
        />
      )}
    </>
  )
}

Whether you use vscode://, webstorm://, or another protocol, it's hardcoded in the code.

Sometime you want it infer which is the current local IDE you or your different team member are using now.

Sometimes, you might want it to infer the current local IDE that you or your team members are using.

To generally infer the current local IDE, server-side configuration is required. Follow the TWO steps below:

1. Add Inspector React Component

import React from 'react'
import { Inspector, InspectParams } from 'react-dev-inspector'

const isDev = process.env.NODE_ENV === 'development'

export const Layout = () => {
  // ...

  return (
    <>
      <YourComponent />

      {isDev && (
        <Inspector
          // props see docs:
          // https://github.com/zthxxx/react-dev-inspector#inspector-component-props
          // keys={['control', 'shift', 'command', 'c']}
          // onHoverElement={(inspect: InspectParams) => {}}
          // onClickElement={(inspect: InspectParams) => {}}
        />
      )}
    </>
  )
}

2. Set up Inspector Config

In server side, You need to add:

  • an server middleware, to open local IDE
    • import { launchEditorMiddleware } from 'react-dev-inspector/plugins/webpack'
  • an OPTIONAL inspector babel plugin, to inject source code location info (relative path)
    • react-dev-inspector/plugins/babel

to your current project development config.

Such as add server middleware into your webpack-dev-server config or other server setup, add the babel plugin (optional) into your .babelrc or webpack babel-loader config.

Note: The react-dev-inspector/plugins/babel is optional. Most React frameworks/scaffolds already integrate the @babel/plugin-transform-react-jsx-source or an equivalent in swc to inject the absolute path for source code on React Fiber,

while the react-dev-inspector's plugin refining to inject the relative path on DOM for brevity and clarity to debug.


There are some example ways to set up, please pick the one fit your project best.

In common cases, if you're using webpack, you can see #raw-webpack-config,

If your project happen to use vite / nextjs / create-react-app and so on, you can also try out our integrated plugins / examples with

Raw Webpack Config

Support webpack v4 and v5, examples see:

<summary>webpack.config.ts</summary>
// webpack.config.ts
import type { Configuration } from 'webpack'
import { ReactInspectorPlugin } from 'react-dev-inspector/plugins/webpack'

const config: Configuration = {
  plugins: [
    /**
     * react-dev-inspector webpack plugin
     * this plugin will create
     *  `devServer.setupMiddlewares` config for webpack5
     *   and `devServer.before` config for webpack4
     */
    new ReactInspectorPlugin(),
  ],
}

However, if you want more manually config with webpack-dev-server, here are some equivalent:

<summary>webpack.config.ts (for webpack-dev-server)</summary>
// webpack.config.ts
import type { Configuration } from 'webpack'
import { launchEditorMiddleware } from 'react-dev-inspector/plugins/webpack'

const config: Configuration = {
  devServer: {
    /**
     * react-dev-inspector - dev server config
     * for webpack-dev-server@^4
     */
    setupMiddlewares: (middlewares, devServer) => {
      middlewares.unshift(launchEditorMiddleware)
      return middlewares
    },

    /**
     * react-dev-inspector - dev server config
     * for webpack-dev-server@^3
     */
    before: (app, server, compiler) => {
      app.use(launchEditorMiddleware)

      // ... other middlewares after
    },
  },
}
<summary>.babelrc.js</summary>
// .babelrc.js
module.exports = {
  plugins: [
    /**
     * react-dev-inspector plugin, options docs see:
     * https://github.com/zthxxx/react-dev-inspector#inspector-babel-plugin-options
     */
    'react-dev-inspector/plugins/babel',
  ],
}

Usage with Vite2

compatible with vite@3 / vite@4.

example project see: https://github.com/zthxxx/react-dev-inspector/tree/master/examples/vite2

<summary>vite.config.ts</summary>
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { inspectorServer } from 'react-dev-inspector/plugins/vite'

export default defineConfig({
  plugins: [
    react(),

    /**
     * react-dev-inspector configuration
     * only need setup an inspector middleware
     */
    inspectorServer(),
  ],
})

Usage with Next.js

use Next.js Custom Server + Customizing Babel Config

example project see: https://github.com/zthxxx/react-dev-inspector/tree/master/examples/nextjs

<summary>server.js</summary>
...

const {
  launchEditorMiddleware,
} = require('react-dev-inspector/plugins/webpack')

app.prepare().then(() => {
  createServer((req, res) => {
    /**
     * middlewares, from top to bottom
     */
    const middlewares = [
      /**
       * react-dev-inspector configuration, two middlewares for nextjs
       */
      launchEditorMiddleware,

      /** Next.js default app handle */
        (req, res) => handle(req, res),
    ]

    const middlewarePipeline = middlewares.reduceRight(
      (next, middleware) => (
        () => { middleware(req, res, next) }
      ),
      () => {},
    )

    middlewarePipeline()

  }).listen(PORT, (err) => {
    if (err) throw err
    console.debug(`> Ready on http://localhost:${PORT}`)
  })
})
<summary>package.json</summary>
  "scripts": {
-    "dev": "next dev",
+    "dev": "node server.js",
    "build": "next build"
  }
<summary>.babelrc.js</summary>
module.exports = {
  plugins: [
    /**
     * react-dev-inspector plugin, options docs see:
     * https://github.com/zthxxx/react-dev-inspector#inspector-babel-plugin-options
     */
    'react-dev-inspector/plugins/babel',
  ],
}

Usage with Rspack

<summary>rspack.config.ts</summary>
import { defineConfig } from '@rspack/cli'
import { launchEditorMiddleware } from 'react-dev-inspector/plugins/webpack'

export default defineConfig({
  devServer: {
    /**
     * react-dev-inspector server config for rspack
     */
    setupMiddlewares(middlewares) {
      middlewares.unshift(launchEditorMiddleware)
      return middlewares
    },
  },
})

Usage with create-react-app

create-react-app + react-app-rewired + customize-cra example config-overrides.js:

example project see: https://github.com/zthxxx/react-dev-inspector/tree/master/examples/cra

Support create-react-app v4, v5, example config see:

<summary>config-overrides.js</summary>
const {
  launchEditorMiddleware,
  ReactInspectorPlugin,
} = require('react-dev-inspector/plugins/webpack')
const {
  override,
  overrideDevServer,
  addBabelPlugin,
} = require('customize-cra')


/**
 * origin config:
 *   https://github.com/facebook/create-react-app/blob/v5.0.1/packages/react-scripts/config/webpack.config.js
 *   https://github.com/facebook/create-react-app/blob/v5.0.1/packages/react-scripts/config/webpackDevServer.config.js
 *
 * customize-cra api code: https://github.com/arackaf/customize-cra
 */
module.exports = {
  webpack: override(
    /** react-dev-inspector - babel config */
    addBabelPlugin([
      // plugin options docs see:
      // https://github.com/zthxxx/react-dev-inspector#inspector-babel-plugin-options
      'react-dev-inspector/plugins/babel',
      {
        excludes: [
          /xxxx-want-to-ignore/,
        ],
      },
    ]),

    /**
     * react-dev-inspector - dev server config
     * for create-react-app@^4 + webpack-dev-server@^3
     */
    addWebpackPlugin(
      new ReactInspectorPlugin(),
    ),
  ),

  /**
   * react-dev-inspector - dev server config
   * for create-react-app@^5 + webpack-dev-server@^4.7
   */
  devServer: overrideDevServer(
    serverConfig => {
      // https://webpack.js.org/configuration/dev-server/#devserversetupmiddlewares
      serverConfig.setupMiddlewares = (middlewares) => {
        middlewares.unshift(launchEditorMiddleware)
        return middlewares
      }

      return serverConfig
    },
  ),
}

Usage with Umi3

example project see: https://github.com/zthxxx/react-dev-inspector/tree/master/examples/umi3

<summary>.umirc.dev.ts</summary>
// https://umijs.org/config/
import { defineConfig } from 'umi'

export default defineConfig({
  plugins: [
    'react-dev-inspector/plugins/umi/react-inspector',
  ],
  inspectorConfig: {
    // babel plugin options docs see:
    // https://github.com/zthxxx/react-dev-inspector#inspector-babel-plugin-options
    excludes: [],
  },
})

Usage with Umi2

<summary>.umirc.dev.ts</summary>
import { launchEditorMiddleware } from 'react-dev-inspector/plugins/webpack'

export default {
  // ...
  extraBabelPlugins: [
    // plugin options docs see:
    // https://github.com/zthxxx/react-dev-inspector#inspector-babel-plugin-options
    'react-dev-inspector/plugins/babel',
  ],

  /**
   * And you need to set `false` to `dll` in `umi-plugin-react`,
   * becase these is a umi2 bug that `dll` cannot work with `devServer.before`
   *
   * https://github.com/umijs/umi/issues/2599
   * https://github.com/umijs/umi/issues/2161
   */
  chainWebpack(config, { webpack }) {
    const originBefore = config.toConfig().devServer

    config.devServer.before((app, server, compiler) => {

      app.use(launchEditorMiddleware)

      originBefore?.before?.(app, server, compiler)
    })

    return config
  },
}

Usage with Ice.js

<summary>build.json</summary>
// https://ice.work/docs/guide/basic/build
{
  "plugins": [
    "react-dev-inspector/plugins/ice",
  ]
}

Examples Project Code


Configuration

<Inspector> Component Props

checkout TS definition under react-dev-inspector/es/Inspector.d.ts.

Property Description Type Default
keys inspector hotkeys

supported keys see: https://github.com/jaywcjlove/hotkeys#supported-keys
string[] ['control', 'shift', 'command', 'c']
disableLaunchEditor disable editor launching

(launch by default in dev Mode, but not in production mode)
boolean false
onHoverElement triggered when mouse hover in inspector mode (params: InspectParams) => void -
onClickElement triggered when mouse hover in inspector mode (params: InspectParams) => void -
// import type { InspectParams } from 'react-dev-inspector'

interface InspectParams {
  /** hover / click event target dom element */
  element: HTMLElement,
  /** nearest named react component fiber for dom element */
  fiber?: React.Fiber,
  /** source file line / column / path info for react component */
  codeInfo?: {
    lineNumber: string,
    columnNumber: string,
    /**
    * code source file relative path to dev-server cwd(current working directory)
    * need use with `react-dev-inspector/plugins/babel`
    */
    relativePath?: string,
    /**
    * code source file absolute path
    * just need use with `@babel/plugin-transform-react-jsx-source` which auto set by most framework
    */
    absolutePath?: string,
  },
  /** react component name for dom element */
  name?: string,
}

Inspector Babel Plugin Options

interface InspectorPluginOptions {
  /** override process.cwd() */
  cwd?: string,
  /** patterns to exclude matched files */
  excludes?: (string | RegExp)[],
}

Inspector Loader Props

// import type { ParserPlugin, ParserOptions } from '@babel/parser'
// import type { InspectorConfig } from 'react-dev-inspector/plugins/webpack'

interface InspectorConfig {
  /** patterns to exclude matched files */
  excludes?: (string | RegExp)[],
  /**
   * add extra plugins for babel parser
   * default is ['typescript', 'jsx', 'decorators-legacy', 'classProperties']
   */
  babelPlugins?: ParserPlugin[],
  /** extra babel parser options */
  babelOptions?: ParserOptions,
}

IDE / Editor config

This package uses react-dev-utils to launch your local IDE application, but, which one will be open?

In fact, it uses an environment variable named REACT_EDITOR to specify an IDE application, but if you do not set this variable, it will try to open a common IDE that you have open or installed once it is certified.

For example, if you want it always open VSCode when inspection clicked, set export REACT_EDITOR=code in your shell config like .bashrc or .zshrc, don't forget restart shell or IDE to reload the updated environment variable.


VSCode

  • install VSCode command line tools, follow the official docs, you need to run the command: >Shell Command: Install 'code' command in PATH install-vscode-cli

  • set env to shell, like .bashrc or .zshrc

    export REACT_EDITOR=code

WebStorm

  • just set env with an absolute path to shell, like .bashrc or .zshrc (only MacOS)
    export REACT_EDITOR='/Applications/WebStorm.app/Contents/MacOS/webstorm'

OR

  • install WebStorm command line tools install-webstorm-cli

  • then set env to shell, like .bashrc or .zshrc

    export REACT_EDITOR=webstorm

Vim

Yes! you can also use vim if you want, just set env to shell

export REACT_EDITOR=vim

How It Works

  • Stage 1 - Compile Time

    • [babel plugin] inject source file path/line/column to JSX data attributes props
  • Stage 2 - Web React Runtime

    • [React component] Inspector Component in react, for listen hotkeys, and request api to dev-server for open IDE.

      Specific, when you click a component DOM, the Inspector will try to obtain its source file info (path/line/column), then request launch-editor api (in stage 3) with absolute file path.

  • Stage 3 - Dev-server Side

    • [middleware] setup launchEditorMiddleware in webpack dev-server (or other dev-server), to open file in IDE according to the request params.

      Only need in development mode,and you want to open IDE when click a component element.

      Not need in prod mode, or you just want inspect dom without open IDE (set disableLaunchEditor={true} to Inspector component props)

Analysis of Theory


License

MIT LICENSE

changelog

Changelog

1.9.0 (2023-08-14)

feat

  • compatible with the incorrect fileName: "</xxx/file>" by rspack.
  • integrate queryParserMiddleware into the launchEditorMiddleware, then mark queryParserMiddleware as deprecated.

docs

  • add explain for optional babel plugin
  • add example config for rspack
  • add example for webstorm url protocol

fix

  • fix incorrect resolve path in umi4 plugin

1.8.6 (2023-07-06)

feat

  • try to compatible umi4 with addBeforeBabelPlugins api

fix

  • fix Inspector component with non-children to render

1.8.4 (2022-12-09)

revert

  • revert v1.8.3 / v1.8.2 due to umi3/umi4 build TypeError

1.8.3 Deprecated (2022-12-08)

feat

  • compatible with umi3/umi4 addBeforeMiddlewares/addBeforeMiddewares plugin api (99ff088)

1.8.2 Deprecated (2022-10-17)

feat

  • compatible with umi4 plugin api (8a504d8)

1.8.1 (2021-06-15)

fix

  • @babel/types should be listed in dependencies instead of devDependencies, close #127 (4aa7072)

docs

  • add docs for VSCode only, simple without any other configuration (884d576)

chore

  • update all non-major dependencies

1.8.0 (2021-04-18)

feat

  • update ReactInspectorPlugin for compatible with webpack v5 and v4 (914b12c)

docs

  • update docs for use with create-react-app v5, webpack v5 and webpack-dev-server v4 (914b12c)

chore

  • upgrade create-react-app to v5 and update example project config file (914b12c)
  • upgrade vite and @vitejs/plugin-react, update example project config file (914b12c)

1.7.1 (2021-11-02)

feat

  • inspect element immediately at mouse point while press hotkey (06b57fb)

1.7.0 (2021-08-16)

feat

  • support for Next.js, add example project and docs (c7f52ba), close #98

chore

  • upgrade many dependencies version by renovate bot
  • enhance ts types for tsc build (b8b8375)
  • refactor build scripts, add commonjs output for ssr like Next.js (e2d4d8d)
  • rename sites to examples (407ff76)
  • update config of jest / npm / ts (a40f9b8)
  • upgrade lockFileVersion to v2 (a45873b)

docs

  • add docs for changelog (fe1206b), close #25
  • update comments of inspect reference fiber rule (fb74698)

1.6.0 (2021-04-27)

Features

  • support ice.js and docs (6ea0776)

1.5.3 (2021-03-23)

Bug Fixes

  • fix example sites package-lock (0540581)

Features

  • sideEffects false for tree-shaking (#29) (8b54095)
  • add module field for better support of tree-shaking, close #30

1.5.1 (2021-03-13)

Bug Fixes

Features

  • support vite, add vite demo and docs (f5c120e)

1.4.0 (2020-03-06)

Features

  • add inspector babel-plugin, add tests, adjust api docs (7c4c9c9)

1.3.6 (2020-03-07)

Refactor

  • optimize inspect interactive experience (7aca0c28) relate to (#20)

1.3.1 (2021-03-05)

Bug Fixes

  • fix postinstall error (390c302)
  • fix inspector source dir with monorepo umi3 (dec5713)
  • fix lerna correct lint in bootstrap (46c1422)

Features

  • do not need to config or use pwd in react runtime, v1.3.0 (e85a030), close #21
  • add create-react-app example project, adjust umi example code (e8ccaeb)
  • add feature of jumping to the place of reference more humanized (#15) (#20) (16d9aca)

1.1.4 (2020-12-15)

Bug Fixes

  • fix loader relative path in monorepo like lerna, close #12 #9 (c9d3c1e)

  • compatible with internalInstanceKey in react v16.14, close #10 (87d58bc)

  • fix npm-run-all bin file in lock file (dc097a6)
  • fix package-lock file with npm7 (b3d39e1)

1.1.1 (2020-12-07)

First public release version, initialized project.

Features

  • add webpack plugin to inject launch IDE middleware in devServer (ab205ab)
  • improve getFiberName (6ea2d37)
  • add react-dev-inspector demo website page (6f6e546)
  • add more inspector-config (44b260f)
  • add element handler callback, refactor inspector plugins (77c5ba7)