Frontend Development 24 min read

code‑inspector‑plugin: A Frontend Development Tool for Fast Source‑Code Location and IDE Integration

The article introduces code‑inspector‑plugin, an open‑source plugin for bundlers like webpack, Vite, and Rspack that lets developers click a DOM element in the browser to instantly open the corresponding source file in their IDE, detailing installation, configuration, usage, implementation principles, and common troubleshooting tips.

大转转FE
大转转FE
大转转FE
code‑inspector‑plugin: A Frontend Development Tool for Fast Source‑Code Location and IDE Integration

Tool Introduction

code‑inspector‑plugin is an open‑source plugin for bundlers such as webpack, Vite, Rspack, Next.js, Nuxt, and UmiJS that improves development efficiency; clicking a DOM element on the page automatically opens the IDE and positions the cursor at the corresponding source code location.

Advantages

Development Efficiency

Simply click a DOM element, and the IDE opens at the exact source line, multiplying debugging speed.

Ease of Use

No source‑code intrusion is required—just add the plugin to the build tool and it works out of the box.

Strong Compatibility

Full Build‑Tool Support : webpack, Vite, Rspack, Rsbuild, esbuild, Farm, Next.js, Nuxt, UmiJS and others.

Multiple Frameworks : Vue, React, Preact, Solid, Qwik, Svelte, Astro, etc.

IDE Recognition : VS Code, WebStorm, Atom, HBuilderX, PhpStorm, PyCharm, IntelliJ IDEA.

Environment Detection : Active only in development mode, leaving production builds untouched.

Usage

Installation

npm install code-inspector-plugin -D

yarn add code-inspector-plugin -D

pnpm add code-inspector-plugin -D

Configuration (Webpack example)

const { codeInspectorPlugin } = require('code-inspector-plugin');

module.exports = () => ({
  plugins: [
    codeInspectorPlugin({
      bundler: 'webpack',
    }),
  ],
});

Usage

Two ways to trigger source locating:

Method 1 (Recommended)

Hold the shortcut key (Mac default Option + Shift , Windows default Alt + Shift ) and hover over a DOM element; the element is highlighted and a tooltip appears. Clicking the element opens the IDE at the source location.

Hovering highlights the element with an overlay.

Clicking opens the IDE and jumps to the exact line.

Method 2

Enable the code inspection switch button (set showSwitch: true in the plugin options). The switch toggles between a colored mode (inspection active) and a black‑white mode (inactive).

For full details, see the official guide: https://inspector.fe-dev.cn/guide/start.html

Implementation Principle

Implementation Idea

Participate in source compilation: the plugin injects itself into the bundler pipeline and performs AST analysis on Vue or JSX files to extract DOM‑to‑source mappings (file path, line, column) and adds them as extra attributes.

Frontend interaction logic: after compilation, the plugin embeds a small client script that listens for click events, reads the injected attributes, and sends an HTTP request to a local server.

Backend service handling: a Node.js server receives the request, parses the file, line, and column parameters, and launches the appropriate IDE.

Automatic IDE opening: the server uses spawn or exec to run commands such as code -g file:line:column , or a custom IDE path if configured.

Source Code Analysis

Entry Point

Based on the bundler option, the plugin loads the corresponding sub‑plugin (Vite, Webpack, or Esbuild) and respects .env.local and the CODE_INSPECTOR environment variable to decide whether to activate.

import { ViteCodeInspectorPlugin } from 'vite-code-inspector-plugin';
import WebpackCodeInspectorPlugin from 'webpack-code-inspector-plugin';
import { EsbuildCodeInspectorPlugin } from 'esbuild-code-inspector-plugin';

export function CodeInspectorPlugin(options) {
  if (!options?.bundler) {
    console.log(chalk.red('Please specify the bundler in the options of code-inspector-plugin.'));
    return;
  }
  // ...
  if (options.bundler === 'webpack' || options.bundler === 'rspack') {
    return new WebpackCodeInspectorPlugin(params);
  } else if (options.bundler === 'esbuild') {
    return EsbuildCodeInspectorPlugin(params);
  } else {
    return ViteCodeInspectorPlugin(params);
  }
}

export const codeInspectorPlugin = CodeInspectorPlugin;

Webpack Loader Logic

The plugin adds a custom loader.js and inject-loader.js to the Webpack configuration. applyLoader injects these loaders, and WebpackCodeInspectorPlugin registers them only in development mode.

const applyLoader = (options, compiler) => {
  if (!isFirstLoad) return;
  isFirstLoad = false;
  const _compiler = compiler?.compiler || compiler;
  const module = _compiler?.options?.module;
  const rules = module?.rules || module?.loaders || [];
  rules.push({
    test: options?.match ?? /\.(vue|jsx|tsx|js|ts|mjs|mts)$/,
    exclude: /node_modules/,
    use: [{ loader: path.resolve(compatibleDirname, './loader.js'), options }],
    ...(options.enforcePre === false ? {} : { enforce: 'pre' }),
  }, {
    ...(options?.injectTo ? { resource: options?.injectTo } : {
      test: /\.(jsx|tsx|js|ts|mjs|mts)$/,
      exclude: /node_modules/,
    }),
    use: [{ loader: path.resolve(compatibleDirname, './inject-loader.js'), options }],
    enforce: 'post',
  });
};

class WebpackCodeInspectorPlugin {
  constructor(options) { this.options = options; }
  apply(compiler) {
    let isDev;
    if (typeof this.options?.dev === 'function') {
      isDev = this.options?.dev();
    } else {
      isDev = this.options?.dev;
    }
    if (isDev === false) return;
    if (!isDev && compiler?.options?.mode !== 'development' && process.env.NODE_ENV !== 'development') return;
    applyLoader({ ...this.options, record }, compiler);
  }
}
export default WebpackCodeInspectorPlugin;

First Loader (transformer)

The loader examines file types (Vue, JSX, Svelte) and calls transformCode , which delegates to transformVue , transformJsx , or transformSvelte based on fileType . The transformation injects a data‑insp‑path attribute containing filepath:line:column:tag .

export async function WebpackCodeInspectorLoader(content) {
  const isJSX = isJsTypeFile(filePath) || (filePath.endsWith('.vue') && jsxParamList.some(p => params.get(p) !== null));
  if (isJSX) return transformCode({ content, filePath, fileType: 'jsx', escapeTags });
  // Vue handling
  const isVue = filePath.endsWith('.vue') && params.get('type') !== 'style' && params.get('type') !== 'script' && params.get('raw') === null;
  if (isVue) return transformCode({ content, filePath, fileType: 'vue', escapeTags });
  // Svelte handling
  const isSvelte = filePath.endsWith('.svelte');
  if (isSvelte) return transformCode({ content, filePath, fileType: 'svelte', escapeTags });
  return content;
}

Second Loader (inject‑loader)

This loader starts the local Node server and injects the client web component into the entry file, so the component runs automatically in the browser.

export default async function WebpackCodeInjectLoader(content, source, meta) {
  this.async();
  this.cacheable && this.cacheable(true);
  const filePath = normalizePath(this.resourcePath);
  const options = this.query;
  content = await getCodeWithWebComponent(options, filePath, content, options.record);
  this.callback(null, content, source, meta);
}

Frontend Interaction Logic

The custom code‑inspector‑component built with LitElement listens for the configured hotkeys, shows an overlay on hover, extracts the data‑insp‑path attribute, and sends a request (XHR or image beacon) to the local server to open the IDE. It also provides a draggable switch button to enable/disable the feature.

class CodeInspectorComponent extends LitElement {
  @property() hotKeys = 'shiftKey,altKey';
  @property() port = DefaultPort;
  // ...
  isTracking = e => this.hotKeys && this.hotKeys.split(',').every(key => e[key.trim()]);
  renderCover = target => {
    const { line, column, path, name } = this.parsePath(target.getAttribute(PathName));
    this.element = { name, path, line, column };
    this.show = true;
  };
  sendXHR = () => {
    const file = encodeURIComponent(this.element.path);
    const url = `http://${this.ip}:${this.port}/?file=${file}&line=${this.element.line}&column=${this.element.column}`;
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.send();
    xhr.addEventListener('error', () => { this.sendImg(); });
  };
  // ...
}

Backend Service Processing Requests

A lightweight HTTP server created with Node's http module listens on a dynamically allocated port (using portfinder ). When a request arrives, it parses file , line , and column query parameters and launches the configured editor (defaulting to VS Code) at the specified location.

export function createServer(callback, options) {
  const server = http.createServer((req, res) => {
    const params = new URLSearchParams(req.url.slice(1));
    const file = decodeURIComponent(params.get('file'));
    const line = Number(params.get('line'));
    const column = Number(params.get('column'));
    res.writeHead(200, {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': '*',
      'Access-Control-Allow-Headers': '*',
      'Access-Control-Allow-Private-Network': 'true',
    });
    res.end('ok');
    options?.hooks?.afterInspectRequest?.(options, { file, line, column });
    launchEditor(file, line, column, options?.editor, options?.openIn, options?.pathFormat);
  });
  portFinder.getPort({ port: DefaultPort }, (err, port) => {
    if (err) throw err;
    server.listen(port, () => callback(port));
  });
}

export async function startServer(options, record) {
  if (!record.port) {
    if (!record.findPort) {
      record.findPort = new Promise(resolve => {
        createServer(port => resolve(port), options);
      });
    }
    record.port = await record.findPort;
  }
}

Automatic IDE Opening

The server uses spawn or exec to run commands like code -g file:line:column . If a custom editor path is provided, it runs {IDE_PATH} -g {file}:{line}:{column} . The implementation also detects running IDE processes to choose the most appropriate editor, preferring VS Code and WebStorm for web projects.

Common Issues and Solutions

IDE does not open after clicking: ensure the code command is added to the system PATH (e.g., via VS Code's "Shell Command: Install 'code' command in PATH").

GlobalThis is not defined on low‑end Android devices: upgrade to the latest version of the plugin where the author added compatibility fixes.

ESLint errors caused by enforcePre: true : set enforcePre: false in the plugin options.

Poor experience on mobile when using the switch: the latest plugin version improves mobile interaction, allowing long‑press to show the overlay and tap to open the IDE, with draggable switch positioning.

References

Official guide: https://inspector.fe-dev.cn/guide/start.html

Original article on Juejin: https://juejin.cn/post/7326002010084311079

GitHub repository: https://github.com/zh-lx/code-inspector

IDE IntegrationWebpackvitefrontend debuggingcode-inspectorsource mapping
大转转FE
Written by

大转转FE

Regularly sharing the team's thoughts and insights on frontend development

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.