包与依赖关系
XiHan.UI 是一个 pnpm workspace。packages/*/* 是对外发布的库包(按角色分 adapters / design / features / engine 四组),tooling/* 是内部工具(永不发布),apps/* 是两个 playground。
全部库包
| 包 | 版本 | 依赖 | peer 依赖 | 层 |
|---|---|---|---|---|
@xihan-ui/kernel | 1.0.0-alpha.0 | — | — | 1 |
@xihan-ui/machine | 1.0.0-alpha.0 | kernel | — | 1 |
@xihan-ui/tokens | 1.0.0-alpha.0 | — | — | 1 |
@xihan-ui/icons | 1.0.0-alpha.0 | — | — | 1 |
@xihan-ui/behavior | 1.0.0-alpha.0 | kernel | — | 2 |
@xihan-ui/position | 1.0.0-alpha.0 | kernel | — | 2 |
@xihan-ui/code-highlight | 1.0.0-alpha.0 | kernel | — | 2 |
@xihan-ui/chat-stream | 1.0.0-alpha.0 | kernel | — | 2 |
@xihan-ui/markdown | 1.0.0-alpha.0 | — | — | 2 |
@xihan-ui/headless | 1.0.0-alpha.0 | kernel machine behavior + @internationalized/date | — | 3 |
@xihan-ui/styles | 1.0.0-alpha.0 | tokens(只取其 CSS 产物) | — | 3 |
@xihan-ui/backgrounds | 1.0.0-alpha.0 | kernel behavior | — | 3 |
@xihan-ui/vue | 1.0.0-alpha.0 | kernel machine behavior headless position code-highlight | vue、backgrounds(可选) | 4 |
@xihan-ui/web-components | 1.0.0-alpha.0 | kernel machine behavior headless position code-highlight | backgrounds(可选) | 4 |
版本状态
14 个包都已发布到 npm,版本 1.0.0-alpha.0,latest 与 alpha 两个 dist-tag 都指向它。这是 alpha 预发布:不承诺语义化版本,接口还会变,不要用于生产。
发布走 changesets,所有库包同属一个 fixed 版本组,一起升到同一个版本号。
依赖图
箭头方向 = 依赖方向,只画实际声明的依赖。同层之间除 machine → kernel 外无横向依赖。
层 4 ┌─────────────┐ ┌──────────────────┐
│ vue │ │ web-components │ peer: backgrounds(可选);vue 另有 peer: vue
└──────┬──────┘ └────────┬─────────┘
│ kernel · machine · behavior · headless · position · code-highlight
└───────────┬───────────┘
▼
层 3 ┌──────────────┐ ┌─────────────┐ ┌────────────────┐
│ headless │ │ backgrounds │ │ styles(纯CSS)│
└──────┬───────┘ └──────┬──────┘ └───────┬────────┘
kernel·machine·behavior kernel·behavior tokens 的 CSS 产物
+ @internationalized/date
│ │
▼ ▼
层 2 ┌──────────┐ ┌──────────┐ ┌────────────────┐ ┌─────────────┐ ┌──────────┐
│ behavior │ │ position │ │ code-highlight │ │ chat-stream │ │ markdown │
└────┬─────┘ └────┬─────┘ └───────┬────────┘ └──────┬──────┘ └──────────┘
└────────────┴───────────────┴─────────────────┘ 无依赖
▼
层 1 ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────┐
│ kernel │◄─┤ machine │ │ tokens │ │ icons │
└──────────┘ └──────────┘ └──────────┘ └───────┘
零运行时依赖 零运行时依赖 无依赖 无依赖三个包完全独立、可以单独用:
@xihan-ui/tokens——只要设计令牌与主题运行时,不要组件;@xihan-ui/markdown——只要流式 Markdown 渲染内核;@xihan-ui/styles——纯 CSS,它对tokens的依赖只是为了@import令牌产物,不引入任何 JS。
依赖规则
分层拓扑写在 tooling/eslint-config/src/layers.json 里,由 dependency-cruiser 在 pnpm boundaries 时强制。层级越低越基础,只能依赖 canDependOn 列出的包。
除分层外还有四条规则:
| 规则 | 内容 |
|---|---|
no-circular | 禁止循环依赖 |
no-unresolvable | 解析不出来的 import——最常见的成因正是「伸手够了邻层却没在 package.json 里声明依赖」 |
styles-no-js-deps | styles 是纯 CSS,不得依赖任何 JS 包 |
no-external-in-packages | 库包的运行时代码不得引第三方 |
第三方运行时依赖
全库只有一个:@internationalized/date,只在 @xihan-ui/headless 的日期族里用(零框架的纯数据包,无副作用)。
以下东西都是自研的,不引第三方:
| 能力 | 包 | 常见的第三方选择 |
|---|---|---|
| 浮层定位 | position | Floating UI |
| 代码着色 | code-highlight | Shiki / Prism |
| Web Components 响应式基类 | web-components | Lit |
| Markdown 渲染 | markdown | markdown-it / marked |
| 状态机 | machine | XState |
要新增第三方运行时依赖,必须逐条登记进白名单并写明理由与摘除条件,check-runtime-deps 门禁盯着这件事。
开发期第三方是另一回事
@lit/reactive-element 与 commonmark-spec 出现在 workspace catalog 里,但它们只供测试对拍——前者用于差分校验自研响应式基类,后者用于读 CommonMark 官方用例。两者都不进任何包的运行时依赖。
版本约定
- 内部运行时依赖一律
workspace:^(发布时展开为^1.0.0-alpha.0区间),内部开发期依赖用workspace:*; - 第三方版本只从 workspace catalog 取,包内写
catalog:,不得内联版本号(check-exact-pins门禁); - 升级只改
pnpm-workspace.yaml的 catalog 一处。
产物契约
| 项 | 值 |
|---|---|
| 模块格式 | ESM only,不提供 CJS |
| Node | >= 18 |
| 类型 | 每个入口一份 .d.ts |
| 副作用 | 全部 sideEffects: false,styles 与 tokens/tokens.css 除外 |
pnpm gate:publish 逐包跑 publint 与 attw,按 ESM-only 的支持面校验 exports 条件与类型解析(node16-from-ESM 与 bundler 两列)。
子路径导出
大多数包只有主入口。有子路径的:
| 包 | 子路径 |
|---|---|
@xihan-ui/tokens | ./runtime ./tokens.css ./tokens.json |
@xihan-ui/machine | ./vanilla |
@xihan-ui/behavior | ./presence |
@xihan-ui/vue | ./backgrounds |
@xihan-ui/web-components | ./define ./backgrounds ./custom-elements.json |
@xihan-ui/styles | 每份皮肤一条 CSS,共 108 条,另有 ./index.css 与 ./index.unlayered.css 两个整包入口 |
组件没有单独的子路径导出——按需引入靠 tree-shaking,不靠手写路径。
