babel插件实战:console.log()在周四的时候,加入"疯狂星期四,v我50🐶"

前言

上期文章有个小伙伴提出了一个很有意思的提议

这提议很有创意,很有意思呀,那必须安排

需求描述

我们在本地开发的时候会打印一些console.log()来方便调试,和打印执行结果

比如

js 复制代码
console.log('你好');

现在我们要实现的结果是,在周四的时候,console.log变成如下

js 复制代码
console.log('疯狂星期四,v我50🐶','你好');

实现思路拆解

1. 首先我们要在astexplorer网站查看console.log的AST:

program 是代表整个程序的节点,它有body属性代表程序体,存放statement(语句)数组,就是具体执行的语句的集合。因为一个程序就是由不同的语句组成的,所以statement是一个数组。

CallExpression(函数调用表达示):callee 属性是一个表达式节点,表示函数体,arguments 是一个数组,元素是表达式节点,表示函数参数列表.

其他的我们不用知道太多,知道太多也记不住,我们主要找我们想改变的东西所在的type

我们要做的是在遍历AST的时候对console.logconsole.infoapi自动插入一些参数,也就是通过visitor指定对callExpression的AST做一些修改。

2. 打开transform,开始编写插件

在这里我们穿插讲一下@babel/types

@babel/types是做什么用的呢?

我们看一下文档上的解释

This module contains methods for building ASTs manually and for checking the types of AST nodes.(这个模块包含一些手动创建AST和检测AST类型的方法)

这个模块是非常常用的

@babel/types文档地址

比如我们想创建一个字符串,那么我们就可以使用

js 复制代码
t.stringLiteral(value);

如果我们想判断是不是字符串 可以使用

js 复制代码
t.isStringLiteral(node, opts)

还是很好记的

首先我们判断函数的类型是否是isMemberExpression,然后判断函数体的名称等

现在我们实现如下,还是比较简单的,但是右侧显示我们输入的字符串都是unicode形式,问题不大,稍后我们会配置,然后把写的plugin内容,放到我们项目中

./pluginDemo

js 复制代码
module.exports = (babel) => {
  const { types: t } = babel;

  return {
    name: "ast-transform", // not required
    visitor: {
      CallExpression(path) {
        if (t.isMemberExpression(path.node.callee)
          && path.node.callee.object.name === 'console'
          && ['log', 'info', 'error', 'debug'].includes(path.node.callee.property.name)
        ) {

          path.node.arguments.unshift(t.stringLiteral('疯狂星期四,v我50🐶'))
        }
      }
    }
  };
}

.babelrc中配置generatorOpts,这个主要是解决显示中文问题的

js 复制代码
{
  "plugins": ["./pluginDemo.js"],
  "generatorOpts" :{
    "jsescOption": {
      "minimal": true
    }
  }
}

测试一下,看一下dist中的输出,没有问题

3.判断是否是周四

如果获取当前是否是周四呢?

js 复制代码
const today = new Date().getDay(); // 获取当前星期几,0表示星期日,1表示星期一,以此类推

所以最后完整代码

js 复制代码
module.exports = (babel) => {
  const { types: t } = babel;
  const today = new Date().getDay();
  return {
    name: "ast-transform", // not required
    visitor: {
      CallExpression(path) {
        if (today !== 4) {
          return;
        }
        if (t.isMemberExpression(path.node.callee)
          && path.node.callee.object.name === 'console'
          && ['log', 'info', 'error', 'debug'].includes(path.node.callee.property.name)
        ) {
          path.node.arguments.unshift(t.stringLiteral('疯狂星期四,v我50🐶'))
        }
      }
    }
  };
}

需求变更

当我们这样子打印console.log的时候,有时候因为console.log的日志因为比较乱,反而影响观看,所以我们决定在consol.log之前打印---疯狂星期四,v我50🐶

在讲解代码之前我们先来讲解一下@babel/template

@babel/template

通过 @babel/types 创建 AST 还是比较麻烦的,要一个个的创建然后组装,如果 AST 节点比较多的话需要写很多代码,这时候就可以使用 @babel/template 包来批量创建。

这个包有这些 api:

javascript 复制代码
const ast = template(code, [opts])(args);
const ast = template.ast(code, [opts]);
const ast = template.program(code, [opts]);

比如我们要创建一个console.log(), 因为console.log是表达式,所以我们要创建一个expression

js 复制代码
const string = "疯狂星期四,v我50🐶";
const newNode = template.expression(`console.log("${string}")`)();

添加console.log注意点

因为我们要往console.log前面添加console.log,而我们新添加的console.log他也会遍历,这样就会出现一个无限循环的问题

如何解决呢?

我们可以在新添加的节点上加上一个标识,如何使新添加的console.log,那么就不用遍历

js 复制代码
 newNode.isNew = true;

最终我们的代码

js 复制代码
const template = require('@babel/template');

module.exports = (babel) => {
  const { types: t } = babel;
  const today = new Date().getDay();
  return {
    name: "ast-transform", // not required
    visitor: {
      CallExpression(path) {
        if (today !== 4) { return; }
      
        if (
          t.isMemberExpression(path.node.callee) &&
          path.node.callee.object.name === "console" &&
          ["log", "info", "error", "debug"].includes(path.node.callee.property.name)
        ) {
          if (path.node.isNew) {
            return;
          }
          const string = "疯狂星期四,v我50🐶";
          const newNode = template.expression(`console.log("${string}")`)();
          newNode.isNew = true;
          path.insertBefore(newNode);
        }
      }
    }
  };
}

总结

  1. 今天我们学习了@babel/types @babel/template的使用
  • @babel/types主要用于创建AST节点和判断AST节点类型
  • @babel/template主要是因为babel/types创建AST节点比较复杂,所以使用babel/template来进行创建
  1. 然后我们学习了如何插入节点,如何防止死循环的问题

参考

相关推荐
m0_471199631 分钟前
【自动化】前端开发,如何将 Jenkins 与 Gitee 结合实现自动化的持续集成(构建)和持续部署(发布)
前端·gitee·自动化·jenkins
w***95493 分钟前
spring-boot-starter和spring-boot-starter-web的关联
前端
Moment6 分钟前
富文本编辑器技术选型,到底是 Prosemirror 还是 Tiptap 好 ❓❓❓
前端·javascript·面试
xkxnq11 分钟前
第二阶段:Vue 组件化开发(第 18天)
前端·javascript·vue.js
晓得迷路了13 分钟前
栗子前端技术周刊第 112 期 - Rspack 1.7、2025 JS 新星榜单、HTML 状态调查...
前端·javascript·html
怕浪猫15 分钟前
React从入门到出门 第五章 React Router 配置与原理初探
前端·javascript·react.js
jinmo_C++15 分钟前
从零开始学前端 · HTML 基础篇(一):认识 HTML 与页面结构
前端·html·状态模式
鹏多多22 分钟前
前端2025年终总结:借着AI做大做强再创辉煌
前端·javascript
小Tomkk30 分钟前
⭐️ StarRocks Web 使用介绍与实战指南
前端·ffmpeg
不一样的少年_34 分钟前
产品催: 1 天优化 Vue 官网 SEO?我用这个插件半天搞定(不重构 Nuxt)
前端·javascript·vue.js