likes
comments
collection
share

如何使用 Rollup 插件开发

作者站长头像
站长
· 阅读数 21

rollup 插件开发

tips: 本文带有强烈的个人理解,所以有些地方我理解可能有误,读者如有质疑不用怀疑,绝对是我的错,是我水平太次。

上一篇文章讲了讲 rollup 的简单配置,对于插件这块没有怎么讲,那么本篇开启插件的旅程~

插件生命周期

如何使用 Rollup 插件开发

密密麻麻的一堆堆的API真让人讨厌,遇到问题不要慌。

下面我将列出一些我个人觉得常用的API,咱们先熟悉大概知道API干啥的。

name: 插件的名称,用于错误消息和警告。

version: 插件的版本,用于插件间通信场景。

来看下下面两张图

编译阶段钩子

如何使用 Rollup 插件开发

输出生成钩子

如何使用 Rollup 插件开发

看完了? OK,那么我们开始进入实战……

你肯定好奇我为啥不细讲,因为我看到这些API我自己也懵逼,我讲了你更懵逼……

自动上传构建包与自动生成文档

在某某科技公司,我们男主角"小明"正在快乐的摸鱼划水。

突然领导拍着他的肩膀说:小火砸,现在有个任务交给你,公司的项目比较多。有很多的重复工具相关的代码,现在你负责把这些工具类代码抽出来,到时候给其他的开发使用。你这个包回头要发到cdn上,体积最好小点,不要影响用户体验。

小明这条老咸鱼三下五除二,很快的就把工作做完了,也用rollup把代码打包好了,手动上传代码至cdn服务器上。

但是小明呢喜欢摸鱼,每次上传cdn服务器都要手动上传也挺麻烦的,万一哪天整错了,出问题了呢?

小明就去翻翻rollup文档,发现 输出生成钩子 有个叫writeBundleAPI,小明突然灵光乍现,可以rollup输出生成代码后调用接口直接将代码上传至cdn服务器呀。

tips: 知识点 writeBundle钩子,会在构建完成后调用


// upload-source.js

import FormData from "form-data";
import axios from "axios";

export default function buildEndUploadBundle() {
  return {
    name: "uploadBundle", // this name will show up in warnings and errors
    async writeBundle() {
      const formData = new FormData();
      const filePath = path.join(process.cwd(), "/dist/index.js");
      // 构建完成后读取本地文件
      formData.append("files[]", fs.createReadStream(filePath));
      const formHeaders = formData.getHeaders();
      // const { code } = await axios.post("xxxx.com/upload", formData, {
      //   headers: {
      //     ...formHeaders,
      //   },
      // });
      if (code) {
        // 调用钉钉通知
      }
    },
  };
}

小明写完这个插件之后每次只需要执行构建命令,打包后的代码就会自动的上传至cdn服务器,又能节省出时间来摸鱼了。

过了半个月,小明写的工具类已经应用到公司的所有项目中。

但是现在小明很苦恼,同事需要什么工具方法的时候,小明就要往里头加,写文档,打包,最后告诉某位同事你需要的方法加好了,有时候忙小明忘了通知……搞得开发非常的不愉快。

这个小问题还不简单,小明分分钟就搞定了。只需要在上传完成的阶段调用钉钉机器人通知同事就完事了。

可接着小明突然发现一个事情,现在工具类已经很大了,有不少的方法函数,虽然每个方法都加有注释,但是缺少文档!可就算现在去写文档,文档后期维护也是一个问题!

现在问题来了 文档怎么办?


// doc-generate.js
import * as parser from "@babel/parser";
import traverse from "@babel/traverse";
import doctrine from "doctrine";

// 省略一些与本文相关性不太大的代码 
...

export default function docGenerate() {
  const docs = [];
  return {
    name: "docGenerate",
    async transform(code, id) {
      const sourceCode = code;
      const babelParsedAst = parser.parse(sourceCode, {
        sourceType: "unambiguous",
      });
      traverse.default(babelParsedAst, {
        ExportNamedDeclaration(path) {
          const funcDeclaration = path.get("declaration");
          docs.push({
            type: "function",
            name: funcDeclaration.get("id").toString(),
            params: funcDeclaration.get("params").map((paramPath) => {
              return {
                name: paramPath.toString(),
                type: resolveType(paramPath.getTypeAnnotation()),
              };
            }),
            return: resolveType(
              funcDeclaration.get("returnType").getTypeAnnotation()
            ),
            doc:
              path.node.leadingComments &&
              parseComment(path.node.leadingComments[0].value),
          });
        },
      });
      return {
        code,
        map: null,
      };
    },
    writeBundle() { 
      fs.writeFile("./文档.md", generateDoc(docs), function (err) {
        if (err) {
          return console.log("文件写入失败!" + err.message);
        }
        console.log("文件写入成功!");
      });
    },
  };
}

tips: 知识点 transform钩子,会把源码和源码文件地址传递过来。

正当小明正在苦思冥想文档怎么的时候,某位神秘的大佬来给小明讲了讲神奇的ASTbabel

小明突发奇想利用 rollup 构建阶段 transform ,这个 API 会返回代码文件内容和文件地址,这个时候拿到 code 代码,再去使用 babel 分析 AST 树拿到我们想要的信息存储,最终等待编译阶段的时候重新生成一篇新的文档,由于文档内容来源于注释,所以每次构建都是最新的文档。

完结

这一篇文章给大家相对粗浅的讲了一下插件开发,讲了两个 API ,其实我个人用的多还是 writeBundle ,写过一个上传source map 的插件。

第一个插件 用了 writeBundle 钩子,这个插件做的事情大家都应该能看得懂,无非就是读取文件上传然后再去调用接口。

第二个插件用了 transform 钩子难度直线飙升,大家应该看的很懵逼把? 不要慌! 其实大部分的场景我们使用 npm 上的rollup 插件已经能够满足我们的开发需求,之所以写第二个插件是为了让大家认识下 babelAST

可能你们会好奇,为什么我不细讲讲第二个插件,因为第二个插件的所需要的知识点不是一两句话能够讲清楚的,单说 babel 复杂的配置项,让人脑壳痛,更别提 AST 树(好像 AST 树简单点……

当然如果你们对 babel 感兴趣的话,有机会出"十几篇"文章聊聊也不是不可以哦!这里提一嘴,我们用的 vite webpack 等等背后都有 babel 参与!

下一篇文章,来聊聊 cli 工具开发。

转载自:https://juejin.cn/post/7252246218289872954
评论
请登录