混淆 Vite 构建

@afterpack/vite 在 generateBundle 中混淆你的构建,早于 Vite 写出文件;它是其他所有 Vite 系包所封装的参考集成。

@afterpack/vite 是参考集成。Astro、Svelte、SvelteKit、Vue 和 Nuxt 都封装了它,因此本页所述同样适用于这些包。

它先从你的源码中读取 @afterpack 指令,然后在 generateBundle 中运行引擎,此时完整的 bundle 还在内存中,Vite 尚未写出任何文件。

安装

$ npm install -D @afterpack/vite
// vite.config.ts
import { defineConfig } from "vite";
import { afterpackVite } from "@afterpack/vite";

export default defineConfig({
  plugins: [afterpackVite()],
});

构建

配置到此为止。npm run build 现在会输出已混淆的结果;vite dev 不受影响,因为插件只在构建的 generateBundle 阶段起作用。Vite 写出的是已混淆的 chunk,原始文件从不落盘;混淆失败时,outDir 保持原样。

它混淆的是 Vite 构建出来的内容:输出 bundle 中的每一个 chunk。Vite 从 publicDir 原样复制到输出目录的文件没有经过构建,不在 bundle 中,也不会被处理。任何需要保护的内容都应经过打包工具。

插件会写出:

  • protectionMap.html:整个构建的一份合并地图,始终写入项目根目录下已被 gitignore 的 .afterpack/。它内嵌了你的原始源码。绝不要让它落入对外提供服务的目录。
  • foo.js.map:启用 map 时写出的 source map。关闭 map 时(这是生产环境的默认值),Vite 自身的 map 条目会被丢弃,而不会写在已混淆的 chunk 旁边。

这里不会有 foo.backup.<hash>.js。已混淆的字节直接回写到 bundle 中,没有已写出的文件可供备份放在旁边,因此 build.backup: true 只会发出警告,而不会写出备份。

.afterpack/ 自带一个 .gitignore,因此插件从不修改你的 .gitignore。产物落入对外提供服务的路径时,它会发出警告。

验证

Protection Map 默认会在 Vite 输出 source map 且构建不在 CI 中(CI=true 或 CI=1)时写出;生产构建不会关闭它。除非设置了 build.sourcemap,否则 Vite 不会输出 source map,因此在常规构建中 Protection Map 默认关闭。想要查看时请显式开启:

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

在未设置 build.sourcemap 的情况下,这同时会开启 Vite 自身的 source map,以便地图能够渲染你的原始源码。找不到可用的 map 时,插件没有可据以渲染原始源码的来源,并会如实说明。

报告写入 .afterpack/protectionMap.html。它包含你的完整源码。绝不要提交或对外提供它,只在本地查看。

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.
绝不要把 source map 发布到生产环境

一个 .map 文件就能直达你的原始源码。绝不要部署它。

选项

每个配置键都可以通过这里的选项对象传入,例如 afterpackVite({ preset: "hard", complexity: 40 })。所有选项均为可选。具名导出为 afterpackVite。

preset 和 complexity 设置的是不同的东西。预设是一组打包好的设置:它同时设定复杂度目标、总输出体积倍数和膨胀预算。complexity 则是这组设置中的一个数字:

afterpackVite({ preset: "hard", complexity: 40 }); // hard's size limits, target 40

指令

插件会在任何压缩器剥离注释之前,从你的源码中读取指令。语法只接受块注释:

/* @afterpack preset=hard */
function verifyLicense(token) {
  // ...
}
/* @afterpack end */

// @afterpack … 这样的行注释不会被扫描,并会作为诊断信息报告出来。

指令可以设置的每个键都列在指令页面上。

捕获时会跳过 node_modules 以及非 JS/TS 文件。写在 .vue 或 .svelte 单文件组件内的指令不会被捕获。请改放到该组件导入的普通 .ts/.js 模块中。设置 directives.enabled: false 可完全跳过扫描。

边界情况

库模式

build.lib 的输出是一份公开契约。AfterPack 作用于最终的 bundle,因此导出名称会保留;内部实现仍会被重命名和重组。

多个输出

每次 Vite 构建只混淆自己的 bundle,不涉及其他内容,因此即使多次构建写入同一个目录,它们也是各自独立的处理。如果它们共享运行时契约,请为它们固定同一个 seed。

下一步