点击DOM元素跳转源码位置插件

# 图例

  • 开发环境下,按住command键,点击元素,自动打开 vscode,并跳转到该 dom 对应的源码位置
An image

An image

# 插件功能

  • 在多人协作的项目里,如果有改 bug、改他人代码、改不熟悉的功能代码的场景,都能直接从浏览器快速定位到 VSCode 中的代码行,极大地提高了开发效率

# 实现思路

  • 生成fileMap.json文件,为项目中的所有文件创建了一个编号映射,并且存到这个文件中

    • 写一个方法getAllFiles,递归遍历项目目录,获取这个目录下所有文件的完整路径

    • 写一个方法generateFileMap,使用getAllFiles方法,为 src 下所有的文件生成一个映射 Map 并且保存到本地fileMap.json文件里

  • 实现插件vite-plugin-open-in-vscode

    • 初始化的时候,在configResolved钩子里面执行,当 Vite 项目配置解析完成后,如果当前环境是开发环境,插件会调用generateFileMap生成fileMap.json文件。确保了在开发过程中,fileMap.json始终是最新的,包含了项目中所有文件的映射。

      • 需要注意的是,如果在一次开发过程中,如果新建了很多文件,需要重新yarn dev,才能生成这些新建文件的映射
    • 集成一个express服务,这个服务用来提供一个接口http://localhost:6003/vscode/openfile,这个接口的主要作用是根据接口的参数filePathlineNumber,通过exec命令调用 vscode,从而打开指定文件filePath定位到指定行lineNumber

      • 这个接口会在开发环境下,通过开发者的特定操作(按住command的时候鼠标左边点击 dom 元素),获取该 dom 元素的data-sfdata-sl属性,触发请求vscode/openfile,其中 dom 元素的data-sfdata-sl属性就是接口的请求参数filePathlineNumber

# 实现代码

点击 展开/收起 jsx 代码
import express from 'express';
import { exec } from 'child_process';
import fs, { readFileSync } from 'fs';
import path from 'path';
import { join } from 'path';

// 获取所有文件路径的函数
function getAllFiles(dirPath, arrayOfFiles = []) {
  const files = fs.readdirSync(dirPath);

  files.forEach((file) => {
    if (fs.statSync(path.join(dirPath, file)).isDirectory()) {
      arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);
    } else {
      arrayOfFiles.push(path.join(dirPath, file));
    }
  });

  return arrayOfFiles;
}

// 生成文件映射的函数
function generateFileMap() {
  const allFiles = getAllFiles('./src'); // 遍历 src 目录下的所有文件
  const fileMap = {};
  allFiles.forEach((file, index) => {
    fileMap[index] = file;
  });

  fs.writeFileSync('fileMap.json', JSON.stringify(fileMap, null, 2));
}

export default function openInVSCodePlugin() {
  return {
    name: 'vite-plugin-open-in-vscode',
    configResolved(config) {
      if (process.env.NODE_ENV === 'development') {
        // 项目运行之前生成文件映射
        generateFileMap();
      }
    },
    configureServer(server) {
      const app = express();

      app.use(express.json());

      app.get('/vscode/openfile', async (req, res) => {
        const { filePath, lineNumber, columnNumber } = req.query;

        // 读取文件映射
        const fileMap = JSON.parse(
          readFileSync(join(process.cwd(), 'fileMap.json'), 'utf8'),
        );

        if (filePath && lineNumber) {
          exec(
            `code -g ${fileMap[filePath]}:${lineNumber}:${columnNumber || 1}`,
            (err) => {
              if (err) {
                console.error(`Error opening file in VSCode: ${err.message}`);
                res.status(500).send('Error opening file in VSCode');
                return;
              }
              res.send('File opened in VSCode');
            },
          );
        } else {
          console.error('Invalid filePath or lineNumber');
          res.status(400).send('Invalid filePath or lineNumber');
        }
      });

      app.listen(6003, () => {
        console.log(`Express server running on http://localhost:${6003}`);
      });

      server.middlewares.use(app);
    },
  };
}