Plate 构建链路硬切换:slate-v2 全面迁移 tsdown 并发布纯 ESM 产物的落地实践
2026/9/16 23:39:45 网站建设 项目流程

Plate 构建链路硬切换:slate-v2 全面迁移 tsdown 并发布纯 ESM 产物的落地实践

【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate

导读

本文基于 Plate 仓库中的技术决策文档 2026-04-16-slate-v2-tsdown-esm-cut.md,完整还原了 slate-v2 分支从 Rollup 构建切换到 tsdown、并发布纯 ESM(ESM-only)包产物的全流程。文中包含决策理由、共享构建配置设计、包清单改造要点、开发期 HMR 验证方案与测试通道适配,读者可直接参考该方案为自己的 monorepo 完成构建器迁移与 ESM 化改造。

一、目标:对 Rollup 的硬切换(Hard-cut)

文档开篇明确列出了本次改造的 Goal:

Hard-cut Rollup from slate-v2,move package builds totsdown,and publish ESM-only package output.

翻译过来即三条主线:

  1. 硬性移除 Rollup:不再保留 Rollup 构建链路(原配置文件rollup.config.js被删除),彻底切换而非并行共存;
  2. 构建器迁移到 tsdown:所有包的构建统一交由tsdown执行;
  3. 发布纯 ESM 产物:包输出只保留 ESM 格式,cjsumd一律裁掉。

值得注意的是,该文档描述的是对独立仓库slate-v2的改造动作,而当前 plate 仓库作为其最终归宿,已将这套思路完整落地:根级tooling/config/tsdown.config.ts共享配置、包的 ESM-only 清单、以及开发期直接消费包源码的 Next dev 通道,都能在仓库中找到对应实现。

二、关键决策:ESM-only 与源码直连

文档记录的 Decisions 是理解整套改造的钥匙:

  • keep package output ESM-only:包产物保持纯 ESM,这是顶层不可动摇的约束;
  • cutcjsandumd:明确删除 CommonJS 与 UMD 两种格式,缩减产物矩阵;
  • use a sharedconfig/tsdown.config.mtsshaped after Plate's architecture, not Plate's exact config:采用共享的 tsdown 配置,架构上参考 Plate,但并非照搬其具体配置——这是一条"形似而非神似"的克制原则;
  • keep Next dev resolving packagesrcdirectly so HMR does not depend on package builds:开发模式下 Next 直接解析各包的src源码,使热更新(HMR)完全摆脱对包构建的依赖。

最后一条决策是本文的精华:它意味着"构建产物"与"开发体验"彻底解耦。开发者改包源码后无需重新构建 dist,HMR 即刻生效,这对多包编辑器类项目(源码量大、迭代频繁)尤其重要。

三、实施清单:从配置到清单的逐项落地

文档列出的 Implementation 是本次改造的操作清单,逐项对应仓库现状如下。

1. 新增共享 tsdown 配置

原文档新增/Users/zbeyens/git/slate-v2/config/tsdown.config.mts。在 plate 仓库中,对应实现在 tooling/config/tsdown.config.ts,其关键设计点包括:

  • 入口自动探测:优先寻找src/index.ts,否则回退src/index.tsx;同时探测src/react/index.*src/static/index.*是否存在并追加为额外入口,天然支持 React 子入口与静态子入口;
  • platform: 'neutral':产物面向中性平台,不绑定 Node 或浏览器运行时的特殊假设;
  • dts: { bundle: true }:声明文件打包为单文件,配合exports: true生成规范导出;
  • CI 中禁用 sourcemapconst enableSourcemaps = !process.env.CI,在 CI 环境关闭 sourcemap 以加速构建;
  • 自定义插件fix-broken-dts-chunk-imports:在writeBundle阶段遍历所有.d.ts文件,将指向不存在的.js的导入改写回无扩展名形式,修复打包声明文件中可能出现的错误 chunk 导入;
  • Babel 插件链:接入@rollup/plugin-babel,并启用babel-plugin-react-compiler(target 18),同时exclude: '**/static/**'排除静态入口。

这证实了文档中"架构参考 Plate、配置不照搬"的决策——仓库里的共享配置正是 Plate 架构的形态。

2. 删除 Rollup 配置与类型同步脚本

原文档删除了config/rollup/rollup.config.jsscripts/sync-package-types.mjs,前者代表旧构建链路的彻底移除,后者则因 tsdown 的dts.bundle已内建声明文件处理而不再需要单独同步类型。

3. 包清单改造为 ESM-only

原文档要求将包清单转换为dist/index.js+dist/index.d.ts的纯 ESM 形态。以仓库中的 packages/slate/package.json 为范本,可以看到完整的字段组合:

{ "name": "@platejs/slate", "sideEffects": false, "exports": { ".": "./dist/index.js", "./package.json": "./package.json" }, "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": ["dist/**/*"], "type": "module", "module": "./dist/index.js" }

要点解读:

  • "type": "module"声明整个包按 ESM 解释,exports字段中不再出现require条件——即彻底放弃 CJS 消费者;
  • mainmodule统一指向./dist/index.jstypes指向./dist/index.d.ts,产物矩阵收敛为"一个 JS + 一个类型声明";
  • "files": ["dist/**/*"]收紧发布内容,仅包含构建产物;
  • "sideEffects": false便于打包器 tree-shaking。

4. 包构建脚本切换为 tsdown

原文档将包build脚本切换到 tsdown。在根级 package.json 中,这一逻辑通过代理脚本体现:

"p:build": "cd ${INIT_CWD:-.} && pnpm p:tsdown", "p:build:watch": "cd ${INIT_CWD:-.} && pnpm exec tsdown --config=${PROJECT_CWD:-.}/tooling/config/tsdown.config.ts --log-level warn --watch", "p:tsdown": "cd ${INIT_CWD:-.} && pnpm exec tsdown --config=${PROJECT_CWD:-.}/tooling/config/tsdown.config.ts --log-level warn"

${INIT_CWD}${PROJECT_CWD}使同一套脚本可被任意包复用:在任意包目录下执行pnpm p:build,即会使用仓库共享的 tsdown 配置构建当前包。包内脚本(如packages/slate"build": "plate-pkg p:build")再经由plate-pkg转发,形成"根共享配置 → 包级代理"的调用链。p:build:watch提供了监听模式,供需要持续构建的场景使用。

5. 根 dev 脚本简化为pnpm serve

原文档简化了根dev脚本。当前仓库的devturbo --filter=www dev,通过 Turbo 仅启动 www 应用,不再在 dev 期间串行构建各包——这正与"开发期不依赖包构建"的决策互为表里。

6. Jest 切换到源码映射,避免 ESM dist 破坏测试通道

原文档将 Jest 切换为"对 workspace 包做 source mapping",使测试直接吃源码而非 ESM dist。这一思路在后续演进中进一步升级为 Bun 测试通道:仓库根目录的 bunfig.toml 通过preload预加载 tooling/config/bunTestSetup.ts,该 setup 完成了 Happy DOM 全局注册、Testing Library matcher 扩展、afterEach(cleanup)jest.fn/jest.spyOn兼容全局等一揽子工作,并在 tooling/config/global.d.ts 中提供类型增强。也就是说,测试侧的目标同样是"解析源码、绕开 dist",从而与 ESM-only 发布形态共存。

7. 修复 slate-dom 公共 DOM 辅助类型导出

原文档提到修复slate-dom公共 DOM helper 类型导出,以保证打包声明文件(bundled declarations)的正确性——这正是共享配置中fix-broken-dts-chunk-imports插件要解决的典型问题:声明打包后,跨 chunk 的相对导入若残留.js后缀而实际无对应文件,会导致类型解析失败。

四、验证矩阵:五条命令守住回归底线

文档给出的 Verification 是迁移完成后的硬性验收:

pnpm install pnpm lint pnpm typecheck pnpm test pnpm build
  • pnpm install:验证 workspace 依赖与清单改造后仍可正常安装;
  • pnpm lint:代码规范与格式检查(当前仓库由 biome + eslint 承担,见 package.json 的lint脚本);
  • pnpm typecheck:类型检查(根脚本g:typecheck会先构建再对包做--only类型检查,见 package.json);
  • pnpm test:验证测试通道在源码映射/Bun 通道下不受 ESM 产物影响;
  • pnpm build:最终确认 tsdown 构建全量通过。

这五条命令恰好覆盖了"依赖安装 → 静态检查 → 类型 → 测试 → 构建"的完整发布前链路,任何一环失败都意味着迁移不完整。

五、HMR Proof:用一次真实补丁证明 dev 不依赖包构建

文档末尾记录了非常有说服力的实证过程,用于证明"Next dev 直接消费包 src":

  1. 复用运行中的 Next dev 服务器(http://localhost:3100);
  2. 打开/examples/plaintext示例页;
  3. 在页面中输入编辑器内容;
  4. 临时给packages/slate-react/src/components/string.tsx打一个探针补丁(添加一个 probe attribute);
  5. 观察已打开的页面在保留已输入文本的前提下,立刻渲染出新属性;
  6. 撤销探针补丁。

That proves the app is consuming packagesrclive in dev without any package build step.

这一实验的结论是:页面在无任何包构建的情况下实时消费了包源码,且 HMR 合并过程中未丢失编辑器内已键入的文本——既证明了"源码直连",也间接验证了 HMR 状态保留能力。

仓库侧对这套机制提供了完整实现支撑:apps/www/next.config.ts 中buildWorkspaceDevAliases()会同时构造srcdist两套别名映射,当设置PLATE_WWW_DEV_SOURCE=1时优先将@platejs/*@udecode/*platejs等 workspace 包解析到src入口(含reactstatic子入口);未设置该环境变量时则回退到 dist 别名,用于模拟发布态。再叠加experimental.externalDir: isDevturbopackFileSystemCacheForDev,共同保证开发期 HMR 与包构建彻底解耦。

六、可复用的迁移方法论总结

综合文档决策与仓库落地,这套 "tsdown ESM-only 硬切换" 可以提炼为一份可复用的操作顺序:

  1. 先立共享配置:新增统一的 tsdown 配置(入口探测、platform neutral、dts bundle、CI 关闭 sourcemap、必要的声明修复插件),放在 tooling 层供所有包复用;
  2. 再改包清单:统一为type: module+exports指向dist/index.js+types指向dist/index.d.ts,删除require条件与 cjs/umd 产物;
  3. 切换构建脚本:包build脚本通过代理转发到共享 tsdown 配置,提供build:watch监听模式;
  4. 解耦开发链路:Next dev 通过 alias 直接解析包src,确保 HMR 不依赖 dist;
  5. 适配测试通道:Jest 对 workspace 包做源码映射(后续可演进为 Bun 预加载 setup + 源码别名),避免 ESM dist 破坏测试;
  6. 收尾清理:删除旧 Rollup 配置与冗余的类型同步脚本,精简根 dev 脚本;
  7. 全量验证:以install → lint → typecheck → test → build五连命令收口,再用一次真实的 dev 补丁实验证明 HMR 与构建解耦。

这套方案的关键收益在于:发布侧获得单一、干净、可 tree-shaking 的 ESM 产物;开发侧获得不依赖构建的即时 HMR。两者通过"构建器(tsdown)"与"解析策略(源码直连)"两条独立轨道同时达成,是大型富文本编辑器类 monorepo 值得参考的工程实践。

【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询