混淆一次 Rollup 构建

@afterpack/rollup 从你的源代码中读取指令,并在 Rollup 写入输出之前,在 generateBundle 中运行引擎。

@afterpack/rollup 与 @afterpack/vite 镜像:Rollup 正是 Vite 构建所依托的基础,因此同样的做法在这里也适用。它从你的源代码中读取指令,然后在 generateBundle 中运行引擎,此时完整的打包产物就在内存里,而 Rollup 还没有写入其中任何一部分。

安装

$ npm install -D @afterpack/rollup
// rollup.config.mjs
import { afterpackRollup } from "@afterpack/rollup";

export default {
  input: "src/index.js",
  output: { dir: "dist", format: "es", sourcemap: true },
  plugins: [afterpackRollup()],
};

具名导出是 afterpackRollup。

构建

该插件以 order: "post" 运行在 generateBundle 中,因此它是最后一个接触打包产物的钩子。它会混淆打包产物中持有的每一个 JavaScript chunk 并交还回去,因此 Rollup 写入的是混淆后的 chunk,而绝不会写入原始内容。一次失败的混淆会让输出目录保持原样。

验证

在生产构建中显式请求 Protection Map:

afterpackRollup({ protectionMap: { enabled: true } });

它会落在已被 gitignore 的 .afterpack/protectionMap.html 中。开启 output.sourcemap 时,它会渲染出你的原始源码;如果没有,就没有可渲染的内容,报告会说明这一点。报告包含你的完整源码。绝不要提交它或对外提供服务。

entitlements.jslight · complexity 5Open full size
entitlements.js, 54 lines, built at the light preset with a directive around the signature check. Click any token to see what was applied to it.

选项

每一个配置键都可以作为选项对象在这里传入,例如 afterpackRollup({ preset: "hard", complexity: 40 })。

指令

插件会在压缩之前从你的源代码中读取指令,因此指令能在打包过程中存活下来。

/* @afterpack preset=hard */
export function signRequest(payload) {
  // ...
}
/* @afterpack end */

捕获会跳过 node_modules、虚拟模块和非 JS/TS 文件。语法在各处都是一样的。

边界情况

多个输出

一个配置了两个输出的构建会对每个输出各运行一次 generateBundle,每次运行只能看到自己的打包产物。无论输出写到哪个目录,都不会有任何内容被混淆两次:

output: [
  { dir: "dist/esm", format: "es", entryFileNames: "lib.js" },
  { dir: "dist/cjs", format: "cjs", entryFileNames: "lib.cjs" },
],
plugins: [afterpackRollup({ seed: "git" })],

对一起发布的多个输出固定同一个 seed。

库构建

AfterPack 运行在 Rollup 已经构建好的打包产物之上,因此你的使用者导入的每一个导出名称都会按原样保留;只有内部实现会被重命名和重构。

下一步

  • 框架:完整对照表。
  • Vite:同样的钩子,搭配 Vite 自己的配置解析。
  • 构建与 CI:种子、开发环境与生产环境的区别、提升同一份字节。
  • Protection Map:如何阅读这份报告。
  • 指令:每一个标记键,以及它的作用。
  • 配置:引擎的配置面。