Skip to content

fix: exports 缺少 "./package.json" 导致 npm 安装后客户端半区被 DSH 组合器静默跳过(插件不出现、控制台零报错) #1

Description

@sitns

【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-gitnpm 安装
底座 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_EXPORTEDlocatePkgJson 返回 undefinedresolveMeta() 返回 nullprocessOne() 把这一行当作「不声明 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 包(也就是绝大多数人)会必现。

附:本机的判定过程

  1. 插件设置里显示已启用 → 宿主条目在。
  2. GET /dsh-ide-git/api/* 返回 403(信任围栏)而非 404 → 宿主半区已加载,路由已注册。
  3. fetch('/plugins/?id=dsh-ide-git&rev=x') → 404 → bundle 路由里没有这一行。
  4. HTTP 缓存里其它插件 bundle 全在、本包 0 命中 → 客户端 bundle 从未下发。
  5. 用真实 dsh-client-modules 代码跑 resolveMeta() → 回退分支下返回 null,修复后返回正确 clientPath

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions