同事写的console.log太多令人烦恼?来手撕一个vite插件去掉它

前言

不知道各位同学在开发的过程中有没有遇到这样的一种情况:就是跟你一起合作做项目的同学调试的时候打了不少 console ,然后提交代码的时候没删。

打印得多了,可能就长成下面的样子。特别是有一些在公共的数据变化时打印,就更加惨不忍睹。

这就给我们自己调试的时候,想在控制台找到自己打印的东西比较麻烦,虽然说花点心思找一下或者搜一下也能找到,但是我为啥要花时间花心思在这上面呢?而且这么多打印的东西看着就烦。

今天,我们就写一个 vite 插件,来去掉同事写的 console.log,当然,要保留我们自己写的。

vite插件初体验

首先新建一个 remove-console-plugin 目录,在这个目录下新建一个 index.js 文件夹。在 vite 插件体系中,修改输出的代码用的是 transform 这个钩子:

js 复制代码
export default function myPlugin() {
  return {
    name: "remove-console-plugin",
    transform(code, id) {
      console.log("code", code);
      return code
    },
  };
}

然后我们可以先随便改点什么东西,比如说我在 js 文件的最后加一行注释:

js 复制代码
transform(code, id) {
  const url = id;
  if (url.includes("/src/") && /\.([tj]sx?|js)$/.test(url)) {
    return code + `\n` + "// 一行注释";
  }
  return code;
},

那么可以看到,请求回来的文件已经带上了我们加的内容。

但这里有一个问题, transform 钩子调用时,代码已经被预处理(例如通过 ESBuild 或者 TypeScript 编译器)过了。

后面我们需要分析这行代码是谁写的,因为我们做的是一个去除别人代码 console 的一个插件,所以我们需要拿到源文件的内容。

拿到源文件内容的话就要使用到 load 这个钩子函数:

js 复制代码
load(id) {
  const url = id;
  if (url.includes("/src/") && /\.([tj]sx?|js)$/.test(url)) {
    let originalContent = fs.readFileSync(id, "utf-8");
    return originalContent;
  }
}

控制台打印一下,我们确实已经能拿到源文件的内容:

AST去console

然后,转换代码我们需要用到 AST(抽象语法树) 。可以使用 Babel 来实现,首先安装一下必要的依赖:

bash 复制代码
npm install @babel/parser @babel/traverse @babel/generator

比如我写了一个这样的代码

js 复制代码
console.log(123)

我们可以在这个 AST工具网站 中看到它转换成 AST 之后是一个怎样的结构:

AST 的使用流程主打一个解析-转换-生成

  • 解析:把我们的源代码解析成 AST 结构,可以理解为一颗 JSON 树,然后通过 traverse 这个库的访问者模式,它可以访问这棵树的每一个节点
  • 转换:找到你需要对它操作的节点,使用 AST 的相关操作,改变这个节点的内容。比如下面的 path.remove() ,就是把这个节点删掉。
  • 生成:把 AST 再次转回源代码

写出下面的代码,找出 src 文件夹下所有 console.log 的位置,且只在开发模式下生效:

js 复制代码
import fs from "fs";
import { parse } from "@babel/parser";
import traverse from "@babel/traverse";
import generator from "@babel/generator";
let isDev = false;
export default function myPlugin() {
  return {
    name: "remove-console-plugin",
    config(config, ctx) {
      isDev = ctx.mode === "development";
    },
    load(id) {
      const url = id;
      if (url.includes("/src/") && /\.([tj]sx?|js)$/.test(url) && isDev) {
        let originalContent = fs.readFileSync(id, "utf-8");
        const ast = parse(originalContent, {
          sourceType: "module",
          plugins: ["jsx", "typescript"],
        });
        traverse.default(ast, {
          CallExpression(path) {
            if (
              path.node.callee.type === "MemberExpression" &&
              path.node.callee.property.name === "log"
            ) {
                
              console.log(
                `我在文件${id}的第${path.node.loc.start.line}行找到了console.log。`
              );
              path.remove(); // 删除这个节点
            }
          },
        });
        const { code } = generator.default(ast);
        return code;
      }
    },
  };
}

这跟我们对应的文件中 console.log 的位置是一致的:

可以看到我们调用 path.remove() 之后,控制台一片清净,大块大块的 console.log 已经没有了。

git接入

当然,这样做是不行的,这样把我们自己打的 console 也去掉了。我们是想去掉别人打的 console

那怎么知道这行是谁打的呢?换个问题就是,我怎么知道这行代码是谁写的?

那当然是依赖代码管理工具咯,我们最常用的始终是 git ,所以这里是以 git 为依托。

这里封装一个执行系统命令的函数,方便我们之后调用各种系统命令。

js 复制代码
const execCommand = (command) => {
  return new Promise((resolve, reject) => {
    exec(command, (err, stdout, stderr) => {
      if (err) {
        reject(err);
        return;
      }
      if (stderr) {
        reject(new Error(stderr));
        return;
      }
      resolve(stdout.trim());
    });
  });
};

然后执行 git config user.name 来获取用户名

js 复制代码
 if (!username) {
    username = await execCommand("git config user.name");
}

获取到用户名之后,我们可以使用 git blame 文件路径 | nl -n ln 这个命令去对每一个文件进行分析,它打印的结果如下

可以看到这里包含了行号、提交人。

我们就可以对所有需要处理的文件进行分析,并组装成一个 mapkey 是行号, value 是提交人

js 复制代码
const blameOutput = await execCommand(`git blame ${id} | nl -n ln`);

  let map = blameOutput
    .trim()
    .split("\n")
    .reduce((acc, line) => {
      let [numStr, hash, author, ...rest] = line.split(/\s+/);
      let num = parseInt(numStr, 10);
      acc[num] = author.replace("(", "").replace(")", "");
      return acc;
    }, {});

  console.log("map", map);

可以得到这样的一个 map

最后就可以通过 console.log 的行号与提交的信息去匹配,如果这条 console 不是我提交的、或者不是未提交的,那么这条打印信息我们就应该去掉。

js 复制代码
const logLine = path.node.loc.start.line;
const commiter = map[logLine];
if (commiter !== username && commiter !== "Not") {
  path.remove();
}

我们就可以看到,别人打印的都已经去掉了,留下来的都是我们自己打印的,整个世界清净了,舒服了。

最后

按理来说, console.log 就不应该被提交到代码仓库中。如果你的项目中有很严格的各种规范校验,那么还是很舒服的,不需要被这种琐碎的事情所打扰。

如果没有,那么就想个办法去解决他吧!

以上就是本文的全部内容,如果你觉得有意思的话,点点关注点点赞吧~

相关推荐
太阳花ˉ2 分钟前
html+css+js实现step进度条效果
javascript·css·html
小白学习日记43 分钟前
【复习】HTML常用标签<table>
前端·html
john_hjy1 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd1 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
丁总学Java1 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele1 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
It'sMyGo2 小时前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
懒羊羊大王呀2 小时前
CSS——属性值计算
前端·css
DOKE2 小时前
VSCode终端:提升命令行使用体验
前端
xgq2 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试