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

简介: 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模块打包工具。

目录
相关文章
|
定位技术
百度地图缩放级别与比例尺的关系
百度地图缩放级别与比例尺的关系
1987 0
|
JavaScript 前端开发
CMD和UMD,ES Module的差别
CMD和UMD,ES Module的差别
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
467 0
|
前端开发 API 开发者
乱花迷人眼 - 一文彻底看懂 package.json 中的各种 dependencies
package.json 中存在各种各样的依赖定义:dependencies、devDependencies、peerDependencies、optionalDependencies、bundleDependencies,很容易让初学的开发者晕头,到底有什么区别。
|
前端开发 Linux iOS开发
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
【4月更文挑战第30天】Flutter扩展至桌面应用开发,允许开发者用同一代码库构建Windows、macOS和Linux应用,提高效率并保持平台一致性。创建桌面应用需指定目标平台,如`flutter create -t windows my_desktop_app`。开发中注意UI适配、性能优化、系统交互及测试部署。UI适配利用布局组件和`MediaQuery`,性能优化借助`PerformanceLogging`、`Isolate`和`compute`。
1427 0
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
|
SQL JSON 关系型数据库
MYSQL--JSON_OBJECT 和 JSON_ARRAYAGG
MYSQL--JSON_OBJECT 和 JSON_ARRAYAGG
|
移动开发 JavaScript 前端开发
vue实现调用手机拍照、录像功能
vue实现调用手机拍照、录像功能
720 0
|
JavaScript 前端开发 Unix
NodeJS文件系统遍历工具:fast-glob
NodeJS文件系统遍历工具:fast-glob
843 0
|
自然语言处理 JavaScript 安全
VUE 学习笔记(三) Vue 渲染流程详解
VUE 学习笔记(三) Vue 渲染流程详解
381 1
|
JavaScript 前端开发
深入了解rollup(二)常用配置
Rollup是一个JavaScript模块打包器,它可以将多个模块打包成一个单独的文件,以便在浏览器中使用。与其他打包工具相比,Rollup的主要优势在于它可以生成更小、更快的代码。在本文中,我们将深入了解Rollup的常用配置的使用方法。
514 0