mcpField
T
tldraw-migrate

tldraw-migrate

github.com/tldraw开发51.1k76 次安装今天更新
代码仓库

关于 tldraw-migrate

用于将项目迁移到较新 tldraw SDK 的技能,面向 SDK 使用者。它检测起始与目标版本,获取相关发布说明和文档,并指导升级依赖及处理 TypeScript 错误和弃用 API。

主要功能

  1. 01检测起始版本和目标版本
  2. 02获取变更记录与 SDK 文档
  3. 03升级已安装的 tldraw 包
  4. 04指导修复 TypeScript 错误
  5. 05查找已弃用的 API 用法

适用场景

  1. 01迁移到最新稳定版 SDK
  2. 02升级到 tldraw 预发布版本
  3. 03处理 SDK 升级后的类型错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tldraw/tldraw --skill tldraw-migrate

安装后在 Claude Code 中输入/tldraw-migrate确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.gitignore
detect-target.mjs
detect-versions.mjs
fetch-release-notes.mjs
README.md
type-errors.md

详细介绍

tldraw 迁移助手

你正在帮助将一个项目迁移到更新版本的 tldraw SDK。请仔细遵循此流程。

在本技能中,${SKILL_DIR} 指代此技能自身的目录(即 SKILL.md、辅助 .mjs 脚本以及缓存的 references/ 文件夹所在的位置)。下面的自动获取代码块会从 SKILL_DIR 环境变量解析该路径(如果已设置),否则会探测常见的技能位置(.claude/skills/、.agents/skills/、.codex/skills/、.cursor/skills/、skills/)。当你在工作流后续运行引用 ${SKILL_DIR} 的 shell 命令时,请替换为相同的绝对路径。如果你将此技能放在其他位置,请在运行自动获取代码块之前设置 SKILL_DIR。

参数:/tldraw-migrate [from-version] [target]。两者均可选。from-version 默认为从 git 历史记录中检测到的上一个 tldraw 版本。target 默认为 latest(npm 上的最新稳定版本);传入 dist-tag(canary、next、beta)或预发布 semver(例如 4.6.0-canary.abc123)可迁移到预发布版本。

已解析的迁移:!SKILL_DIR="${SKILL_DIR:-$(for d in .claude/skills/tldraw-migrate .agents/skills/tldraw-migrate .codex/skills/tldraw-migrate .cursor/skills/tldraw-migrate skills/tldraw-migrate; do [ -d "$d" ] && printf %s "$d" && break; done)}" && FROM=$(node "$SKILL_DIR/detect-versions.mjs" $ARGUMENTS || true) && TARGET=$(node "$SKILL_DIR/detect-target.mjs" $ARGUMENTS) && if [ -z "$FROM" ]; then echo "ERROR: could not detect previous tldraw version. Pass it explicitly: /tldraw-migrate <from-version> [target]" >&2; else echo "from $FROM → target $TARGET"; fi

资源(调用时自动获取)

下面的三个代码块是为 Claude Code 的 ! 前缀自动执行语法编写的。如果你是一个不会自动运行这些代码块的 Agent(Cursor、Codex、普通 Claude 等),请在继续之前将每个代码块作为 shell 命令执行 —— 它们会下载更新日志、完整文档,以及(对于预发布目标)工作流其余部分所依赖的下一个版本发布说明。

!SKILL_DIR="${SKILL_DIR:-$(for d in .claude/skills/tldraw-migrate .agents/skills/tldraw-migrate .codex/skills/tldraw-migrate .cursor/skills/tldraw-migrate skills/tldraw-migrate; do [ -d "$d" ] && printf %s "$d" && break; done)}" && mkdir -p "$SKILL_DIR/references" && CHANGELOG="$SKILL_DIR/references/tldraw-releases.txt" && PREV=$(node "$SKILL_DIR/detect-versions.mjs" $ARGUMENTS || true) && if [ -z "$PREV" ]; then echo "ERROR: could not detect previous tldraw version — skipping changelog fetch. Pass it explicitly: /tldraw-migrate <from-version> [target]" >&2; else node "$SKILL_DIR/fetch-release-notes.mjs" "$PREV" > "$CHANGELOG" && echo "Saved changelog (from $PREV, sourced from github.com/tldraw/tldraw) to $CHANGELOG ($(wc -l < "$CHANGELOG") lines)"; fi

!SKILL_DIR="${SKILL_DIR:-$(for d in .claude/skills/tldraw-migrate .agents/skills/tldraw-migrate .codex/skills/tldraw-migrate .cursor/skills/tldraw-migrate skills/tldraw-migrate; do [ -d "$d" ] && printf %s "$d" && break; done)}" && DOCS="$SKILL_DIR/references/tldraw-full-docs.txt" && STALE=$(find "$DOCS" -mtime +30 2>/dev/null) && if [ -s "$DOCS" ] && [ -z "$STALE" ]; then echo "Using cached full docs at $DOCS ($(wc -l < "$DOCS") lines) — delete the file or wait 30 days to refresh"; else curl --fail -sS https://tldraw.dev/llms-full.txt -o "$DOCS" && echo "Saved full docs to $DOCS ($(wc -l < "$DOCS") lines)"; fi !SKILL_DIR="${SKILL_DIR:-$(for d in .claude/skills/tldraw-migrate .agents/skills/tldraw-migrate .codex/skills/tldraw-migrate .cursor/skills/tldraw-migrate skills/tldraw-migrate; do [ -d "$d" ] && printf %s "$d" && break; done)}" && mkdir -p "$SKILL_DIR/references" && TARGET=$(node "$SKILL_DIR/detect-target.mjs" $ARGUMENTS) && case "$TARGET" in canary|next|beta|alpha|rc|*-canary*|*-next*|*-beta*|*-alpha*|*-rc*) NEXT="$SKILL_DIR/references/tldraw-next.mdx" && curl --fail -sS https://raw.githubusercontent.com/tldraw/tldraw/main/apps/docs/content/releases/next.mdx -o "$NEXT" && echo "Pre-release target ($TARGET) — saved next-release notes to $NEXT ($(wc -l < "$NEXT") lines)" ;; *) echo "Stable target ($TARGET) — skipping next-release notes" ;; esac

  • 已筛选的变更日志 — 从上一版本到当前版本之间稳定版本的发布说明,直接取自 tldraw/tldraw main 分支上的 apps/docs/content/releases/(GitHub 才是事实来源,而非 tldraw.dev)。每一项破坏性变更(标记为 💥)都带有一个 <details><summary>Migration guide</summary> 块,其中包含改动前/后的迁移方案。这些迁移块是特定版本修复方案的主要来源 —— 本技能有意不重复它们。当你遇到 TS 错误时,在此文件中 grep 相关 API 名称,并阅读其迁移块。
  • 完整文档 — 完整的 tldraw SDK 文档(约 1.5MB)。不要一开始就读它。按需使用 Grep 或带行范围的 Read 搜索特定主题(例如自定义形状、TLTextOptions)。
  • 下一版本发布说明 — 仅当目标是预发布版本时才存在。 即将发布的版本的进行中发布说明(来自 main 的原始 MDX)。同样的 <details><summary>Migration guide</summary> 结构。稳定版变更日志不会覆盖 canary/next 的差异;这些差异就在此文件中。

搜索迁移方案:

始终同时 grep 稳定版变更日志和 next.mdx(后者只对预发布目标存在 —— 2>/dev/null 会吞掉文件缺失的警告)。迁移块通常有 40–80 行,因此默认使用 -A60 作为后续上下文行数。

# List every breaking change with a migration block
grep -nE '💥|<summary>Migration guide' ${SKILL_DIR}/references/tldraw-releases.txt ${SKILL_DIR}/references/tldraw-next.mdx 2>/dev/null

# Find the migration recipe for a specific symbol
grep -n -B2 -A60 'getIndicatorPath\|TLUserStore\|EmbedShapeUtil' ${SKILL_DIR}/references/tldraw-releases.txt ${SKILL_DIR}/references/tldraw-next.mdx 2>/dev/null

第 1 步:了解环境

在做任何更改之前,先扫描项目以了解你要处理的内容。并行运行以下检查:

  • 包管理器:检查锁文件(yarn.lock、pnpm-lock.yaml、bun.lockb、package-lock.json)。全程使用对应的工具。
  • tldraw 包:grep -E "tldraw|@tldraw" package.json —— 安装了哪些包,版本是多少?注意:并非每个项目都会用到所有 tldraw 包。
  • 源码目录:找出项目的 TypeScript 源码所在位置(通常是 src/、app/ 或 lib/;Next.js App Router 项目没有 src/)。下面每次 grep 都使用该目录 —— 不要假设是 src/。
  • 导入风格:grep -r "from '@tldraw" <source-dir> --include="*.ts" --include="*.tsx" -l | head -5 —— 项目是从 'tldraw'、'@tldraw/editor' 还是两者都导入?这会影响模块增强的目标。
  • TypeScript:检查 package.json 中是否有 typecheck 或 tsc 脚本。如果都没有,退回到 npx tsc --noEmit(TypeScript 通常是 devDependency)。同时检查 TypeScript 版本。
  • 构建工具:检查 package.json 的脚本中的构建命令(vite、next、webpack、esbuild 等)。
  • 代码检查工具:检查 oxlint/eslint/biome 配置文件(.oxlintrc*、.eslintrc*、eslint.config.*、biome.json)。代码检查工具之后可能有助于捕获弃用项。
  • Monorepo:package.json 是在工作目录根目录,还是这是一个嵌套的包?检查 workspace 配置。

第 2 步:升级软件包

使用检测到的包管理器,将项目依赖中已有的所有 tldraw 软件包升级到解析出的目标版本(在上面的 "Resolved migration" 行中打印)。不要添加项目尚未使用的新软件包。

  • 对于稳定目标(latest 或稳定的 semver):安装该 tag/版本,例如 yarn add tldraw@latest 或 npm install [email protected]。
  • 对于预发布目标(canary、next、beta 或预发布 semver):安装该 tag/版本,例如 yarn add tldraw@canary。如果列出了多个 @tldraw/* 软件包,请将它们全部固定到同一目标,以避免版本偏差。

将版本升级单独提交。 在修复类型之前,仅暂存并提交 package.json 和 lockfile。后续步骤中的源代码更改保留在各自的提交中,这样最终的 git diff 就可以作为 "迁移所触及的内容" 进行审查,而不会与依赖更改混在一起。

第 3 步:识别所有 TypeScript 错误

运行项目的类型检查命令(来自第 1 步)并对错误进行分类:

  1. 按 TS 代码统计错误数量(例如 TS2344、TS2786),以了解分布情况。

  2. 列出出现错误的唯一文件,以了解影响范围。

  3. 识别错误模式。 以下类别与版本无关——它们描述的是常见错误的形态。针对每个重命名、移除或签名更改的具体修复方法位于发行说明的迁移块中(参见上文的 "搜索迁移配方"),而不在本技能中。

    • TS2786 "cannot be used as JSX component" / "bigint not assignable to ReactNode":React 类型偏差。@types/react(通常还有 @types/react-dom)与 tldraw 捆绑的版本不匹配。要查找捆绑版本:使用 npm 或 yarn classic 时,检查 node_modules/@tldraw/editor/node_modules/@types/react/package.json;使用 pnpm 或 yarn berry 时,运行包管理器的 "why" 命令(例如 pnpm why @types/react)。
    • TS2344 "does not satisfy constraint TLShape/TLBaseBoxShape" / TS2416 shapeType 不匹配:自定义形状或绑定类型未针对新的全局 props 映射注册。修复模式见 4b。
    • TS2305 / TS2724 "has no exported member" / "is not exported":API 已移除或重命名。在 tldraw-releases.txt / tldraw-next.mdx 中匹配的 Migration guide 块中查找替代项。
    • TS2339 "property does not exist":API 已重命名或方法签名已更改。同样查找。
    • TS2515 "non-abstract class does not implement inherited abstract member":基类新增了一个必需方法。错误中会指明该方法名——在迁移块中搜索该方法名。(注意:如果你的实现只是抛出异常,TypeScript 可能会推断为 never;请添加显式返回类型以满足基类签名。)
    • TS2345 出现在 createShapes/updateShapes 上:异构映射数组需要 as TLShapePartial[] / as TLCreateShapePartial[] 类型断言。参见 4e。
    • TS2345 涉及两个结构相似的 Editor 类解析到 node_modules/@tiptap/core 和 node_modules/@tldraw/editor/node_modules/@tiptap/core 下的不同路径:TipTap v2/v3 双重安装。参见 4d。

第 4 步:按影响程度依次修复错误

按此顺序修复 —— 每项修复都会消除大量下游错误。每完成一个子步骤后,重新运行类型检查,并确认该子步骤针对的错误码已解决或正在减少。忽略无关的错误码;它们属于后续子步骤。

子步骤症状
4a. React 类型TS2786“bigint 无法赋值给 ReactNode”
4b. 自定义形状与绑定TS2344 约束错误、TS2416 shapeType 不匹配
4c. API 重命名、移除、抽象方法TS2305、TS2724、TS2339、TS2515
4d. TipTap 导入仅当项目依赖 @tiptap/* 时 —— 否则跳过
4e. 剩余类型错误使用 .map() 的 createShapes/updateShapes、遗留的类型断言

阅读 type-errors.md,获取项目实际存在的错误码对应的解决方案。不要从头读到尾 —— 直接跳到匹配的子步骤。

第 5 步:修复弃用项

所有类型错误解决后,查找并修复 @deprecated API 的使用。这些代码仍能编译,但应当迁移。

  1. 查找已弃用的符号。 更新日志是最可靠的起点 —— tldraw 的 .d.ts 文件使用多行 JSDoc,因此对类型定义执行 grep 很脆弱(声明行通常位于 @deprecated 标签之后 2–5 行,而非紧邻其后)。

    从这里开始:

    grep -i 'deprecated' ${SKILL_DIR}/references/tldraw-releases.txt
    

    当目标是预发布版本时,还要执行:grep -i 'deprecated' ${SKILL_DIR}/references/tldraw-next.mdx。

    要对照类型定义交叉检查(一次一个包 —— 对项目导入的每个 @tldraw/* 包以及 tldraw 本身重复此操作),请使用 -A 而非 -B:

    grep -A5 '@deprecated' node_modules/@tldraw/editor/dist-cjs/index.d.ts \
      | grep -oE '\b(class|function|interface|const|type|let|var)\s+\w+' \
      | awk '{print $NF}' | sort -u
    

    这能捕获多行 JSDoc,但会产生一些误报 —— 应将输出视为候选列表,而非权威列表。应以更新日志的 grep 结果为准。

  2. 如果配置了 linter,请运行它 —— 弃用规则(例如 eslint 的 deprecation/deprecation)会自动标记已弃用的导入。如果未配置 linter,请跳过此步骤。

  3. 在项目源码中搜索每个已弃用的符号。使用 @deprecated JSDoc 注释、更新日志条目或文档中推荐的替代方案进行替换。

  4. 对类型检查未能捕获的重命名进行核查。 如果更新日志或 tldraw-next.mdx 描述了某项重命名,而项目仍在导入旧名称,TypeScript 可能通过通配符重新导出解析它而未能发现。浏览更新日志/next.mdx 中的“renamed” / “moved to”条目,并在源码中 grep 你找到的所有旧名称。

第 6 步:验证

运行项目的类型检查和构建命令(如第 1 步中所发现的那样)。

如果仍有错误,重复分类并修复的循环。

迁移后健全性检查

所有错误解决后,做一次快速审计:

  1. 统计本次迁移添加的带类型 as 断言。 简单地 grep ' as ' 会多算,因为它会包含 as const(推荐的收窄模式)以及 import 路径中的 as。基于 diff 并过滤:

    git diff -- '<source-dir>/**/*.ts' '<source-dir>/**/*.tsx' \
      | grep -E '^\+' | grep -v '^+++' \
      | grep -E '\bas\s+[A-Z]' | grep -v 'as const'
    

    本次迁移新增的带类型断言应不超过少数几个(理想情况下为零,主要合理解例是异构 createShapes/updateShapes 数组上的 as TLShapePartial[] / as TLCreateShapePartial[])。如果你在整个迁移中新增超过约 5 个,请回头修复——你几乎肯定在错误使用新 API。

  2. 审查你添加的每个带类型断言:逐一验证是否确实必要,检查能否用 as const、satisfies、泛型类型参数或模块增强来替代。删除或替换所有有更简洁替代方案的断言。

  3. 审计模块增强。 模块增强适用于 TLGlobalShapePropsMap / TLGlobalBindingPropsMap 注册,以及添加真正缺失的公共类型。它不适用于重新暴露 SDK 已降级为 @internal 的符号——那是权宜之计,不是修复。改为寻找公共替代品(迁移块通常会指明)。

  4. 验证没有桩代码或死代码:如果你为已移除的 API 做了桩(例如用 no-op 替换已移除的函数),确保调用方代码不依赖返回值。如果依赖,请在迁移块或文档中寻找恰当替代。

第 7 步:报告

以一条总结消息结束迁移并发送给用户。不要注水——只讲事实:

  • 从 → 目标版本,如果是标签,给出解析后的目标版本(canary → 4.7.0-canary.abc123)。
  • 修改的文件(数量,如果不多则附列表)。
  • 修复的 TypeScript 错误,按代码细分(例如 TS2786 ×12, TS2344 ×3, TS2305 ×1)。使用你在第 4 步收集的前后计数。
  • 新增的带类型 as 断言(数量,加上每个断言的一行理由——应为零或接近零;标记任何你不确定的)。
  • 新增的模块增强(涵盖内容——TLGlobalShapePropsMap、TLGlobalBindingPropsMap、@tiptap/core 链式命令等)。
  • 已迁移的弃用(数量及符号)。
  • 任何标记为未记录的内容——在任何迁移块中都找不到的符号、更新日志中的缺口,或与实际 API 表面不符的配方。这些都值得反馈给 tldraw 团队。
  • 任何跳过或推迟的内容——如果你搁置了某个困难的重命名或留下了 TODO,请明确说明,以便用户知道还有哪些需要自己处理。

质量检查

  • 类型安全至关重要。 目标是实现与原始代码同等类型安全的迁移。不要添加 as any、as unknown 或宽泛的类型断言。不要添加 @ts-ignore 或 @ts-expect-error。这些永远不可接受 —— 如果你无法让类型正常工作,说明你还不理解新的 API。停下来,先阅读 changelog 和类型定义再继续。
  • 优先使用 TypeScript 的类型收窄特性,而非类型断言。 使用 as const 表示字面量类型,使用 satisfies 在不拓宽类型的前提下进行类型检查,在调用处使用泛型参数,使用模块扩充来扩展接口。这些才是迁移的正确工具 —— as 断言不是。
  • 对于大量相同模式的批量处理,使用并行 Agent。 先手动将一个文件按配方处理以确认可行,然后在一条消息中为每个剩余文件生成一个 Agent(多个 Agent 工具调用并行执行)。向每个 Agent 传递:(1) 待迁移文件的路径,(2) 来自模板文件的前后对比 diff,(3) tldraw-releases.txt 中相关的迁移代码块。这在 4b(跨多个 shape 文件进行机械式 TLBaseShape → TLGlobalShapePropsMap 迁移)和 4c(一个符号重命名波及整个项目)中最有用。
  • 不要只是消除错误 —— 要理解新的 API。 当方法签名发生变化时(例如新增了参数、属性被重命名为更丰富的类型),请阅读 changelog 和当前的类型定义,理解其_为什么_变化。一个能编译但在新 API 期望真实数据的地方传入硬编码/假值的修复,比类型错误更糟糕 —— 它会悄无声息地降低行为质量。例如,如果一个函数新增了必填参数,请检查应该由哪些 shape props 或编辑器状态来提供这些参数,而不是传入 0 或 1。
  • 当不确定某个 API 模式时,在完整文档(${SKILL_DIR}/references/tldraw-full-docs.txt)中 grep 该特定 API 的用法示例。文档中包含展示每个 API 规范用法的代码示例。

提示

  • 阅读 ${SKILL_DIR}/references/tldraw-releases.txt 获取筛选后的 changelog —— 这是了解变更内容的主要来源
  • 当你需要某个特定 API 的文档时,grep ${SKILL_DIR}/references/tldraw-full-docs.txt
  • 搜索 tldraw 类型时,尝试多个路径 —— dist 目录结构因版本和包管理器而异

评论

支持 Markdown