tldraw 仓库中的@tldraw/tldraw遗留兼容包:它是什么、如何工作、为何应该改用tldraw
【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldraw
导读
在 tldraw SDK 的 monorepo 中,packages/namespaced-tldraw目录对应 npm 包@tldraw/tldraw,这是一个为历史兼容而保留的“薄壳”包:它本身不实现任何画布逻辑,而是完整地重新导出标准tldraw包的全部 API,并注册库版本信息与打包 CSS 资源。本文将基于该包在仓库中的 README、源码、构建脚本与测试,拆解它的定位、实现机制、构建流程与许可约束,并给出从旧包名迁移到tldraw的实操建议。
一、这个包为什么存在:历史包袱与迁移过渡
阅读 packages/namespaced-tldraw/README.md 可以看到,官方对该包的定位非常直白:"This package is kept around for legacy purposes. It just tracks the normaltldrawpackage. Please install that one instead."
也就是说:
@tldraw/tldraw是被保留的旧包名,仅用于照顾历史用户与存量代码;- 它本身不维护独立功能,而是始终跟踪并跟随标准
tldraw包的发布节奏; - 新项目应当直接安装
tldraw,而不是@tldraw/tldraw。
从仓库中的 package.json 可以印证这一点:该包的"name"为@tldraw/tldraw,版本号与tldraw包完全一致(当前均为 5.4.0),其唯一运行时依赖就是"tldraw": "workspace:*"(monorepo 工作区引用,保证两者永远同步发布)。
二、包的结构:一个几乎“空”的入口
整个包的结构非常精简,核心文件只有三个:
| 文件 | 作用 |
|---|---|
| src/index.ts | 包的唯一入口:重新导出tldraw并注册版本 |
| src/index.test.ts | 验证导出完整性的单元测试 |
| scripts/copy-css-files.mjs | 构建期合并生成tldraw.css |
其 API 报告(api-report.api.md)也证实了这一点——整个包的公开 API 只有一行:
export * from "tldraw";这意味着@tldraw/tldraw的完整类型面(TypeScript 类型、Editor类、Tldraw组件、各类 shape 工具、Vec/VecModel等几何工具与类型)与tldraw包逐字一致,不会多也不会少。
三、核心实现:re-export 与版本注册的完整机制
3.1 入口文件的完整逻辑
packages/namespaced-tldraw/src/index.ts全文仅数行,但包含了两个关键动作:
import { registerTldrawLibraryVersion } from 'tldraw' // eslint-disable-next-line tldraw/no-export-star export * from 'tldraw' registerTldrawLibraryVersion( (globalThis as any).TLDRAW_LIBRARY_NAME, (globalThis as any).TLDRAW_LIBRARY_VERSION, (globalThis as any).TLDRAW_LIBRARY_MODULES )动作一:全量重新导出。export * from 'tldraw'让使用旧包名的代码无需改动即可获得tldraw的全部命名导出。文件头部的eslint-disable-next-line tldraw/no-export-star注释说明仓库的 lint 规则本身反对export *的写法(避免命名冲突与 API 面失控),但此处的通配导出是有意为之——它正是“兼容壳”的本质。
动作二:注册库版本信息。registerTldrawLibraryVersion接收三个来自globalThis的注入值(TLDRAW_LIBRARY_NAME、TLDRAW_LIBRARY_VERSION、TLDRAW_LIBRARY_MODULES),这些值由构建期工具注入,用于在运行时把当前加载的 SDK 版本与模块清单登记到全局,便于调试、错误上报与版本一致性检测。
值得注意:tldraw包自身的入口 packages/tldraw/src/index.ts 也以完全相同的模式在文件末尾调用registerTldrawLibraryVersion。也就是说,@tldraw/tldraw与tldraw的“自我注册”行为完全对齐,二者在运行时行为上没有可感知差异。
3.2 底层依赖关系
从 package.json 可以进一步看到这个壳的边界:
dependencies:仅tldraw(workspace 引用),其余一切由tldraw内部消化;peerDependencies:react: ^18.2.0 || ^19.2.1、react-dom: ^18.2.0 || ^19.2.1,与tldraw包 packages/tldraw/package.json 声明的 peer 范围一致——React 18.2 及以上、React 19.2.1 及以上均可;engines.node: ">=22.12.0":开发与构建环境要求 Node.js 22.12 或更高版本。
而tldraw包本身则聚合了@tldraw/editor、@tldraw/state、@tldraw/store、@tldraw/tlschema、@tldraw/driver等核心工作区包以及@tiptap/*(文本编辑)、idb(IndexedDB 持久化)、lz-string(压缩)等依赖。因此当你的应用只安装tldraw时,画布引擎、状态管理、数据模型与 UI 全套能力都会随之就位。
四、构建机制:CSS 从哪来
由于壳包自身没有 UI 源码,它的样式文件需要在构建期生成。packages/namespaced-tldraw/scripts/copy-css-files.mjs实现了这一逻辑:
let combinedContent = [ join(packageDir, '..', 'editor', 'editor.css'), join(packageDir, '..', 'tldraw', 'src', 'lib', 'ui.css'), ].reduce((acc, path) => { const content = readFileSync(path, 'utf8') acc += content + '\n' return acc }, `/* THIS CSS FILE IS GENERATED! ... */`)该脚本将两份源样式拼接为最终的tldraw.css:
packages/editor/editor.css——来自@tldraw/editor的核心画布样式;packages/tldraw/src/lib/ui.css——来自tldraw的 UI(工具栏、菜单、面板等)样式。
脚本注释特别说明:它直接从源文件生成 CSS,而不是依赖tldraw构建产出的tldraw.css,这是为了避免 lazyrepo 缓存跳过 prebuild 步骤时产物缺失的问题。对应的 package.json scripts 也体现了这条流水线:
"predev": "node ./scripts/copy-css-files.mjs", "dev": "chokidar '../tldraw/tldraw.css' -c 'node ./scripts/copy-css-files.mjs'", "prebuild": "node ./scripts/copy-css-files.mjs", "build": "yarn run -T tsx ../../internal/scripts/build-package.ts"即:开发时用chokidar监听源 CSS 变化并自动重新合并;构建前通过predev/prebuild钩子强制生成。"files"字段中仅发布tldraw.css,说明这个包在 npm 上真正对外提供的就是“JS re-export + 一份合并样式”。
五、质量保障:测试如何验证“壳”的完整性
packages/namespaced-tldraw/src/index.test.ts用两个用例确保 re-export 没有失效:
import * as tldraw from './index' it('exports things from tldraw', () => { expect(tldraw).toHaveProperty('Tldraw') expect(new tldraw.Vec()).toMatchObject({ x: 0, y: 0 }) }) it('exports types from tldraw', () => { const _thing: tldraw.VecModel = { x: 0, y: 0 } })- 第一个用例验证值导出:
Tldraw组件存在(toHaveProperty),并且Vec是可实例化的类(new tldraw.Vec()得到{ x: 0, y: 0 },说明构造默认值正确); - 第二个用例验证类型导出:
VecModel这样的 TypeScript 类型也能从壳包导入并被正常使用。
这两个测试恰好覆盖了“值”与“类型”两个维度,确保export * from 'tldraw'的两类导出都健康。测试运行使用vitest,并复用根目录 vitest.config.ts 体系——该包的 vitest.config.ts 直接继承internal/config/vitest/node-preset标准 Node 预设,无额外覆盖。
六、使用建议:新项目请直接安装tldraw
结合 README 的指引与仓库实现,对使用者最直接的结论是:
# 旧写法(兼容,但不推荐用于新项目) yarn add @tldraw/tldraw # 推荐写法 yarn add tldraw迁移时你几乎不需要改动业务代码,因为两个包的导出面完全一致(这正是export *的意义所在)。唯一需要留意的差异点是:
- 样式导入:确认你的样式引用从
@tldraw/tldraw/tldraw.css切换到tldraw/tldraw.css,二者都是构建期由同一份源 CSS 生成的合并文件; - peer 依赖:确保 React 版本落在
^18.2.0 || ^19.2.1区间内; - Node 版本:仓库开发环境要求 Node >= 22.12.0(对应
engines字段)。
如果你正在阅读tldraw的源码(比如 packages/tldraw/src/index.ts 中的Editor、Tldraw等全部导出),可以把@tldraw/tldraw当作一层透明的代理来理解:任何对它的调用最终都落在tldraw包上。
七、许可与商标:沿用 SDK 的统一约束
README 还交代了使用该包时需要注意的合规边界,这里同样适用于tldraw包:
- 许可:作为 tldraw SDK 的一部分,本包依据 tldraw SDK 许可提供(见仓库根目录 LICENSE.md);
- 水印要求:你可以将 tldraw SDK 用于商业或非商业项目,但前提是保留画布上的 “Made with tldraw” 水印;如需移除水印,需要购买商业许可;
- 商标:tldraw 名称与 Logo 是 tldraw Inc. 的商标(Copyright (c) 2024-present tldraw Inc.),使用需遵守仓库根目录 TRADEMARKS.md 中的商标使用准则。
结语
@tldraw/tldraw是 tldraw 演进史留下的一个“版本钉子”:它用最克制的实现(一行通配导出 + 版本注册 + 构建期 CSS 合并)保证了旧用户的无痛升级,同时让仓库的 API 面与许可约束保持单一事实来源。理解它的存在,能帮你避免在排查依赖时被两个相似包名迷惑——记住核心结论即可:功能永远以tldraw为准,@tldraw/tldraw只是它的镜像。
【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldraw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考