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.
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 -DConfiguration (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
大转转FE
Regularly sharing the team's thoughts and insights on frontend development
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.