同事写的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 就不应该被提交到代码仓库中。如果你的项目中有很严格的各种规范校验,那么还是很舒服的,不需要被这种琐碎的事情所打扰。

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

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

相关推荐
Myli_ing27 分钟前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风30 分钟前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave37 分钟前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟39 分钟前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾1 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧1 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7012 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm2 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
幼儿园的小霸王2 小时前
通过socket设置版本更新提示
前端·vue.js·webpack·typescript·前端框架·anti-design-vue