诊断(DIAG_* 参考)

AfterPack 发出的每一个 DIAG_* 代码、触发它的原因、如何解决,以及构建返回的退出代码。

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 级别的保护,要么就不交付。

AfterPack fail-closed behavior on a Pro buildPro build calls the cloudrequests a cloud-side transformCloud reachableCloud unreachableretrying…Obfuscated output written↻ Retries exhaustedexponential backoff, still down✓ EXIT 0build succeeds✕ EXIT 1loud error, build failsSuccess pathcloud-obfuscated output onlyFail-closed guarantee· no Free-tier fallback· no --allow-degraded· never writes plaintextAfterPack never writes an unprotected file in place of a requested Pro one.

一旦一个 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_REACHEDInfo。文件的每个部分都达到了所请求的 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_HITInfo。仅在设置了有限 inflation.max 的非 floor 构建上触发:某个增大体积的阶段在到达目标前就停在该预算处。输出有效。从引擎 0.2.3 起,字符串常量 floor 绝不会向预算让步,因此在 floor 构建上这个代码不会触发,也绝不意味着有字符串字面量保持可读。在引擎 0.2.2 中,显式设置的 inflation.max 也可能让字符串编码中途停止;如果你设置过它,请用引擎 0.2.3 重新构建。
DIAG_SIZE_CAP_REACHEDError,退出码 3,且不写出任何输出:输出将达到你设置的 inflation.max。从引擎 0.2.3 起,最终生成的文件也会按这个上限检查,消息会给出两个字节数,并且字符串编码绝不会为了满足上限而中途停止。请调高 inflation.max、降低 complexity,或用 paths.exclude 排除该文件。
DIAG_SELF_INTEGRITY_INERTError,从引擎 0.2.3 起,仅限 Pro,且不写出任何输出:transforms.selfIntegrity.enabled 已开启,但构建中没有可供它附着的、开启了 transforms.crossDependency.enabled 的 medium 或更高级别的代码。请把预设或某个区域提高到 medium,重新开启 crossDependency,或关闭 selfIntegrity。在引擎 0.2.2 中,这样的构建会在没有完整性校验的情况下成功。
DIAG_PATH_EXCLUDEDInfo。某个文件匹配了 paths.exclude 并按字节原样交付;它的字节数被计为用户排除。
DIAG_DYNAMIC_SCOPE_NATIVEInfo,从引擎 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_INTEGRITYInfo。提供的上游 .map 未能通过针对其输入的校验,因此该文件的 source map 被丢弃,混淆后的代码照常交付。缺失 map 无害;交付错误的 map 才是此处要避免的危害。
DIAG_OUTPUT_UNPARSEABLEError,也是引擎缺陷的信号。重新解析输出失败,因此该文件失败即中止:不写出代码、source map 或保护图。
DIAG_PARSE_ERRORError。某个输入代码块解析失败。AfterPack 严格运行在构建之后,作用于已生成的 JavaScript:先编译,再将它指向 JS 输出。
DIAG_CONFIG_PARSE_ERRORError。引擎配置 的 JSON 未能反序列化。
DIAG_PRO_DIRECTIVE_REQUIRES_KEYError,退出码 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_ENVIRONMENTError。输入的嵌套深度超过了解析器能安全处理的范围,在解析前就已估算,上限为 160 层。没有输出。请减少源代码的嵌套。
DIAG_OUTPUT_NESTING_EXCEEDS_ENVIRONMENTError。混淆后的输出会嵌套超过 400 层,这是浏览器和 Worker 能安全解析的上限。没有输出。请针对该输入降低 complexity。
DIAG_ENGINE_BUG_NO_PROGRESSCritical。引擎没有任何进展。这是一个引擎缺陷。
DIAG_ENGINE_BUG_NO_APPLICABLE_TRANSFORMCritical。文件的某一部分最终低于复杂度目标,却没有任何关闭的变换或区域指令可以解释。这是一个引擎缺陷。
DIAG_ENGINE_BUG_PROCESS_THREAD_PANICCritical。某个工作线程发生 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_UNRESOLVEDInfo。某条指令所在模块的位置无法映射到构建输出中,因此未被应用。请将被标记的代码移动到一个被导入的模块中。
DIAG_DIRECTIVE_DANGLING_END存在一个 /* @afterpack end */,但其上方没有对应的起始区块。
DIAG_DIRECTIVE_REGION_TO_FILEidentifiers.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_KEYkey 缺失、格式错误或未知(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_ACKNOWLEDGEDInfo。该模式被检测到并被保留,因为你已经确认过它。每种模式对应一个代码;中间的名称可以是上面任意一种模式。
DIAG_REFLECTION_NOT_ACKNOWLEDGEDError,退出码 5。检测到某个反射模式但没有被确认,因此构建会停止,而不是交付一段运行时读取会出错的代码;消息会指明该模式。可以用 reflection.allow 确认它——它也接受 angular 和 nestjs——或者用 paths.exclude 把该文件排除在外。
散落的产物会被 audit 捕获

将一个散落的 .map 文件或一个未混淆的同名文件交付进你的部署目录,会被 npx afterpack@latest audit 捕获,而不是被构建本身捕获。

如果某个代码缺失或有误,请提交一个 issue:github.com/afterpack-dev/afterpack/issues。

使用 --diagnostics.level=all 运行,可以在任意构建上获得完整的诊断流,而不是默认的摘要。在插件构建中,同样的开关是 diagnostics.level 选项,或 AFTERPACK_diagnostics_level。

下一步

  • CLI 参考:上方代码所引用的每一个标志。
  • 配置:这些代码所引用的配置键。
  • 语义契约:输出保持不变的部分,以及少数有意差异。
  • 指令:上方指令代码背后完整的 @afterpack 语法。
  • 最佳实践:从一开始就避免这些诊断的模式。
  • 套餐:什么是免费的,什么需要一个 key。