Skyroc Admin Docs
工程化

Exports 策略

package.json exports / 子入口 / 平台分支的统一规范

为什么严肃对待 exports

package.jsonexports 字段决定:

  • 下游 import '@skyroc/utils/storage' 能否解析;
  • TypeScript 能否找到 .d.ts
  • bundler tree-shaking 的边界;
  • ESM / CJS / 浏览器 / RN 平台路由。

本仓库的所有发布包都遵循一份统一的 exports 模板,避免下游用法分裂。

单入口模板

{
  "type": "module",
  "main":  "./dist/index.mjs",
  "types": "./dist/index.d.mts",
  "exports": {
    ".": {
      "types":   "./dist/index.d.mts",
      "default": "./dist/index.mjs"
    },
    "./package.json": "./package.json"
  }
}

要点:

  • type: "module" 全仓统一;
  • types / default 顺序固定(types 必须在最前,Node 16+ 要求);
  • 暴露 ./package.json 便于工具读取版本 / 依赖;
  • 不导出 require 字段(不发 CJS)。

多入口(子路径)模板

库提供多个独立子模块时(如 @skyroc/utils@skyroc/hooks@skyroc/service):

"exports": {
  ".": {
    "types":   "./dist/index.d.mts",
    "default": "./dist/index.mjs"
  },
  "./color": {
    "types":   "./dist/color.d.mts",
    "default": "./dist/color.mjs"
  },
  "./storage": {
    "types":   "./dist/storage.d.mts",
    "default": "./dist/storage.mjs"
  },
  "./package.json": "./package.json"
}

每个子入口都需要 tsdown 在 entry 中列出,否则 dist 不存在对应文件。

平台分支模板

跨平台共享包(如 @skyroc/hooks)通过子入口区分平台:

"exports": {
  ".": {
    "types":   "./dist/index.d.mts",
    "default": "./dist/index.mjs"
  },
  "./web": {
    "types":   "./dist/web.d.mts",
    "default": "./dist/web.mjs"
  },
  "./native": {
    "types":   "./dist/native.d.mts",
    "default": "./dist/native.mjs"
  }
}

不使用 "browser" / "react-native" 条件字段,因为这种「同入口、按 condition 分发」的方式:

  • VS Code 类型推断有时挑不出正确平台;
  • bundler 可能误判 SSR 环境;
  • 调试难度高。

统一约定:跨平台代码用显式子路径(./web./native),不用 condition 字段。

资源 / 样式 / mock 子入口

部分包还需要暴露非 TS 内容(CSS / 模板 / mock):

"exports": {
  ".":      { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" },
  "./style.css": "./dist/style.css",
  "./mock":  { "types": "./dist/mock.d.mts",  "default": "./dist/mock.mjs" }
}

例子:

  • @skyroc/web-ui/style.css — 设计系统全局样式;
  • @skyroc/web-admin-notification/mock — 通知中心 mock 数据;
  • @skyroc/web-admin-devtools/jotai — Jotai devtools 子入口;
  • @skyroc/tailwind-plugin/preset — UnoCSS preset;
  • @skyroc/web-admin-styles/reset.css — 仅 reset 部分。

tsdown entry 与 exports 的一致性

最常见的 bugexports 里有 ./color,但 tsdown.config.ts 没把它列入 entry,结果安装方解析失败。

正确做法:

// tsdown.config.ts
export default defineConfig({
  entry: {
    index:   'src/index.ts',
    color:   'src/color/index.ts',     // ← 必须有
    storage: 'src/storage/index.ts'
  },
  dts: true
});
"exports": {
  ".":         { ... },
  "./color":   { "types": "./dist/color.d.mts",   "default": "./dist/color.mjs" },
  "./storage": { "types": "./dist/storage.d.mts", "default": "./dist/storage.mjs" }
}

两者必须一一对应

peerDependencies 与 external 的协同

含义
dependencies直接依赖,被 npm 安装;tsdown 默认 external
peerDependencies由消费方提供(React、antd 等)
devDependencies仅本包开发用,不影响下游
tsdown external 显式列表必要时手动追加

约定

  • react / react-dom 一律 peerDependencies
  • antd 一律 peerDependencies(仅在 ui-antd 等强绑包出现);
  • 跨包依赖 @skyroc/* 一律 dependencies(外部化即可);
  • 不要把构建工具(tsdown / vite)写到 dependencies

sideEffects

"sideEffects": false                           // 纯 ESM 包
"sideEffects": ["*.css", "*.scss", "*.svg"]    // 仅样式 / 资源有副作用
包类型sideEffects
纯函数 / 类型 / Hookfalse
注入 CSS / 全局副作用(runtime / styles / theme)数组列出
应用不需要

错误的 sideEffects: false 会让 bundler 把 CSS 摇掉,导致样式丢失。

files 字段

"files": [
  "dist",
  "README.md"
]

发布到 npm 时只包含 distREADME.mdpackage.json(后者自动)。不要打包 src / __tests__ / tsconfig.json

publishConfig

公开包:

"publishConfig": {
  "access": "public"
}

私有包:

"private": true

@skyroc scope 默认私有,需要发布的包必须显式 access: "public"

检查清单

新建 / 修改 exports 后:

  • tsdown entryexports 一一对应;
  • types / default 顺序正确;
  • 暴露 ./package.json
  • sideEffects 设置正确(默认 false 但 CSS 包要列);
  • files: ["dist"]
  • 跨平台分支用显式 ./web / ./native
  • pnpm build 产物存在;
  • 在 admin / playground 写一行 import 验证;
  • pnpm publint(如已接入)通过。

调试 exports 错误

错误原因
ERR_PACKAGE_PATH_NOT_EXPORTEDexports 没暴露子路径
TS 找不到类型types 字段缺失或顺序错误
产物找不到tsdown entry 没列
双 ESM 实例该包没把 @skyroc/* 标 external
RN 报 DOM API 错错把 web-only 代码放进主入口

On this page