打包
本文介绍 clientx build 的构建流程、dist/ 产物结构,以及单资源 vs 组合(依赖闭包)两种打包方式。
1. clientx build
CLI 入口:
node scripts/clientx.mjs build # 默认组合 client+userx+msgx+agix+conn+e2ee
node scripts/clientx.mjs build --resources sitex # 只打 sitex(闭包 = sitex)
node scripts/clientx.mjs build --resources hello,client # 自研资源 + 其依赖
node scripts/clientx.mjs build --no-clean # 不清空 dist(增量;默认每次全新构建保证确定性)
node scripts/clientx.mjs --help # 查看帮助
--resources 给出的是你想要启用的资源 id 列表;构建器先做传递依赖闭包解析(resolve-closure.mjs),把依赖一起带上,按拓扑序(依赖先构建)逐个产出。
2. 构建流程
五步编排(scripts/clientx.mjs 逐个子构建器执行,任一步非零退出码立即失败):
- shared-vendor →
dist/assets/shared-vendor.js:react家族(react / react-dom / react-router / react-query / zustand / sonner / next-themes)平铺打包,保证全应用单实例。 - vendor-i18n →
dist/assets/vendor-i18n.js:i18next+react-i18next单实例(i18next 的 default 与 React 冲突,单独一份)。 - vendor-packages →
dist/vendor/{runtime,common}/**:@clientx两包以 preserveModules 逐模块产出,供 importmap 精确子路径解析。 - build-resource →
dist/resource/<id>/**:闭包内每资源独立构建,依赖资源先构建;资源代码保持 bare import(react/@clientx/.../@/resource/<dep>/...),由 importmap 解析。 - build-app →
dist/index.html+dist/resource/{manifest,importmap}.json:构建 core-app(框架 external 保持 bare),生成闭包清单与 importmap,并把 importmap 内联进index.html的<head>。
3. 产物结构
dist/
├── index.html # core-app 入口(内联 <script type="importmap">)
├── assets/
│ ├── shared-vendor.js # react 家族单实例
│ ├── vendor-i18n.js # i18n 家族单实例
│ └── ... # core-app 其余 chunk
├── vendor/
│ ├── runtime/** # @clientx/runtime 逐模块产物
│ └── common/** # @clientx/common 逐模块产物
├── resource/
│ ├── manifest.json # 闭包清单:{ resources: [{ id, entry, deps }] }
│ ├── importmap.json # bare specifier → URL 映射
│ ├── <id>/register.js # 每资源入口(loader 动态 import)
│ ├── <id>/** # 资源内部模块
│ └── <id>/i18n/zh.json # 语言包
└── downloads/** # 仅 portal 在闭包时:拷贝 resource/portal/downloads
manifest.json 是运行时加载的唯一真相(见「组装」页):
{
"resources": [
{ "id": "sitex", "entry": "/resource/sitex/register.js", "deps": [] },
{ "id": "portal", "entry": "/resource/portal/register.js", "deps": ["sitex"] }
]
}
注意:manifest 里的 deps 是资源 id(构建期闭包解析的结果),不是 Resource.dependsOn 的类型/kind(如 site)。portal 的声明依赖 dependsOn: ['site'] 是 kind 级约束;构建期闭包 deps 由源码 import(@/resource/sitex/...)推导,实际是 ["sitex"]。
importmap.json 是单实例保证的关键:react 家族精确映射到 shared-vendor.js,@clientx/runtime|common 的每个子路径映射到 vendor/<pkg>/<sub>.js,跨资源子路径映射到 /resource/<dep>/<sub>.js。
4. 单资源 vs 组合(依赖闭包)
单资源
node scripts/clientx.mjs build --resources sitex
闭包 = sitex(+ 它 import 的 @clientx 包,那些进 vendor/)。dist/resource/ 只有 sitex/。适合「只想要一个可插拔单元」或验证资源独立成立。
node scripts/clientx.mjs build --resources hello,client
自研资源 hello 声明依赖 client,闭包 = [client, hello](依赖先构建)。缺依赖的组合会显式失败:build-app 校验 App 帧静态依赖的资源必须在闭包内(sitex-only 等组合 fail loud,不静默产坏 bundle)。
组合(默认应用 / 官网)
node scripts/clientx.mjs build # 默认应用: client+userx+msgx+agix+conn+e2ee
node scripts/clientx.mjs build --resources client,sitex,portal # 官网: 应用壳 + 站点框架 + 官网站点
要点:
- 资源级独立成立:
--resources portal的闭包 =[sitex, portal](sitex是portal的声明依赖),不依赖client。官网的营销首页、下载页与版本日志都只依赖 sitex 的内容模型与块注册表。 - 全量官网需要 client:
build-app的 App 帧静态依赖@/resource/client/*(P3b 帧解耦前),所以全站构建要用含client的组合。 - 可选资源不进默认构建:
sitex/portal不在DEFAULT里,不显式列出就不打包、不声明、不下载。
5. 下载产物
scripts/p3/package-downloads.mjs 把可分发产物打到 resource/portal/downloads/(构建期拷贝进 dist/downloads/,经 /downloads/ 下载):
node scripts/p3/package-downloads.mjs
clientx-core-prebuild.tar.gz—— 先跑一次默认clientx build,打包整个dist/。clientx-core-source.tar.gz—— 工具链源码。resource-<id>-source.tar.gz—— 内置资源源码(自动发现:src/resource/下含register.ts的目录,删资源即删包)。resource-hello-example.tar.gz——clientx create hello --deps client生成的案例源码。downloads-manifest.json—— 下载页卡的文件/大小数据源。downloads-meta.json—— 每产物的标题/描述(SEO),下载卡标题/描述读它。
6. 下一步
- 「组装」页:core 运行时如何从 manifest 加载,
clientx run如何开发。 - 「案例」页:
hello资源从源码到打包的完整流程。