深入了解rollup(四)插件开发示例

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Rollup是一个JavaScript模块打包器,它可以将多个模块打包成一个单独的文件,以便在浏览器中使用。与其他打包工具相比,Rollup的主要优势在于它可以生成更小、更快的代码。在本文中,我们将深入了解Rollup的插件开发。

引言

Rollup是一个JavaScript模块打包器,它可以将多个模块打包成一个单独的文件,以便在浏览器中使用。与其他打包工具相比,Rollup的主要优势在于它可以生成更小、更快的代码。在本文中,我们将深入了解Rollup的插件开发。

@rollup/pluginutils介绍

@rollup/pluginutils是一个官方提供的Rollup插件开发工具库,它提供了一些实用的函数和工具,用于简化插件开发过程中的一些常见任务。

该工具库提供了以下常用的函数和工具方法:

  1. createFilter(include?: string | RegExp | (string | RegExp)[], exclude?: string | RegExp | (string | RegExp)[]): FilterPattern
  • 用于创建一个过滤器,可以根据指定的包含和排除规则来过滤文件。
  • 可以传入字符串、正则表达式或字符串/正则表达式数组作为参数。
  • 返回一个函数,该函数接受文件路径作为参数,并返回一个布尔值,表示该文件是否应该被处理。
  1. makeLegalIdentifier(name: string): string
  • 用于将给定的字符串转换为合法的JavaScript标识符。
  • 主要用于处理可能包含非法字符或保留字的模块名称。
  1. dataToEsm(data: any, options?: DataToEsmOptions): string
  • 将给定的数据转换为ES模块导出语法的字符串。
  • 可以传入选项对象来自定义导出语法。
  1. attachScopes(ast: any, scope: Scope): void
  • 将作用域信息附加到AST(抽象语法树)节点上。
  • 可以帮助插件在处理代码时正确地处理变量作用域。

这些函数和工具可以帮助开发者更方便地处理文件过滤、标识符转换、数据转换和作用域处理等常见任务,提高插件开发的效率和可靠性。

插件上下文

插件上下文

这个其实也是插件中很常用的一些api,可以通过 this 从大多数钩子中访问一些实用函数和信息位。

自定义插件

rollup-plugin-custom

import { createFilter } from'@rollup/pluginutils';
importpathfrom"path";
exportdefaultfunctioncustomPlugin(options= {}) { 
constfilter=createFilter(options.include, options.exclude);
return {
name: "custom-plugin",
transform(code, id) { 
if (!filter(id)) { 
returnnull;
      }
constparsedCode=this.parse(code);
constsource=`${code}\n\n ${JSON.stringify(parsedCode, null, 2)}`;
constfileName=path.basename(id, path.extname(id));
console.log(fileName);
if (options.emitFile) { 
this.emitFile({
type: "asset",
fileName: fileName+".txt",
source        })
      }
    }
  }
}

首先,通过 createFilter 函数创建一个过滤器,用于确定哪些文件需要被处理。options.includeoptions.exclude 分别指定了需要包含和排除的文件。

然后,返回一个对象,其中包含了插件的名称和一个 transform 方法。transform 方法会在每个模块被转换时调用。 在 transform 方法中,首先使用过滤器检查当前模块是否需要处理。如果不需要处理,则返回 null

接下来,使用 this.parse(code) 方法解析代码,并将解析结果与原始代码拼接成一个新的字符串 source。 然后,使用 path.basename(id, path.extname(id)) 获取当前模块的文件名(不包含扩展名),并打印输出。 如果设置了 options.emitFile 为 true,则调用 this.emitFile() 方法将处理后的代码作为一个 asset 文件输出。输出的文件名为当前模块的文件名加上 .txt 扩展名。

最后,这个插件可以通过在 Rollup 配置文件中引入并添加到插件列表中来使用。

rollup.config.mjs

import { defineConfig } from"rollup";
importcustomPluginfrom"./plugins/rollup-plugin-custom.js";
exportdefaultdefineConfig({
input: "src/index.js",
output: {
dir: "dist",
format: "esm",
sourcemap: true,
  },
plugins: [
customPlugin({
emitFile:true    })
  ],
});

JSON插件示例

rollup默认是不能直接读取json文件的内容的,我们自己写一个插件处理一下。

安装

npm install @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/pluginutils -D

rollup-plugin-json

import { createFilter,dataToEsm } from'@rollup/pluginutils';
importpathfrom'path';
exportdefaultfunctionmyJson(options= {}) {
// rollup 推荐每一个 transform 类型的插件都需要提供 include 和 exclude 选项,生成过滤规则constfilter=createFilter(options.include, options.exclude);
return {
name: 'rollup-plugin-json',
transform: {
order: "pre",
handler(code, id) {
if (!filter(id) ||path.extname(id) !=='.json') returnnull;
try {
constparse=JSON.stringify(JSON.parse(code));
return {
// dataToEsm 将数据转换成esm模块// 其实就是 export default "xxx"code: dataToEsm(parse), 
map: { mappings: '' }
          };
        } catch (err) { 
constmessage='Could not parse JSON file';
this.error({ message, id, cause: err });
returnnull;
        }
      }
    }
  };
}

首先,通过 createFilter 方法创建一个过滤器,用于确定哪些文件需要被处理。options.includeoptions.exclude 分别指定了需要包含和排除的文件。

然后,返回一个对象,其中包含了插件的名称和一个 transform 对象。transform 对象中有两个属性:orderhandler

  • order: "pre" 表示这个插件在转换过程中应该在其他插件之前执行。
  • handler(code, id) 是一个处理函数,它会在每个模块被转换时调用。

handler 函数中,首先使用过滤器检查当前模块是否需要处理,并且判断当前模块是否是 JSON 文件。如果不需要处理或者不是 JSON 文件,则返回 null

接下来,尝试将代码解析为 JSON 对象,并使用 dataToEsm(parse) 方法将解析后的对象转换为 ES 模块格式的代码。然后返回一个对象,其中包含了转换后的代码和一个空的 Source Map。

如果解析过程中出现错误,则会捕获错误并通过调用 this.error() 方法抛出错误信息,并返回 null

最后,这个插件可以通过在 Rollup 配置文件中引入并添加到插件列表中来使用。它会在构建过程中将 JSON 文件转换为 ES 模块格式的代码。

页面使用

importpkgfrom"../package.json";
console.log(pkg.name)

图片读取

mini-svg-data-uri是一个用于将SVG图像转换为mini data URI格式的JavaScript库。它可以将SVG图像的内容转换为base64编码,并生成一个data URI,以便在HTML或CSS中直接使用。

安装mini-svg-data-uri

npm install mini-svg-data-uri -D

rollup-plugin-image

import { createFilter,dataToEsm } from"@rollup/pluginutils";
import { extname,resolve,basename,relative,normalize,sep } from"path";
importfsfrom"fs";
importsvgToMiniDataURIfrom"mini-svg-data-uri";
constdefaults= {
fileSize: 1024*4,
target: "./dist",
include: null,
exclude: null,
}
constmimeTypes= {
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".svg": "image/svg+xml",
".ico": "image/x-icon",
".webp": "image/webp",
".avif": "image/avif"}
constgetDataUri= ({ format, isSvg, mime, source }) =>isSvg?svgToMiniDataURI(source) : `data:${mime};${format},${source}`;
constensureDirExists=async (dirPath) => { 
try {
awaitfs.promises.access(dirPath);
returntrue;
  } catch (err) { 
// 文件夹不存在就创建文件夹try {
awaitfs.promises.mkdir(dirPath, { recursive: true });
returntrue;
    }
catch (err) { 
console.error(err);
returnfalse;
    }
  }
}
exportdefaultfunctionmyImage(opts= {}) { 
constoptions=Object.assign({}, defaults, opts);
constfilter=createFilter(options.include, options.exclude);
return {
name: "my-image",
asynctransform(code, id) { 
if (!filter(id)) returnnull;
// 获取后缀constext=extname(id);
// 判断是否是图片if(!mimeTypes.hasOwnProperty(ext)) {
returnnull;
      }
// 获取图片的mime类型constmime=mimeTypes[ext];
// 判断是否svgconstisSvg=mime===mimeTypes[".svg"];
// 图片format格式constformat=isSvg?"utf-8" : "base64";
// 目标路径constassetsPath=resolve(process.cwd(), options.target);
//获取文件名constfileName=basename(id);
// 最终文件路径constfilePath=resolve(assetsPath, fileName);
letrelativePath=normalize(relative(process.cwd(), filePath));
relativePath=relativePath.substring(relativePath.indexOf(sep) +1);
try {
// 如果图片文件过大,就应该直接拷贝文件,返回文件路径// 读取图片文件大小与设置的大小进行比较conststat=awaitfs.promises.stat(id);
if (stat.size>options.fileSize) {
// 文件的拷贝,以及对象的返回// 文件拷贝,无非就是文件源路径,目标路径// copyFile 拷贝文件地址的文件夹必须存在// 如果文件夹不存在,那么就创建文件夹constdirExists=awaitensureDirExists(assetsPath);
dirExists&&awaitfs.promises.copyFile(id, filePath);
return {
code: dataToEsm(relativePath), //返回拷贝之后处理的路径map: { mappings: "" }
          }
        } else {
// 否则转换为base64格式// 读取文件constsource=awaitfs.promises.readFile(id, format);
return {
code: dataToEsm(getDataUri({ format, isSvg, mime, source })),
map: { mappings: "" }
          }
        }
      } catch (err) { 
constmessage="图片转换失败:"+id;
this.error({ message, id, cause: err });
returnnull;
      }
    }
  }
}
  1. createFilter(include, exclude): 这个函数来自于@rollup/pluginutils包,用于创建一个过滤器函数,根据给定的include和exclude规则来判断文件是否需要被处理。
  2. extname(id): 这个函数来自于path模块,用于获取文件路径的扩展名。
  3. resolve(...paths): 这个函数来自于path模块,用于将多个路径片段解析为绝对路径。
  4. basename(path): 这个函数来自于path模块,用于获取文件路径的基本名称(不包含目录部分)。
  5. relative(from, to): 这个函数来自于path模块,用于获取从一个路径到另一个路径的相对路径。
  6. normalize(path): 这个函数来自于path模块,用于规范化给定的路径字符串。
  7. sep: 这是一个常量,表示操作系统特定的路径分隔符(例如,在Windows上是反斜杠``)。
  8. fs.promises.access(path): 这是一个Promise-based API,用于检查指定路径是否可访问。
  9. fs.promises.mkdir(path, options): 这是一个Promise-based API,用于创建指定路径的目录。options参数可以包含递归选项,以便创建多级目录。
  10. fs.promises.stat(path): 这是一个Promise-based API,用于获取指定路径的文件信息,例如文件大小。
  11. fs.promises.copyFile(src, dest): 这是一个Promise-based API,用于将源文件复制到目标文件。
  12. fs.promises.readFile(path, encoding): 这是一个Promise-based API,用于读取指定路径的文件内容。encoding参数用于指定读取的编码格式。
  13. dataToEsm(value): 这个函数来自于@rollup/pluginutils包,用于将给定的值转换为ES模块导出语法。
  14. svgToMiniDataURI(svg): 这个函数来自于mini-svg-data-uri包,用于将SVG图像转换为mini data URI格式。

在插件的transform方法中,首先使用过滤器函数判断是否需要处理当前文件。然后根据文件扩展名判断是否为图片文件,并获取对应的MIME类型。接下来根据配置的目标路径和文件名构建最终的文件路径。如果图片文件大小超过了设置的阈值,则直接拷贝该文件到目标路径,并返回拷贝后的路径。否则,将图片内容转换为base64格式,并返回对应的data URI。

rollup.config.mjs

import { defineConfig } from"rollup";
importimagePluginfrom'./plugins/rollup-plugin-image.js'exportdefaultdefineConfig({
input: "src/index.js",
output: {
dir: "dist",
format: "esm",
sourcemap: true,
  },
plugins: [
imagePlugin({
fileSize: 1024*10,
target: './dist/assets'    })
  ],
});

总结

  1. Rollup插件机制允许开发者通过编写自定义插件来扩展Rollup的功能。
  2. 插件是由一个或多个钩子函数组成的,钩子函数定义了在打包过程中的不同阶段执行的操作。
  3. 常用的钩子函数有optionsresolveIdloadtransformgenerateBundle,每个钩子函数都有特定的调用时机和参数。
  4. 插件可以通过返回一个Promise对象来处理异步操作。
  5. Rollup插件可以使用第三方库来辅助开发,例如rollup-pluginutils用于创建过滤器。
  6. 开发者可以根据自己的需求编写自定义插件,并将其添加到Rollup配置中,以实现各种功能扩展,例如压缩代码、处理CSS、加载和解析JSON等。
  7. 插件开发需要注意性能和代码质量,避免不必要的操作和副作用。

通过使用Rollup插件机制,开发者可以灵活地定制打包过程,并根据项目需求添加各种功能扩展。这使得Rollup成为一个强大而灵活的JavaScript模块打包工具。

目录
相关文章
|
7月前
|
存储 JavaScript 前端开发
深入了解rollup(五)插件输出生成钩子
输出生成钩子可以提供有关生成的产物的信息并在构建完成后修改构建。它们的工作方式和类型与 构建钩子 相同,但是对于每个调用 bundle.generate(outputOptions) 或 bundle.write(outputOptions),它们都会单独调用。仅使用输出生成钩子的插件也可以通过输出选项传递,并且因此仅针对某些输出运行。
165 0
|
4月前
|
JSON JavaScript IDE
[译] 使用 microbundle 打包 TypeScript 组件库
[译] 使用 microbundle 打包 TypeScript 组件库
|
7月前
|
缓存 前端开发 JavaScript
深入了解rollup(三)插件机制
Rollup 插件是一个对象,具有属性]、构建钩子 和 输出生成钩子 中的一个或多个,并遵循我们的约定。插件应作为一个导出一个函数的包进行发布,该函数可以使用插件特定的选项进行调用并返回此类对象。 插件允许你通过例如在打包之前进行转译代码或在node_modules文件夹中查找第三方模块来自定义 Rollup 的行为。
143 1
|
JSON JavaScript 前端开发
30分钟搞懂Rollup+Typescript工程构建(二)
在本文中不讨论Typescript的具体用法,我们将学习如何将Typescript代码转为JavaScript。
510 0
|
缓存 JSON JavaScript
30分钟搞懂Rollup+Typescript工程构建(一)
最近在研究一个ngptcommit命令行工具,然后想通过Rollup+Typescript去编译的时候,发现对Rollup和Typescript的编译配置有点陌生,所以希望通过本文能够对其有个系统的认知。
233 0
|
JavaScript 前端开发 API
Vite 是如何使用 Rollup 进行构建的
Vite 是如何使用 Rollup 进行构建的
335 0
|
缓存 JSON Rust
|
JavaScript 前端开发 算法
rollup 是什么?如何使用?
继 Webpack、Vite 等前端工具链系列的了解之后,又碰到了 Rollup,我之前对 Rollup 的了解仅停留在 Vite 打包时使用、组里的大佬使用 Rollup 写过一个静态官网页面,为了
|
缓存 前端开发 API
Vite 是如何兼容 Rollup 插件生态的
Vite 是如何兼容 Rollup 插件生态的
201 0
|
JSON 前端开发 JavaScript
基于rollup打造组件库,你可能会用到这些插件
最近在重复赵轮子,用rollup搞一个react 组件库,今天就简单整理下用到的插件。 简单来说插件就是能力者,帮助你完成某事,相信大家都用过webpack构建项目,我们会用到各种plugin和loader来帮我们处理js、css、图片、压缩代码等,那么在rollup里想要完成某事,也同样需要对应的plugin来完成,而且rollup的配置会更加简单。
964 1
基于rollup打造组件库,你可能会用到这些插件