AfterPack 绝不会交付比你要求的更弱的保护。当某个问题无法自动解决时,构建会带着一个具名的 DIAG_* 代码和具体的修复方法停止。
严重级别
AfterPack 发出的每一条诊断都恰好属于三种严重级别之一。不存在 Warning 级别。
| 严重级别 | 含义 | 会发生什么 |
|---|---|---|
| Info | 预期行为:你已经确认过这一点,或引擎采取了一种安全、保持正确性的回退方案。 | 构建成功,输出已写入。 |
| Error | 在不改动你这一端的情况下,无法满足你声明的要求(过紧的体积上限、解析错误、无效的密钥)。 | 非零退出,不写入输出。 |
| Critical | 引擎自身的缺陷。你这一端的任何操作都无法修复它。 | 非零退出,不写入输出,始终如此。提交报告。 |
退出代码
npx afterpack@latest 会返回八种代码中的一种。可以直接在脚本中对它们做判断。
| 代码 | 含义 |
|---|---|
0 | 成功。所有被收集的文件都已混淆并写入。--help 和 --version 也以 0 退出。 |
1 | 失败。没有产生可用的输出,stderr 上的一条消息会说明发生了什么。 |
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 | 用法错误。未知的标志或命令、格式错误的值,或重复的路径参数。 |
什么会让构建以 64 退出
用法问题会在运行开始之前就将其终止。
- 未知的标志、未知的命令、格式错误的值,或多于一个的位置路径参数。用法说明会与错误一起重新打印。
什么会让构建以 1 退出
- 没有
<path>参数,也没有找到构建输出(dist/、build/、out/、.output/、.next/,或package.json中所命名打包工具的惯例目录)。AfterPack 从不读取打包工具自身的配置文件,因此自定义的输出目录无法通过这种方式被检测到。 - 构建目录不存在,或不包含任何
.js/.mjs/.cjs文件。空的构建目录会导致构建失败。常见原因是指向了错误的目录,或在构建之前就运行了它。 - 某个文件彻底失败,或在输入非空的情况下返回了空输出。
- 引擎无法安全混淆的文件。构建会失败并列出受影响的文件。使用
paths.exclude来划出一个必须原样交付的文件。 verify失败,或audit扫描失败。- 下方任何带有
Error或Critical严重级别的代码,DIAG_SIZE_CAP_REACHED(退出码3)、DIAG_PRO_DIRECTIVE_REQUIRES_KEY(退出码4)和DIAG_REFLECTION_NOT_ACKNOWLEDGED(退出码5)除外。
失败即中止,没有例外
一次 Pro 构建要么交付 Pro 级别的保护,要么就不交付。
一旦一个 key 从 AFTERPACK_key 或 afterpack.json 解析出来,这次构建就是一次云端构建。如果云端调用因任何原因失败,引擎会返回一个错误,构建以 1 退出;如果云端需要更新版本的 @afterpack/core,则以 6 退出。它不会回退到本地构建,也不会发出未混淆的输出。没有任何标志能交付未混淆的输出。让构建停下来,是为了把问题摆在你能看见的地方。
这一约定不会因调用路径而改变:npx afterpack@latest 与插件在打包结束时的自动运行表现完全相同。
CI 中的退出代码
npx afterpack@latest dist/ || {
echo "AfterPack failed: nothing shipped. See the error above."
exit 1
}非零退出会终止流水线。原因会出现在 stderr 上,以及下方的代码中。
完整参考
引擎
| 代码 | 含义 |
|---|---|
DIAG_TARGET_REACHED | Info。文件的每个部分都达到了所请求的 complexity。 |
DIAG_TARGET_NOT_REACHED | 从引擎 0.2.3 起为 Error,且不写出任何输出:文件的某一部分无法在你允许的变换下达到 complexity 目标,原因是 transforms.scopeDeepen.enabled 为 false,或者某条区域指令限制了这些行。消息会指出文件和行号。请重新开启该变换,为该文件或区域降低 complexity,或用 paths.exclude 排除该文件。在引擎 0.2.2 中它是 Info:构建会交付层级更浅的输出,而由关闭的变换造成的不足可能被报告为 DIAG_TARGET_REACHED。 |
DIAG_INFLATION_BUDGET_HIT | Info。仅在设置了有限 inflation.max 的非 floor 构建上触发:某个增大体积的阶段在到达目标前就停在该预算处。输出有效。从引擎 0.2.3 起,字符串常量 floor 绝不会向预算让步,因此在 floor 构建上这个代码不会触发,也绝不意味着有字符串字面量保持可读。在引擎 0.2.2 中,显式设置的 inflation.max 也可能让字符串编码中途停止;如果你设置过它,请用引擎 0.2.3 重新构建。 |
DIAG_SIZE_CAP_REACHED | Error,退出码 3,且不写出任何输出:输出将达到你设置的 inflation.max。从引擎 0.2.3 起,最终生成的文件也会按这个上限检查,消息会给出两个字节数,并且字符串编码绝不会为了满足上限而中途停止。请调高 inflation.max、降低 complexity,或用 paths.exclude 排除该文件。 |
DIAG_SELF_INTEGRITY_INERT | Error,从引擎 0.2.3 起,仅限 Pro,且不写出任何输出:transforms.selfIntegrity.enabled 已开启,但构建中没有可供它附着的、开启了 transforms.crossDependency.enabled 的 medium 或更高级别的代码。请把预设或某个区域提高到 medium,重新开启 crossDependency,或关闭 selfIntegrity。在引擎 0.2.2 中,这样的构建会在没有完整性校验的情况下成功。 |
DIAG_PATH_EXCLUDED | Info。某个文件匹配了 paths.exclude 并按字节原样交付;它的字节数被计为用户排除。 |
DIAG_DYNAMIC_SCOPE_NATIVE | Info,从引擎 0.2.3 起。with 语句或直接 eval 让其周围的作用域保持原样:从它向外直到文件顶层的每一层作用域中,名称都保留原来的写法,with 语句体内的全局变量也保持原样。文件的其余部分照常混淆。要保护这些名称,如果被求值的代码不需要局部变量,就用间接方式调用 eval,即 (0, eval)(code),或者把需要 eval 或 with 的代码移到单独的文件中。参见语义契约。 |
DIAG_SURVIVING_READABLE_LITERAL | 残留的可读字符串字面量数量超过了 strings.leaks.max 允许的上限。可以调高 complexity、降低 strings.minLength,或调高该上限。 |
DIAG_SOURCE_MAP_INTEGRITY | Info。提供的上游 .map 未能通过针对其输入的校验,因此该文件的 source map 被丢弃,混淆后的代码照常交付。缺失 map 无害;交付错误的 map 才是此处要避免的危害。 |
DIAG_OUTPUT_UNPARSEABLE | Error,也是引擎缺陷的信号。重新解析输出失败,因此该文件失败即中止:不写出代码、source map 或保护图。 |
DIAG_PARSE_ERROR | Error。某个输入代码块解析失败。AfterPack 严格运行在构建之后,作用于已生成的 JavaScript:先编译,再将它指向 JS 输出。 |
DIAG_CONFIG_PARSE_ERROR | Error。引擎配置 的 JSON 未能反序列化。 |
DIAG_PRO_DIRECTIVE_REQUIRES_KEY | Error,退出码 4,且不写出任何输出:Free 构建遇到了一条把保护提高到程序配置之上的区域指令。请设置 key,或删除该指令。从引擎 0.2.3 起,只降低保护的指令在 Free 上会被忽略;引擎 0.2.2 也会拒绝这类指令。 |
DIAG_INPUT_TOO_LARGE | 单个输入超过了体积上限。 |
DIAG_BATCH_TOO_LARGE | 一个批次的总体积超过 500 MB(413,不可重试);构建以失败即中止的方式停止。 |
DIAG_INPUT_NESTING_EXCEEDS_ENVIRONMENT | Error。输入的嵌套深度超过了解析器能安全处理的范围,在解析前就已估算,上限为 160 层。没有输出。请减少源代码的嵌套。 |
DIAG_OUTPUT_NESTING_EXCEEDS_ENVIRONMENT | Error。混淆后的输出会嵌套超过 400 层,这是浏览器和 Worker 能安全解析的上限。没有输出。请针对该输入降低 complexity。 |
DIAG_ENGINE_BUG_NO_PROGRESS | Critical。引擎没有任何进展。这是一个引擎缺陷。 |
DIAG_ENGINE_BUG_NO_APPLICABLE_TRANSFORM | Critical。文件的某一部分最终低于复杂度目标,却没有任何关闭的变换或区域指令可以解释。这是一个引擎缺陷。 |
DIAG_ENGINE_BUG_PROCESS_THREAD_PANIC | Critical。某个工作线程发生 panic。这是一个引擎缺陷。 |
构建插件 / CLI
| 代码 | 含义 |
|---|---|
DIAG_DIRECTIVE_UNKNOWN | 一条 @afterpack 指令命名了一个无法识别的键。请对照哪些键可以被某个区域覆盖来检查它。 |
DIAG_DIRECTIVE_UNSUPPORTED_FORM | 该标记形式无法放置(// @afterpack … 行注释,或 @afterpack:begin/:end 区间)。请使用 /* @afterpack … */ 块标记。 |
DIAG_DIRECTIVE_NOT_IMPLEMENTED | 一个仅限 program 作用域的键被写进了指令中(例如 reflection.allow),或者是一条多余的指令,如 skip=false。关于一个区域实际能覆盖什么,请参阅哪些键。 |
DIAG_DIRECTIVE_SOURCE_UNRESOLVED | Info。某条指令所在模块的位置无法映射到构建输出中,因此未被应用。请将被标记的代码移动到一个被导入的模块中。 |
DIAG_DIRECTIVE_DANGLING_END | 存在一个 /* @afterpack end */,但其上方没有对应的起始区块。 |
DIAG_DIRECTIVE_REGION_TO_FILE | identifiers.globals.rename 无法被限定在某个区域内,因此命名了它的指令会被忽略并报告。 |
DIAG_DIRECTIVE_COVERAGE_UNVERIFIED | 一个携带指令的打包代码块没有可解码的 source map,因此被标记的区域无法在其字节中被定位。 |
DIAG_DIRECTIVE_TARGET_ELIMINATED | 指令在代码块的 map 中被定位到了,但匹配到零个范围。被标记的代码已被摇树优化移除,或者坐标没有对齐。 |
DIAG_ALREADY_OBFUSCATED | 被收集的文件已经携带了上一次运行留下的 AfterPack 自身的混淆输出,通过内容哈希与保护回执匹配得出。AfterPack 的输出不是幂等的——请先从源代码重新构建。 |
DIAG_AFTERPACK_DIR_IN_OUTPUT | 错误,且不会重写任何文件。构建输出中有一个 .afterpack/ 文件夹,其中的备份、回执和 Protection Map 会随构建一起发布。删除它并重新构建。参见.afterpack/ 所在的位置。 |
DIAG_RECEIPT_UNWRITABLE | 错误,且不会重写任何文件。无法把保护回执写入 .afterpack/receipts/。让该文件夹可写,然后重新构建。 |
云端 API
| 代码 | 含义 |
|---|---|
DIAG_INVALID_KEY | key 缺失、格式错误或未知(401)。请检查 AFTERPACK_key,或从控制台签发一个新密钥。 |
DIAG_KEY_REVOKED | 该 API 密钥已被撤销(401)。 |
DIAG_KEY_EXPIRED | 该 API 密钥已过期(401)。 |
DIAG_KEY_OUT_OF_SCOPE | 请求的 IP 不在该密钥允许的范围内(403)。 |
DIAG_MISSING_IP | 该请求未携带客户端 IP(400)。 |
DIAG_INVALID_MANIFEST | 格式错误的 multipart 请求体,或缺少 batch 清单部分。 |
DIAG_FILE_TOO_LARGE | 单个文件超过了单文件字节上限(413)。可以拆分它,或用 paths.exclude 排除它。 |
DIAG_ENTITLEMENT_LAPSED | 订阅付款已逾期(402);更新账单信息以继续构建。 |
DIAG_QUOTA_EXHAUSTED | 构建的总字节数超过了剩余额度,或达到了超量花费上限。每次构建只检查一次,且在任何文件分发之前,因此不会有已受保护的内容被降级。可以充值、升级,或调高上限;错误信息中会带有相应链接。 |
DIAG_RATE_LIMITED | 超出速率限制(429);请遵守 Retry-After。 |
DIAG_RATE_CHECK_UNAVAILABLE | 速率限制检查本身失败,因此请求以失败即中止的方式停止(503);请重试。 |
DIAG_BUILD_DEADLINE | 在所有文件都开始处理之前,实际耗时预算已用尽;未完成的文件会被列出。 |
DIAG_ENGINE_FAILURE | 云端引擎在某个文件上失败。 |
运行时反射
AfterPack 会检测以下运行时反射模式:functionToString、nameIntrospection、argumentsCallee、prototypeChain、decoratorMetadata 和 constructorIntrospection。每一次检测都携带一个代码:已确认的形式在代码中带有模式名称,未确认的形式是单一代码,其消息会指明该模式。
| 代码 | 含义 |
|---|---|
DIAG_REFLECTION_nameIntrospection_ACKNOWLEDGED | Info。该模式被检测到并被保留,因为你已经确认过它。每种模式对应一个代码;中间的名称可以是上面任意一种模式。 |
DIAG_REFLECTION_NOT_ACKNOWLEDGED | Error,退出码 5。检测到某个反射模式但没有被确认,因此构建会停止,而不是交付一段运行时读取会出错的代码;消息会指明该模式。可以用 reflection.allow 确认它——它也接受 angular 和 nestjs——或者用 paths.exclude 把该文件排除在外。 |
将一个散落的 .map 文件或一个未混淆的同名文件交付进你的部署目录,会被 npx afterpack@latest audit 捕获,而不是被构建本身捕获。
如果某个代码缺失或有误,请提交一个 issue:github.com/afterpack-dev/afterpack/issues。
使用 --diagnostics.level=all 运行,可以在任意构建上获得完整的诊断流,而不是默认的摘要。在插件构建中,同样的开关是 diagnostics.level 选项,或 AFTERPACK_diagnostics_level。