@koishi-ce/plugin-market 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.mjs +29 -8
- package/package.json +3 -3
- package/src/__tests__/index.test.ts +33 -1
- package/src/node/installer.ts +36 -10
package/lib/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
2
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
3
3
|
import { Logger, Schema, Service, Time, defineProperty, pick, sleep, valueMap } from "@koishi-ce/koishi";
|
|
4
4
|
import { compare, gt, satisfies, valid } from "semver";
|
|
5
5
|
import { DataService } from "@koishi-ce/console";
|
|
6
|
-
import { readFileSync } from "node:fs";
|
|
6
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import Scanner, { resolvePackageJson } from "@koishi-ce/registry";
|
|
9
9
|
import spawn from "execa";
|
|
@@ -86,6 +86,32 @@ function loadManifest(name) {
|
|
|
86
86
|
defineProperty(meta, "$workspace", !filename.includes("node_modules"));
|
|
87
87
|
return meta;
|
|
88
88
|
}
|
|
89
|
+
/** 缓存键归一(win32 大小写不敏感),对齐 require.cache 键与探测路径的比对 */
|
|
90
|
+
function normalizeCacheKey(path) {
|
|
91
|
+
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 判断某依赖包是否有模块驻留在 require.cache(旧版本仍在内存)。
|
|
95
|
+
*
|
|
96
|
+
* 原上游实现用 require.resolve(name)(FIXME koishijs/webui#273 的报错
|
|
97
|
+
* 源):安装流程在包落盘前的探测已把该裸名记入 Bun 的进程内解析负缓
|
|
98
|
+
* 存,装完后同进程内必然抛 ResolveMessage。改为经 resolvePackageJson
|
|
99
|
+
* (fs 探测兜底)取包目录后扫 require.cache 前缀;任何环节失败都保守
|
|
100
|
+
* 返回 true——多一次重载只是进程重启,漏判才会让旧版本代码继续驻留。
|
|
101
|
+
*/
|
|
102
|
+
function isResidentInCache(name) {
|
|
103
|
+
try {
|
|
104
|
+
const dir = dirname(resolvePackageJson(name));
|
|
105
|
+
const prefixes = [normalizeCacheKey(dir + sep)];
|
|
106
|
+
try {
|
|
107
|
+
const real = realpathSync(dir);
|
|
108
|
+
if (real !== dir) prefixes.push(normalizeCacheKey(real + sep));
|
|
109
|
+
} catch {}
|
|
110
|
+
return Object.keys(__require.cache).some((key) => prefixes.some((prefix) => normalizeCacheKey(key).startsWith(prefix)));
|
|
111
|
+
} catch {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
89
115
|
function getVersions(versions) {
|
|
90
116
|
return Object.fromEntries(versions.map((item) => [item.version, pick(item, [
|
|
91
117
|
"peerDependencies",
|
|
@@ -284,12 +310,7 @@ var Installer = class extends Service {
|
|
|
284
310
|
const newDep = newDeps[name];
|
|
285
311
|
if (!local || !newDep || local.workspace) continue;
|
|
286
312
|
if (newDep.resolved === local.resolved) continue;
|
|
287
|
-
|
|
288
|
-
if (!(__require.resolve(name) in __require.cache)) continue;
|
|
289
|
-
} catch (error) {
|
|
290
|
-
logger$2.error(error);
|
|
291
|
-
}
|
|
292
|
-
this.ctx.loader.fullReload();
|
|
313
|
+
if (isResidentInCache(name)) this.ctx.loader.fullReload();
|
|
293
314
|
}
|
|
294
315
|
this.refreshData();
|
|
295
316
|
return 0;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koishi-ce/plugin-market",
|
|
3
3
|
"description": "Manage your bots and plugins with console",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.mjs",
|
|
7
7
|
"typings": "lib/index.d.ts",
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@koishi-ce/client": "^1.0.1",
|
|
64
|
-
"@koishi-ce/loader": "^1.0.
|
|
65
|
-
"@koishi-ce/plugin-config": "^1.0.
|
|
64
|
+
"@koishi-ce/loader": "^1.0.2",
|
|
65
|
+
"@koishi-ce/plugin-config": "^1.0.4",
|
|
66
66
|
"@koishijs/market": "^4.2.10",
|
|
67
67
|
"@types/semver": "^7.5.8",
|
|
68
68
|
"vue": "^3.5.12"
|
|
@@ -59,7 +59,9 @@ const { Console } = await import("@koishi-ce/console");
|
|
|
59
59
|
const { App, Service } = await import("@koishi-ce/koishi");
|
|
60
60
|
const http = (await import("@koishi-ce/plugin-http")).default;
|
|
61
61
|
const market = await import("../node/index.ts");
|
|
62
|
-
const { default: Installer } = await import(
|
|
62
|
+
const { default: Installer, isResidentInCache } = await import(
|
|
63
|
+
"../node/installer.ts"
|
|
64
|
+
);
|
|
63
65
|
const mockPlugin = (await import("@koishi-ce/plugin-mock")).default;
|
|
64
66
|
// 加载包入口占位文件(纯 re-export,无独立逻辑),保证 src 全量被加载
|
|
65
67
|
await import("../index.ts");
|
|
@@ -331,6 +333,36 @@ describe("Installer 安装链路", () => {
|
|
|
331
333
|
});
|
|
332
334
|
});
|
|
333
335
|
|
|
336
|
+
describe("isResidentInCache(装后重载判定)", () => {
|
|
337
|
+
/** 在临时 node_modules 放一个已安装包,返回其目录 */
|
|
338
|
+
function placePkg(name: string) {
|
|
339
|
+
const pkgDir = join(tmp, "node_modules", name);
|
|
340
|
+
mkdirSync(pkgDir, { recursive: true });
|
|
341
|
+
writeFileSync(
|
|
342
|
+
join(pkgDir, "package.json"),
|
|
343
|
+
JSON.stringify({ name, version: "1.0.0", main: "index.js" }),
|
|
344
|
+
);
|
|
345
|
+
writeFileSync(join(pkgDir, "index.js"), "module.exports = {}");
|
|
346
|
+
return pkgDir;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
it("包目录下有模块驻留 require.cache 时返回 true", () => {
|
|
350
|
+
const pkgDir = placePkg("koishi-plugin-resident-check");
|
|
351
|
+
// 入口经 require 进入 require.cache,模拟旧版本驻留内存
|
|
352
|
+
require(join(pkgDir, "index.js"));
|
|
353
|
+
expect(isResidentInCache("koishi-plugin-resident-check")).toBe(true);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("已安装但无模块驻留内存时返回 false", () => {
|
|
357
|
+
placePkg("koishi-plugin-idle-check");
|
|
358
|
+
expect(isResidentInCache("koishi-plugin-idle-check")).toBe(false);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("包不存在时保守返回 true(宁可多重载不漏判)", () => {
|
|
362
|
+
expect(isResidentInCache("koishi-plugin-absent-check")).toBe(true);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
334
366
|
describe("registry 配置探测", () => {
|
|
335
367
|
it("无显式 endpoint 时按 npmrc / 环境变量探测", async () => {
|
|
336
368
|
// 环境变量优先:npm_config_registry 指向本地服务
|
package/src/node/installer.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
4
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
5
5
|
import type {} from "@koishi-ce/console";
|
|
6
6
|
import {
|
|
7
7
|
type Context,
|
|
@@ -138,6 +138,37 @@ export function loadManifest(name: string) {
|
|
|
138
138
|
return meta;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
/** 缓存键归一(win32 大小写不敏感),对齐 require.cache 键与探测路径的比对 */
|
|
142
|
+
function normalizeCacheKey(path: string): string {
|
|
143
|
+
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 判断某依赖包是否有模块驻留在 require.cache(旧版本仍在内存)。
|
|
148
|
+
*
|
|
149
|
+
* 原上游实现用 require.resolve(name)(FIXME koishijs/webui#273 的报错
|
|
150
|
+
* 源):安装流程在包落盘前的探测已把该裸名记入 Bun 的进程内解析负缓
|
|
151
|
+
* 存,装完后同进程内必然抛 ResolveMessage。改为经 resolvePackageJson
|
|
152
|
+
* (fs 探测兜底)取包目录后扫 require.cache 前缀;任何环节失败都保守
|
|
153
|
+
* 返回 true——多一次重载只是进程重启,漏判才会让旧版本代码继续驻留。
|
|
154
|
+
*/
|
|
155
|
+
export function isResidentInCache(name: string): boolean {
|
|
156
|
+
try {
|
|
157
|
+
const dir = dirname(resolvePackageJson(name));
|
|
158
|
+
const prefixes = [normalizeCacheKey(dir + sep)];
|
|
159
|
+
// 符号链接布局下缓存键可能是 realpath 形态,两种前缀都查
|
|
160
|
+
try {
|
|
161
|
+
const real = realpathSync(dir);
|
|
162
|
+
if (real !== dir) prefixes.push(normalizeCacheKey(real + sep));
|
|
163
|
+
} catch {}
|
|
164
|
+
return Object.keys(require.cache).some((key) =>
|
|
165
|
+
prefixes.some((prefix) => normalizeCacheKey(key).startsWith(prefix)),
|
|
166
|
+
);
|
|
167
|
+
} catch {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
141
172
|
function getVersions(versions: RemotePackage[]) {
|
|
142
173
|
return Object.fromEntries(
|
|
143
174
|
versions
|
|
@@ -418,14 +449,9 @@ class Installer extends Service {
|
|
|
418
449
|
const newDep = newDeps[name];
|
|
419
450
|
if (!local || !newDep || local.workspace) continue;
|
|
420
451
|
if (newDep.resolved === local.resolved) continue;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
// FIXME https://github.com/koishijs/webui/issues/273
|
|
425
|
-
// I have no idea why this happens and how to fix it.
|
|
426
|
-
logger.error(error);
|
|
427
|
-
}
|
|
428
|
-
this.ctx.loader.fullReload();
|
|
452
|
+
// 版本变化且旧版本仍驻留内存时才需要整进程重载(判定不走
|
|
453
|
+
// 解析 API,规避 Bun 负缓存,见 isResidentInCache)
|
|
454
|
+
if (isResidentInCache(name)) this.ctx.loader.fullReload();
|
|
429
455
|
}
|
|
430
456
|
this.refreshData();
|
|
431
457
|
|