npx afterpack@latest 混淆一个构建目录。当你的打包工具没有对应插件时,或者在 CI 中,使用它。如果你的框架有插件,请改为安装 @afterpack/<fw>:这样常规构建会直接产出已混淆的输出,不需要单独的步骤。参见框架。
本页涵盖参数、示例、一次运行会做什么、verify、restore、audit、机器可读输出以及退出码。每个选项都只在配置页定义一次。
用法
afterpack [path] [--<key>=<value> ...] [-h | --help] [-v | --version]
afterpack verify [dir]
afterpack restore [dir]
afterpack audit <url>$ npx afterpack@latest路径参数可以是以下两种之一:
- 一个目录,递归遍历其中的每个
.js、.mjs和.cjs文件;或者 - 单个
.js、.mjs或.cjs文件,单独混淆。
不带路径运行时,afterpack 会检测你的构建输出:先看 package.json 中声明的打包工具按惯例写入的目录(Next.js → .next/,Nuxt → .output/,SvelteKit → build/,Electron → out/,其他受支持的打包工具均为 dist/),该目录存在就使用它;否则在 dist/、build/、out/、.output/ 和 .next/ 中选取最新的一个。它从不读取打包工具自己的配置文件,所以自定义的输出目录不会被检测到,这种情况请自行传入路径。它会打印找到了什么、将要做什么,然后开始运行;在 CI 中它从不提示交互。什么都没找到时,它会打印快速上手帮助并以 1 退出。该命令还会从 package.json 读取你的打包工具,并在运行结束时打印一行提示,指向对应的插件。
请指向打包工具的输出目录,而不是源代码。大多数打包工具(Vite、Rollup、esbuild、webpack、Astro、Parcel)写入 dist/。Next.js 写入 .next/,Nuxt 写入 .output/。
示例
npx afterpack@latest # detects the build output, light default
npx afterpack@latest dist/ # a directory you choose
npx afterpack@latest dist/ --preset=hard # heavier posture
npx afterpack@latest .next/ --seed=git # reproducible output, pinned to the commit
npx afterpack@latest build/widget.js # one file
npx afterpack@latest dist/ --diagnostics.level=all # every diagnostic, not the rolled-up summary
AFTERPACK_complexity=40 npx afterpack@latest dist/ # any flag also works as an env var一次运行会做什么
- 收集
<path>下的 JavaScript。路径是目录时递归处理,跳过它自己留下的.backup.<hash>副本,并跳过node_modules/,除非你用--paths.include='**/node_modules/**'显式包含它。什么都没收集到时,构建失败。 - 通过
.afterpack/内的一个.gitignore让该目录不进入 git,这个文件在首次需要时写入。你自己的.gitignore从不会被修改。 - 按请求的预设就地混淆每个收集到的文件;如果你显式设置了
--complexity,则按它执行。在 Next.js、Angular、Vite 或 webpack 构建上,它还会把每个内容哈希文件重命名为与其混淆后字节相符的名称,并改写对它的每一处引用;在其他输出上,文件名保持打包工具写下的样子。参见内容哈希文件名。 - 写出标志所允许的产物,并且总会写出保护回执。合并后的 Protection Map 始终落在
.afterpack/protectionMap.html,已加入 gitignore 且从不对外提供,因为它内嵌了你的原始源代码。source map 或备份同样包含你的完整源代码。两者都不要部署。若其中一个落在会对外提供的路径段之下,CLI 会警告一次。 - 打印诊断信息;如果识别出了打包工具,再打印那行插件提示。
该命令会就地覆盖你的构建输出,并且不保留可供再次混淆的副本。先构建,再运行一次。在 CI 中这个顺序是自动保证的。
不要对同一份输出运行两次
AfterPack 拒绝混淆已经带有上一次运行所产生的混淆输出的文件:它按内容哈希将收集到的文件与保护回执比对,并以 DIAG_ALREADY_OBFUSCATED 让构建失败(无论你从哪个目录运行),而不是对已混淆的代码再混淆一遍。框架对受保护输出所做的副本(例如 SvelteKit 的 build/)也会以同样的方式被拒绝。请先从源代码重新构建:删除输出目录,或运行打包工具的清理步骤。
内容哈希文件名
打包工具按 chunk 内容的哈希为它命名,例如 index-4f9a2c1e.js,这样服务器就能用 Cache-Control: immutable 把它缓存一年。混淆会改变字节,因此在下列构建上,运行会给每个内容哈希文件的名称换上新的哈希,并改写 HTML、清单和其他 chunk 中对它的每一处引用。一个文件名永远不会指向不同的字节,因此浏览器、CDN 或 service worker 永远不会在复用的名称下提供过期副本。新哈希由原哈希加上种子、引擎版本和你的设置得出,与插件为文件命名的方式相同;文件名与缓存说明了种子对缓存意味着什么。
| 构建 | CLI 指向 | 运行会重命名的内容 |
|---|---|---|
| Next.js,基于 webpack 或 Turbopack | .next/、.next/static/ 或 .next/static/chunks/;静态导出时为 out/ | static/chunks/ 下的 chunk |
| Angular application builder | dist/<app>/browser/ 或 dist/<app>/ | 每个带哈希的文件。运行还会重新计算 integrity 属性和 service worker 的 ngsw.json 哈希表 |
| Vite | 含有 index.html 或 .vite/manifest.json 的目录 | assets/ 中,或清单所列文件夹中的 name-<hash> 脚本和样式表 |
| webpack | 由 webpack 运行时加载 chunk 的目录 | 名称中带有十六进制内容哈希的文件 |
在以下情况下,内容哈希文件名保持打包工具写下的样子,运行会打印一条提示:
- 路径是单个文件,或者输出来自其他任何打包工具;
- 无法识别 Next.js 构建所用的打包工具;
- 在 Vite 或 webpack 构建上,某个带哈希的文件没有在构建目录内被引用,两个哈希无法区分,或者构建目录旁的某个文件夹(例如服务端 bundle、
.svelte-kit/或.output/)引用了这些带哈希的文件,或大到无法检查; - 某个预缓存清单(例如 Workbox 的)按 revision 列出这些文件。
要在这样的构建上使用长期缓存,请使用你所用框架的插件,或固定一个不变的 --seed。
如果 Angular 服务端构建的 angular-app-manifest.mjs 内嵌了资源哈希,运行会以退出码 1 拒绝,输出保持构建器写下的样子。请对不带 SSR 的构建的浏览器输出进行混淆。
标志
每个配置键都是一个标志:--<key>=<value>,例如 --complexity=40,列表值则写成 --identifiers.reserved=Hls,Foo。布尔标志单独出现表示 true(--build.backup),加上 =false 则关闭(--protectionMap.enabled=false)。-h, --help 和 -v, --version 打印后以 0 退出。
选项参考中的每个标量或列表键都可以这样作为标志使用。结构化的值应放在 afterpack.json 中。环境变量沿用相同的名称,只是把点换成下划线,参见配置,例如 AFTERPACK_key、AFTERPACK_preset 和 AFTERPACK_telemetry_enabled。
文件
| 路径 | 作用 |
|---|---|
afterpack.json | 配置文件。AfterPack 从工作目录向上查找,取最近的一个匹配。它承载完整的引擎配置:构建选项、key,一切都在这里。 |
.afterpack/receipts/<build dir>.json | 某个构建目录的保护回执,例如 .afterpack/receipts/dist.json 或 .afterpack/receipts/.next.json。路径过长或不寻常时,会改用带哈希的缩短文件名。它记录每个输出文件及其写入字节的哈希、引擎版本和种子。afterpack verify 据此重新计算哈希并比对,而之后对同一份未重新构建的输出再运行一次,也正是因为它而被拒绝。 |
.afterpack/protectionMap.html | 合并后的 Protection Map。自动加入 gitignore。它内嵌了你的原始源代码。绝不要提交或对外提供它。 |
.afterpack/backups/<build dir>/ | 某个构建目录的原始文件,逐字节保留,例如 .afterpack/backups/dist/,并附带一份清单,记录本次运行创建、修改、重命名或删除的每个文件。每个构建目录都有自己的备份和自己的锁,因此同一仓库中不同应用的构建从不共用备份。每次运行都会写入(--build.backup=false 可关闭),afterpack restore 读取它来撤销该次运行。运行结束时会打印它的路径。它就是你的源代码。绝不要提交或部署它。 |
<file>.js.map | 开启 sourceMap.enabled 时生成的 source map。默认关闭:除非声明为开发构建,否则每次运行都是生产模式,而生产模式的运行还会删除打包工具写出的 source map,包括 CSS map。随包发布的 source map 会把输出直接映射回你的源代码。 |
.afterpack/ 存放回执、Protection Map 和各个备份。AfterPack 从构建目录出发查找它,与你从哪个目录运行无关。它向上查找最近的、包含锁文件(package-lock.json、npm-shrinkwrap.json、pnpm-lock.yaml、yarn.lock、bun.lock 或 bun.lockb)、afterpack.json 或 .git 的文件夹。找不到时,使用最近的含有 package.json 的文件夹;仍找不到时,放在构建目录旁边。在这些文档页面中,这个文件夹就是你的项目根目录。.afterpack/ 绝不会位于构建输出之内。
在以下情况下,运行会失败,且不会重写任何文件:在构建输出中发现了 .afterpack/ 文件夹(DIAG_AFTERPACK_DIR_IN_OUTPUT:删除该文件夹后重新构建),或者无法写入回执(DIAG_RECEIPT_UNWRITABLE)。旧版本遗留在输出中的 .afterpack-protection.json 会在下一次运行时被删除。
afterpack verify [dir]
部署闸门。每次受保护的构建都会把一份保护回执写入 .afterpack/receipts/。verify 用与那次运行相同的方式为 [dir] 找到回执,因此可以在任何目录下运行。它对回执中列出的每个文件重新计算哈希,只要有一个不再匹配、回执缺失,或者回执来自另一次构建,就会失败。
npx afterpack@latest verify . # a project root: looks in ./ then ./.next/
npx afterpack@latest verify dist # or the build output directory itself
npx afterpack@latest verify build # SvelteKit's adapter copy; also .output/public, out/框架从构建输出复制出来的目录,例如 SvelteKit 的 build/、Nuxt 的 .output/public/ 或 Next 的 out/,会按被复制目录的回执来检查。verify 依据其中的文件把副本与那份回执对应起来,并检查每一个哈希,因此被改动的文件会导致失败并被点名。请把它指向部署根目录本身。此时文本输出会附加 · copy of <source dir>/。
它不读取任何引擎配置,只读取 --diagnostics.format 和 --diagnostics.level。使用 --diagnostics.format=json 时,摘要中的 receipt 字段给出它检查的回执,copiedFrom 字段给出副本所复制的目录。把它作为部署前的最后一步运行;参见构建与 CI。
如果中途停止的运行或恢复在构建目录中留下了临时的 .afterpack-tmp 文件,它也会失败。参见中途停止的运行。
afterpack restore [dir]
撤销操作。一次运行在写入任何内容之前,会先把你的原始文件复制到 .afterpack/backups/<build dir>/,并记录它将创建、修改、重命名或删除的每个文件。restore 把每个文件以原始名称和原始字节放回原处,撤销重命名和被改写的引用,并删除备份以及该次运行写下的回执。
npx afterpack@latest restore # every backup under the project root
npx afterpack@latest restore dist # only the run over dist/指向某个构建目录时,它只恢复针对该目录、其内部某个目录或包含它的那个构建的运行。指向项目根目录时,它恢复那里的所有备份。如果没有任何备份覆盖该路径,它会失败,并列出拥有备份的目录。它以与运行相同的方式从 [dir] 找到 .afterpack/,也会读取早期版本写下的 .afterpack/backup/ 文件夹。
它失败即中止:在写入任何内容之前,每个记录在案的文件都会被检查,因此运行之后被你编辑或重新构建过的文件会让恢复停止,什么都不会恢复,命令会列出该文件并以 1 退出。没有备份时它会直接拒绝。中途停止的恢复会由下一次 restore 完成。除 --diagnostics.format 外不读取任何引擎配置。
中途停止的运行
因错误而失败的运行会在退出前回滚它自己的写入。每个文件都先写入临时的 <file>.afterpack-tmp,再重命名到位,因此被强行终止的运行永远不会留下写了一半的文件。被终止的运行的备份会保持“未完成”标记,同一目录上的下一次 afterpack 运行会先检查输出:
| 构建输出中的内容 | 下一次运行 |
|---|---|
| 只有打包工具写下的内容和已停止的运行写下的内容 | 以退出码 1 失败。运行 afterpack restore,把输出恢复成打包工具写下的样子;或者从源代码重新构建后再运行。 |
| 一次重新构建,或者与打包工具写下的完全一致,没有留下已停止运行的任何内容 | 丢弃过期的备份并给出提示,然后运行。 |
| 混合状态:一些文件已重新构建,另一些仍保留已停止运行的输出 | 以退出码 1 失败并列出重新构建过的文件,restore 也会拒绝。请从源代码重新构建整个输出后再运行,或者从消息中给出的备份文件夹把其他文件的原始版本复制回去。 |
使用 --build.backup=false 时不会写入备份,因此被终止的运行无法撤销:请从源代码重新构建。AfterPack 不会强制把写入刷到磁盘,因此如果在运行或恢复过程中断电,同样请从源代码重新构建。
并行运行与锁
运行和恢复在完成之前,都会持有其构建目录的锁 .afterpack/backups/<build dir>/lock。针对不同构建目录的运行(例如同一仓库中的两个应用)可以并行。针对同一构建的第二次运行,包括在保护 .next/ 期间针对其内部目录(例如 .next/static/chunks/)的运行,会以退出码 1 失败,并指出持有锁的那次运行。如果锁所属的进程在本机上已经退出,或者锁已超过两个小时,它会被接管并给出提示。如果没有任何运行在进行而锁仍然存在,请删除消息中给出的那个文件。
afterpack audit <url>
扫描一个已部署的站点,查找泄露的密钥、暴露的源代码和未受保护的 JavaScript,并在扫描进行的同时流式输出发现的问题。它不读取任何配置,除 URL 以外不发送任何内容。完整的命令参考见 Audit。
npx afterpack@latest audit example.com机器可读输出
--diagnostics.format=json 会在 stdout 上输出且仅输出一份 JSON 文档;所有面向人阅读的行都转到 stderr。通过标志、AFTERPACK_diagnostics_format=json 环境变量,或 afterpack.json 中的 diagnostics.format 都可以启用它,verify 和 audit 同样遵守。搭配 diagnostics.level=none,运行就只打印这份文档,别无其他。CI 步骤和 AI 代理应当读取这种格式,而不是解析文本输出;该包还附带一份 SKILL.md,为直接调用 CLI 的代理记录了 CLI 的用法。
npx afterpack@latest dist/ --diagnostics.format=json --diagnostics.level=none退出码
| 代码 | 含义 |
|---|---|
0 | 成功。每个收集到的文件都已混淆并写入。--help 和 --version 也返回它。 |
1 | 失败。没有产出任何可用结果:路径不存在、路径下找不到 JavaScript、任何引擎错误、verify 失败、audit 扫描失败,或者带 Pro 密钥的构建其云端调用失败。 |
2 | 部分完成。为配额耗尽的构建预留;目前没有任何构建会返回它。 |
3 | 体积上限。在 inflation.max 的限制内无法达到复杂度目标(DIAG_SIZE_CAP_REACHED)。 |
4 | 需要 Pro。某个 regions 条目把保护级别提升到超出整个程序的配置,需要 key(DIAG_PRO_DIRECTIVE_REQUIRES_KEY)。CLI 不读取源码中的指令,因此只有 regions 会走到这个退出码。 |
5 | 反射。检测到运行时反射,却没有设置 reflection.allow(DIAG_REFLECTION_NOT_ACKNOWLEDGED)。 |
6 | 需要更新。AfterPack 云端,或当前的 afterpack,需要更新版本的 @afterpack/core。没有写出任何内容。 |
64 | 用法错误。未知的标志或命令、格式错误的值,或者重复的路径参数。 |
用 paths.exclude 把必须原样发布的文件排除在外。
解析到 key 的 Pro 构建会失败即中止。如果云端调用失败,构建就停止,而不会回退到 Free 层级的输出。4 是 Free 构建遇到需要 key 的 regions 条目时的退出码;5 是任何构建(Free 或 Pro)遇到未确认的反射模式时的退出码;2 为将来配额耗尽的构建预留,目前没有任何构建会返回它。完整约定见退出码。