Debug dsh 插件的 5 种方法 (不发布 npm)

核心观察: dsh plugin --profile <name> <args...> 在源码 (apps/cli/src/plugin.ts:120-129) 里是 纯 pnpm 转发。任何 pnpm 装包的方式都直接可用。

0. 速查 — 5 种方式对比

方式 推荐场景 反复修改要重做? 验证
1. 本地路径 dsh plugin add ./my-plugin 最常用、最简单 ✅ 不需要 ✅ 改完直接重启
2. link: 协议 单仓库多包复杂 ✅ 不需要 ✅ 一处改全看见
3. 直接 --patch overlay 单文件,无 package.json ❌ 改完一次就行 ❌ 重新 patch
4. pnpm pack 打包本地装 模拟发布后行为 ✅ 需要重新 pack
5. workspace:* 内部包 dsh 自身贡献者 自动 ✅ 链接

最常用: #1 (本地路径)


1. 方式 1: 本地路径 — 99% 场景够用

# 你正在写插件的项目
studyzy-dsh-web-remote-access/
├── package.json          # 包含 dsh.bundle
├── cordis.patch.yml
└── index.js

# 装进 dsh profile
dsh plugin --profile web add ./studyzy-dsh-web-remote-access

# dsh 内部走 (按 plugin.ts:104-112 代码):
#   1. anchorPathSpec('./studyzy-dsh-web-remote-access', CWD)
#      → 转成绝对路径:C:\...\studyzy-dsh-web-remote-access
#   2. pn

q/a: npm 名字必须有效吗?不需要./studyzy-dsh-web-remote-access 是相对路径,pnpm 知道怎么 link 本地目录。

profile 的 package.json 会被改成:

{
  "dependencies": {
    "studyzy-dsh-web-remote-access": "link:/C:/full/path/to/studyzy-dsh-web-remote-access"
  }
}

改源码: 直接改 index.js → 保存 → 重启 dsh --profile web → 改完就生效。

调试流程

# 改 index.js
vim /path/to/studyzy-dsh-web-remote-access/index.js

# 重启(无需重装)
dsh --profile web

# 验证
dsh --profile web --dump-config | grep studyzy

2. 方式 2: --patch overlay — 最快但不能装 bundle

适合单文件插件,不想写 package.json

# 单文件插件
mkdir my-plugin && cd my-plugin
cat > my-plugin.js <<'EOF'
export const name = 'my-plugin'
export function apply(ctx) {
  console.log('hi from my-plugin')
}
EOF

# 写一个 patch 文件
cat > my-patch.yml <<'EOF'
- insert:
    - id: my-plugin
      name: './my-plugin.js'
EOF

# 启动时叠上去
dsh --profile web --patch ./my-patch.yml

但有局限 — dsh plugin 装的是 bundle,要 bundle 就必须有 package.json + dsh.bundle 字段--patch 走的是另一条 loader path,不进 layer 列表。

这条 适合"我只想看看单文件能不能跑"。


如果你想多个项目共享同一个本地路径:

# 在插件项目
cd studyzy-dsh-web-remote-access
pnpm link --global

# 在 dsh profile
dsh plugin --profile web link studyzy-dsh-web-remote-access @ studyzy-dsh-web-remote-access

或直接用上面 #1 的本地路径 — 几乎所有人都用 #1,pnpm link 复杂。


pnpm 支持 link: 路径:

# 任意路径
dsh plugin --profile web add link:/full/path/to/studyzy-dsh-web-remote-access

# 相对路径 (从执行 dsh 的 CWD 算)
dsh plugin --profile web add link:./studyzy-dsh-web-remote-access

# 上一级目录
dsh plugin --profile web add link:../my-workspace/studyzy-dsh-web-remote-access

./ 路径的区别:link: 显式说明"装成链接" (vs file: 是 copy)。两者都来自 pnpm 协议,但语义不同:

  • file: → 复制(占空间,源改了不会传播)
  • link: → 符号链接(源改了生效)
  • ./自动判断 (apps/cli/src/plugin.ts:104-112)

推荐用 ./ 即可,dsh 已经帮你处理。


5. 方式 5: pnpm pack 本地 tarball — 模拟发布后行为

如果你想测试从 npm 装回来的样子(而不是 link):

# 在插件项目
cd studyzy-dsh-web-remote-access
pnpm pack
# → studyzy-dsh-web-remote-access-0.1.0.tgz

# 装
dsh plugin --profile web add ./studyzy-dsh-web-remote-access-0.1.0.tgz

# 改完要重 pack
pnpm pack
dsh plugin --profile web update studyzy-dsh-web-remote-access

适合开发末期,模拟用户从 npm install 的场景


6. 完整 debug 流程 (我的推荐)

# 1. 写插件第一版
mkdir studyzy-dsh-plugin && cd studyzy-dsh-plugin
mkdir src
cat > package.json <<'EOF'
{
  "name": "studyzy-dsh-plugin",
  "version": "0.1.0",
  "type": "module",
  "main": "index.js",
  "files": ["index.js", "cordis.patch.yml"],
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
EOF
cat > index.js <<'EOF'
export const name = 'studyzy-dsh-plugin'
export function apply(ctx) {
  console.log('[studyzy-dsh-plugin] loaded')
  ctx.on('session/event', (s, e) => console.log('event:', e.type))
}
EOF
cat > cordis.patch.yml <<'EOF'
- insert:
    - id: studyzy-dsh-plugin
      name: 'studyzy-dsh-plugin'
EOF

# 2. 装到 dsh profile(关键!)
# cd 到 *上一级*,这样相对路径 ./studyzy-dsh-plugin 能找到
cd ..
dsh plugin --profile web add ./studyzy-dsh-plugin

# 3. 验证
dsh --profile web --dump-config | grep studyzy
# 应该看到你的 id 出现

# 4. 启动 + 调试
dsh --profile web
# 打开浏览器 http://127.0.0.1:3080 看效果

# 5. 改代码
vim studyzy-dsh-plugin/index.js

# 6. 重启 dsh (不需要重装!)
dsh --profile web

# 7. 看不生效?
# - 二选一:
#   a) 重启 hot reload: 看 dsh 有没有 HMR 模式启动
#   b) 删除重装:
dsh plugin --profile web remove studyzy-dsh-plugin
dsh plugin --profile web add ./studyzy-dsh-plugin

7. 为什么用"装到 profile"而不是直接 --patch?

packages/boot/app-boot/README.md 真实文档:

bundles are layered over an empty root in dsh.profile.bundles order

也就是说,dsh plugin add 装的 bundle:

  • 载入机制(进 layer 列表)
  • 跟 base/web-app 等官方 bundle 同位置
  • 顺序在用户 cordis.patch.yml 之前

--patch:

  • 临时 overlay,每次启动叠上去
  • 在所有 bundle 之后
  • 适合"我想测试一下",不适合"我要让我的插件成为正常产品"

结论: 长期开发 = dsh plugin add ./本地路径 一次,之后改源码 + 重启就行。


8. 改代码后,"重启"够吗?

通常够 — 因为 dsh --profile web 启动时重新加载整个 plugin tree:

  • Cordis 重新 apply(ctx) 你的函数
  • 你的 console.log 出现

如果你修改了 cordis.patch.yml (比如新加了一个 insert row),有可能因为 pnpm 缓存,需要:

# 删除 pnpm 缓存的 link
rm -rf ~/.dsh/profiles/web/node_modules/studyzy-dsh-plugin
dsh plugin --profile web add ./studyzy-dsh-plugin

或者:

dsh plugin --profile web remove studyzy-dsh-plugin
dsh plugin --profile web add ./studyzy-dsh-plugin

源码改动(index.js 内部):改完直接重启 dsh → 重新加载插件 → 改完生效。

package.json 改动(比如加了 dsh.bundle):需要重装。


9. 真实的"调试脚本"组合拳

# dev.sh — 你的开发循环
#!/bin/bash

PROFILE=${1:-web}
PLUGIN_DIR=$(pwd)

# 1. 重装
dsh plugin --profile $PROFILE remove $(basename $PLUGIN_DIR) 2>/dev/null
dsh plugin --profile $PROFILE add $PLUGIN_DIR

# 2. 验证
dsh --profile $PROFILE --dump-config | grep -A2 "$(basename $PLUGIN_DIR)"

# 3. 启动
dsh --profile $PROFILE
chmod +x dev.sh
./dev.sh web

10. 还有: dsh --profile web 自己开 HMR 模式

apps/cli/src/profile-boot.ts 真实代码 + vendor/cordis-plugin-hmr 看,HMR 模式可以在改 cordis.yml 时自动重新加载插件树(但重新加载单个插件的内部改动)。这个是 dsh 高阶功能,不在 debug 阶段必用。


posted @ 2026-08-20 15:29  刘阳-jokerleon1688  阅读(0)  评论(0)    收藏  举报