【BUG】npm 安装后插件完全不出现:exports 缺少 ./package.json,客户端半区被 DSH 组合器静默跳过
标题
fix: exports 缺少 "./package.json" 导致 npm 安装后客户端半区被 DSH 组合器静默跳过(插件不出现、控制台零报错)
环境
| 项 |
值 |
| DSH |
0.1.5-rc.2(DSH Desktop 2.0.13,Windows 11) |
| 宿主 Node |
v24.18.1(Electron 内置,满足 engines.node >= 18) |
| 插件 |
dsh-ide-git@0.5.5,经 dsh plugin --profile desktop add dsh-ide-git 从 npm 安装 |
| 底座 |
dsh-better-sidebar@0.19.1(在装,走底座通道) |
| git |
2.44.0.windows.1 |
现象
按 README 装好并重启 DSH 后:
- better-sidebar 的
+ → 新建标签页列表里没有 Git 条目;
- DSH 原生右侧栏的 Guide 页也没有 Git 胶囊(两条通道都没注册);
- 控制台零报错,宿主启动完全健康;
- 插件在 DSH 的插件设置里显示已启用;
- 宿主的 Git 路由
/dsh-ide-git/api/* 存在(打到信任围栏,返回 403 而不是 404),说明宿主半区是活的。
也就是说:宿主半区加载成功,客户端半区从未到达浏览器。
关键取证
// 浏览器控制台
await fetch('/plugins/?id=dsh-ide-git&rev=x').then(r => r.status, () => 'ERR')
// → 404 (bundle 路由里没有这一行)
对比同一 profile 里其它插件的客户端 bundle,都在 Chromium 的 HTTP 缓存里:
| 包 |
HTTP 缓存命中 |
dsh-better-sidebar |
8 |
dsh-scratchpad |
6 |
@linxin666/*(web-all) |
6 |
dsh-ide-git |
0 |
根因
@deepseek-ai/dsh-client-modules 的宿主半区在 locatePkgJson() 里定位一个 loader 条目的包清单。当 Loader 没有提供内部 resolveSync 时(打包后的桌面宿主就是这条路径),走的是 createRequire 回退分支:
// @deepseek-ai/dsh-client-modules lib/index.js locatePkgJson()
return {
path: createRequire(baseUrl).resolve(`${expectedPackageName}/package.json`),
packageName: expectedPackageName
};
而本包的 exports 只有两个键:
"exports": {
".": "./src/index.js",
"./client": "./src/client.js"
}
createRequire(...).resolve('dsh-ide-git/package.json') 因此抛 ERR_PACKAGE_PATH_NOT_EXPORTED → locatePkgJson 返回 undefined → resolveMeta() 返回 null → processOne() 把这一行当作「不声明 dsh.client」静默跳过:
// resolveMeta()
const decl = parseDshClient(packageName, dsh !== null && typeof dsh === "object" ? dsh.client : void 0);
if (decl === void 0 || decl.platform !== "web") {
this.pkgMeta.set(sourceKey, null);
return null; // ← 无声退出,不抛、不 warn
}
因为它是静默跳过而不是抛错,所以:
dsh-ide-git 的宿主半区照常激活(inject = ['webServer'] 满足),assertEntriesActivated 启动审计也过;
- 宿主日志里一条相关记录都没有;
- 浏览器侧永远拿不到 bundle,控制台自然也是干净的。
复现(直接跑真实组合代码)
拿 DSH 自带的 @deepseek-ai/dsh-client-modules 真实代码、用 createRequire 回退分支跑 resolveMeta:
// ctx.loader.internal === undefined → 走 createRequire 回退分支
no-internal | dsh-ide-git => NULL (silently skipped) ← 本包
no-internal | dsh-scratchpad => OK -> ...\dsh-scratchpad\client\index.js
no-internal | dsh-better-sidebar => OK -> ...\dsh-better-sidebar\lib\client.js
后两个的 exports 里都有 "./package.json": "./package.json",所以没事。
注:如果 Loader 提供了内部 resolveSync(走 nearestPackage() 向上找清单),本包也能解析成功。所以这个 bug 只在「打包宿主 / 无内部 resolveSync」的部署上出现——这也解释了为什么作者的联调环境没暴露它。
最小修复
"exports": {
".": "./src/index.js",
- "./client": "./src/client.js"
+ "./client": "./src/client.js",
+ "./package.json": "./package.json"
},
改完两条件码路径都通过:
no-internal | dsh-ide-git => OK -> ...\dsh-ide-git\src\client.js
v1-resolveSync | dsh-ide-git => OK -> ...\dsh-ide-git\src\client.js
这也是 DSH 生态里的既有惯例——对比同 profile 的其它插件:
| 包 |
exports 键 |
dsh-ide-git |
., ./client ← 缺 |
dsh-scratchpad |
., ./client, ./cordis.patch.yml, ./package.json |
dsh-better-sidebar |
., ./invariant, ./client, ./client/service, ./client/api, ./src/*, ./package.json |
建议顺带加一条自动化测试
现有三层测试(smoke / api / graph)都不过 DSH 的真实组合器,所以这个缺陷一路绿灯。建议在 tests/smoke.mjs 里补一条解析门(无 Cordis runtime、无浏览器,符合 smoke 的定位):
import { createRequire } from 'node:module'
// 1) exports 必须导出 ./package.json(打包宿主的组合器依赖它)
// 2) createRequire(import.meta.url).resolve('dsh-ide-git/package.json') 必须成功
同理可校验 files[] 与 dsh.plugin.json 的一致性(现有冒烟已覆盖版本一致,这条补上解析面)。
我这边的临时规避
在本地 fork 里补上 "./package.json" 后用 dsh plugin --profile desktop add link:<path> 安装 —— 已知有效,但用户装 npm 包(也就是绝大多数人)会必现。
附:本机的判定过程
- 插件设置里显示已启用 → 宿主条目在。
GET /dsh-ide-git/api/* 返回 403(信任围栏)而非 404 → 宿主半区已加载,路由已注册。
fetch('/plugins/?id=dsh-ide-git&rev=x') → 404 → bundle 路由里没有这一行。
- HTTP 缓存里其它插件 bundle 全在、本包 0 命中 → 客户端 bundle 从未下发。
- 用真实
dsh-client-modules 代码跑 resolveMeta() → 回退分支下返回 null,修复后返回正确 clientPath。
【BUG】npm 安装后插件完全不出现:
exports缺少./package.json,客户端半区被 DSH 组合器静默跳过标题
环境
0.1.5-rc.2(DSH Desktop 2.0.13,Windows 11)v24.18.1(Electron 内置,满足engines.node >= 18)dsh-ide-git@0.5.5,经dsh plugin --profile desktop add dsh-ide-git从 npm 安装dsh-better-sidebar@0.19.1(在装,走底座通道)2.44.0.windows.1现象
按 README 装好并重启 DSH 后:
+→ 新建标签页列表里没有 Git 条目;/dsh-ide-git/api/*存在(打到信任围栏,返回 403 而不是 404),说明宿主半区是活的。也就是说:宿主半区加载成功,客户端半区从未到达浏览器。
关键取证
对比同一 profile 里其它插件的客户端 bundle,都在 Chromium 的 HTTP 缓存里:
dsh-better-sidebardsh-scratchpad@linxin666/*(web-all)dsh-ide-git根因
@deepseek-ai/dsh-client-modules的宿主半区在locatePkgJson()里定位一个 loader 条目的包清单。当 Loader 没有提供内部resolveSync时(打包后的桌面宿主就是这条路径),走的是createRequire回退分支:而本包的
exports只有两个键:createRequire(...).resolve('dsh-ide-git/package.json')因此抛ERR_PACKAGE_PATH_NOT_EXPORTED→locatePkgJson返回undefined→resolveMeta()返回null→processOne()把这一行当作「不声明dsh.client」静默跳过:因为它是静默跳过而不是抛错,所以:
dsh-ide-git的宿主半区照常激活(inject = ['webServer']满足),assertEntriesActivated启动审计也过;复现(直接跑真实组合代码)
拿 DSH 自带的
@deepseek-ai/dsh-client-modules真实代码、用createRequire回退分支跑resolveMeta:后两个的
exports里都有"./package.json": "./package.json",所以没事。最小修复
"exports": { ".": "./src/index.js", - "./client": "./src/client.js" + "./client": "./src/client.js", + "./package.json": "./package.json" },改完两条件码路径都通过:
这也是 DSH 生态里的既有惯例——对比同 profile 的其它插件:
exports键dsh-ide-git.,./client← 缺dsh-scratchpad.,./client,./cordis.patch.yml,./package.jsondsh-better-sidebar.,./invariant,./client,./client/service,./client/api,./src/*,./package.json建议顺带加一条自动化测试
现有三层测试(smoke / api / graph)都不过 DSH 的真实组合器,所以这个缺陷一路绿灯。建议在
tests/smoke.mjs里补一条解析门(无 Cordis runtime、无浏览器,符合 smoke 的定位):同理可校验
files[]与dsh.plugin.json的一致性(现有冒烟已覆盖版本一致,这条补上解析面)。我这边的临时规避
在本地 fork 里补上
"./package.json"后用dsh plugin --profile desktop add link:<path>安装 —— 已知有效,但用户装 npm 包(也就是绝大多数人)会必现。附:本机的判定过程
GET /dsh-ide-git/api/*返回 403(信任围栏)而非 404 → 宿主半区已加载,路由已注册。fetch('/plugins/?id=dsh-ide-git&rev=x')→ 404 → bundle 路由里没有这一行。dsh-client-modules代码跑resolveMeta()→ 回退分支下返回null,修复后返回正确clientPath。