混淆一个 Astro 站点

@afterpack/astro 是一个很薄的集成,把 @afterpack/vite 接入 Astro 自己的 Vite 流水线。

Astro 会通过它自己的 Vite 流水线编译每一个 island 和客户端打包产物,而 AfterPack 是在打包工具这一层做集成的。有了 @afterpack/astro,你就不必自己手动接入 vite: { plugins: [...] }。

一切都发生在 @afterpack/vite 的 generateBundle 中,处于 Astro 自己的 Vite 流水线内部。

安装

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

export default defineConfig({
  integrations: [afterpack()],
});

默认导出和具名的 afterpackAstro 导出是同一个函数。

构建

astro build 现在会生成混淆后的输出。astro dev 不受影响,因为插件只运行在构建的 generateBundle 上,这正是 @afterpack/vite 一直使用的钩子。Astro 写入的是混淆后的 chunk;原始内容永远不会进入 dist/。

被混淆的范围是 Astro 生成的 JavaScript:island 打包产物和客户端脚本,通常位于 dist/_astro/ 下。永远不会变成客户端 JS 的、服务端渲染的 .astro 模板逻辑不在此范围内。

验证

astro build 运行在生产构建模式下,因此当你想查看 Protection Map 时,需要显式请求它:

integrations: [afterpack({ protectionMap: { enabled: true } })],

它会写入已被 gitignore 的 .afterpack/protectionMap.html。source map 的副作用和处理规则与 @afterpack/vite 相同。

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.

选项

每一个配置键都可以作为选项对象在这里传入,并会逐字转发给 @afterpack/vite,例如 afterpack({ preset: "hard", complexity: 40 })。类型别名 AfterpackAstroOptions 就是 AfterpackViteOptions:

integrations: [afterpack({ seed: "git", preset: "medium" })],

指令

插件会在压缩之前从你的源代码中读取指令。只支持块注释:

.astro 组件文件本身内部的指令不会被捕获。请把指令放在该 island 的 .jsx/.tsx 文件里:

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

下一步

  • 框架:完整对照表。
  • Vite:实际执行工作的插件。
  • Protection Map:如何阅读这份报告。
  • 指令:完整的标记语法。
  • 构建与 CI:种子、开发环境与生产环境的区别、提升同一份字节。