构建过了不代表 Nitro 能启动:给 server bundle 补运行时 import 检查
升级 next-auth 后,TypeScript 没有提示错误,@sidebase/nuxt-auth 却仍在引用旧入口 next-auth/core。构建产物里保留的这类 import,要到 Node 解析时才可能失败。为此增加一条构建后检查:读取 Nitro server bundle,让 Node 按产物目录的条件解析裸包入口。
构建通过不等于 Node 能解析
Nuxt / Nitro 的构建链路会做很多事,但它不等于真的在生产目录下启动一次 Node server。某些裸包 import 可能会被保留到 .output/server 里,直到运行 node .output/server/index.mjs 时,Node resolver 才按 package exports 和当前目录的 node_modules 去解析。
这时常见失败是:
ERR_PACKAGE_PATH_NOT_EXPORTED
它的意思很直接:代码 import 了某个包的子路径,但这个子路径没有被包的 exports 声明公开。安全升级、minor 升级、wrapper 包滞后升级,都可能把这种问题带出来。
TypeScript 没报错也不奇怪。类型声明可能还在,构建器可能只是把 import 留在产物里,或者 dev server 的 alias / optimize 行为和 Nitro server bundle 不完全一样。真正有权判定运行时能不能解析的,是 Node 在产物目录下的 resolver。
从产物中提取运行时 import
这个工具不从源码里猜风险,而是等构建完成后扫 .output/server。它会读取 .js、.mjs、.cjs 文件,提取几类运行时 import:
import x from 'pkg/subpath'import 'pkg/subpath'export { x } from 'pkg/subpath'await import('pkg/subpath')require('pkg/subpath')
然后它只保留运行时会交给 Node 解析的裸包 specifier。相对路径、绝对路径、node:、file:、virtual:、nitro:、#internal 都会跳过,因为它们不属于 package exports 这一类问题。
最后一步直接在 .output/server 目录下启动一个 Node 子进程,逐个执行 import.meta.resolve(specifier)。这样检查条件更接近生产启动时的 Node ESM 解析,也避开了开发目录里的偶然结果。
失败信息要能直接定位
这类检查如果只报一句「有包解析失败」,基本没法用。源码包里把失败格式化成三段:
- 失败的 specifier。
- Node resolver 给出的错误码和错误信息。
- 产物里引用它的文件列表。
这样处理起来会更直接:如果是 wrapper 包继续引用旧入口,就看能不能升级 wrapper;如果上游短期没有修复,再决定是否用 alias 或兼容入口兜住;如果是项目自己 import 了私有子路径,就改回公开 API。
这条检查不能替代真实 smoke test。当前脚本统一从产物根目录按 ESM 条件解析:它能提前发现这一条件下的 exports 错误,但不完全等同于每个文件的真实加载环境。require() 可能走不同的条件导出,嵌套目录也可能解析到不同依赖版本。
此外,import.meta.resolve 返回的是解析后的 URL,不会执行模块,也不保证所有目标文件都存在。需要更完整的检查时,应分别处理 ESM / CommonJS、实际 importer 路径和目标文件存在性;启动时的初始化错误,仍由隔离环境中的 smoke test 负责。
把检查接进构建后的门禁
完整实现与接入示例见完整源码与接入方法弹窗。
迁移后建议把它接到构建验证链路里,而不是只作为手工命令保留。比如先跑 pnpm build 生成 .output/server,再跑 pnpm verify:nitro-runtime-imports。如果项目有轻量的测试构建命令,也可以挂在那里,避免每次都走完整发布构建。
解析检查通过后,仍要在隔离环境启动服务。前一步能提前报告当前 ESM 条件下的入口解析失败,后一步才会执行模块并暴露初始化问题,两项验证不能互相替代。
Nitro 运行时 import 检查源码
接入时不需要把检查逻辑散进业务代码,直接把源码包里的脚本和测试迁进去,再挂到构建后的验证命令里就够了。
请把 Nitro Runtime Import Guardrail 接入当前项目。
工具包根路径:https://shengsheng.fun/files/nitro-runtime-import-bundle-check/kits/nitro-runtime-import-guardrail/
先读 README.md、MANIFEST.json、FILES.json、CHANGELOG.md、AGENT_PROMPT.md,再按 FILES.json 读取源码;迁移 copy/scripts/ 和 copy/tests/,在构建后运行 verify:nitro-runtime-imports。