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 列表。
这条 适合"我只想看看单文件能不能跑"。
3. 方式 3: pnpm link — 跨机器开发
如果你想多个项目共享同一个本地路径:
# 在插件项目
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 复杂。
4. 方式 4: 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.bundlesorder
也就是说,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 阶段必用。

浙公网安备 33010602011771号