【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
上篇 blog
【Agent】【OpenCode】项目配置(undefined)
分析了类型的环境控制,比如 lib 引入了 ES 标准 + DOM 类型,types 显式清空自动加载的全局类型,并详细分析了严格性调整,noUncheckedIndexedAccess 为 false 关闭了索引访问时,自动添加 undefined 的严格检查,noUncheckedIndexedAccess 是个 TypeScript 的严格类型检查选项,其核心作用是,当通过索引 key 访问对象或数组时,TypeScript 是否应该假设这个值不存在,其默认是关闭的,因为如果开启后,所有的索引访问都会带上 | undefined,代码会变得非常啰嗦,在 OpenCode 项目里,Effect 生态有自己的安全机制,不需要 noUncheckedIndexedAccess 来兜底,另外,TUI 场景会大量使用字典映射,这些映射通常是内部可控的,每次都判空会显得代码很臃肿,最后总结,这个选项是全局开关,无法针对单个访问精细化控制,开启更安全但更啰嗦,关闭更简洁但有运行时风险,下面继续分析
OpenCode
上篇 blog 分析了 lib,types 和 noUncheckedIndexedAccess,下面继续分析

- 模块配置解析
javascript
"customConditions": ["browser"]
这里相当于在提醒 Bun 和 TS,在解析包的 package.json 的 exports 配置项时,优先匹配 browser 配置 ,即使这是终端应用,很多同构库(比如 Effect,Solid)的 browser 导出 export 往往包含更完整的 ESM 实现,而 node 的导出可能是 CJS 兼容层,这里指定 browser 为条件可以获得更好的 Tree-shaking 和类型推导
上面提到的 customConditions 涉及到 Node.js/Bun 生态中一个底层的模块解析机制,这里有几个关键点
- 首先是
package.json的exports字段,exports字段像个路由表,告诉运行时,当有模块 import 该包时,根据对方环境来提供不同的文件 ,举个例子,一个同构库(浏览器和服务器都能用)的package.json可能长这样
javascript
{
"name": "some-lib",
"exports": {
".": {
"browser": "./dist/esm/index.mjs", // 👈 浏览器环境走这里
"node": "./dist/cjs/index.cjs", // 👈 Node.js 环境走这里
"default": "./dist/esm/index.mjs" // 👈 兜底
}
}
}
当别的模块导入该包时,比如 import { foo } from "some-lib" 时,Bun/Node 不会直接去读 main 字段,而是先查这个 exports 路由表,根据当前环境的条件标签来决定加载哪个文件
- 接着是
customConditions,Bun 默认会根据自身运行环境,自动激活一些条件标签 ,比如bun,node,import等,这里customConditions指定了browser,意思就是额外手动激活browser这个条件标签 ,效果就是,即使在终端(非浏览器)里运行 Bun,Bun 在查路由表时,也会优先匹配browser,而不是node
这里有个点,就是 OpenCode 终端应用在伪装成浏览器,直觉上 TUI 是终端应用,应该走 node 分支,但实际上
| 导出分支 | 格式 | 问题 |
|---|---|---|
node |
往往是 CJS(require/module.exports) | 为了兼容老版 Node,很多库的 node 分支仍然是 CJS,CJS 无法被 Tree-shaking,类型推导也较差 |
browser |
几乎总是 ESM(import/export) | 浏览器只认 ESM,所以这个分支永远是现代化的纯 ESM,Tree-shaking 友好,TS 类型推导也更准确 |
总之,很多库的 node 分支是为了向后兼容而保留的 CJS 遗留物,而 browser 分支反而是最新,最干净的 ESM 实现,而 Bun 原生支持 ESM,根本不需要 CJS 兼容层,所以直接走 browser 分支反而能获得更好的代码质量和开发体验 ,所以 customConditions 指定 browser 表示虽然应用在终端运行,但也要提供 ESM 版本的代码导入,别用 CJS,这是种绕过历史包袱,强制获取现代化导出的实用技巧,在 Bun 等原生 ESM 运行时中非常常见
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog