create-koishi-ce 1.4.0 → 1.6.0
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/README.md +6 -6
- package/lib/bin.mjs +1 -1
- package/lib/index.d.ts +6 -6
- package/lib/index.mjs +1 -1
- package/lib/{src-B1-ko5wA.mjs → src-C3GEQDVG.mjs} +45 -28
- package/package.json +1 -1
- package/src/__tests__/manifest.test.ts +4 -4
- package/src/__tests__/registry.test.ts +4 -4
- package/src/__tests__/run-default.test.ts +107 -56
- package/src/__tests__/tar-pack.ts +1 -3
- package/src/__tests__/template.test.ts +39 -13
- package/src/index.ts +13 -8
- package/src/registry.ts +1 -1
- package/src/template/README.md +18 -4
- package/src/template/koishi.yml +66 -59
- package/src/template.ts +22 -8
- package/src/utils.ts +13 -11
package/README.md
CHANGED
|
@@ -27,11 +27,11 @@ npm create koishi-ce my-app
|
|
|
27
27
|
|
|
28
28
|
## 生成的项目
|
|
29
29
|
|
|
30
|
-
- **package.json**:`type: module`、`packageManager` 钉 Bun、workspaces 含 `plugins/*` 与 `external/*`;dependencies 预装全套 `@koishi-ce` 常用插件(console、config、market、auth、admin、help、sandbox 等),并预置四行 npm alias 钉住上游名——`koishi` / `@koishijs/core` / `@koishijs/loader` 指向 `@koishi-ce/koishi-shim`、`@koishijs/plugin-console` 指向 `@koishi-ce/console-shim`,社区插件由此全部解析到 CE 框架;
|
|
31
|
-
- **koishi.yml**:按 server(端口 5140-5149)/ basic / console(自动打开、市场指向 registry.koishi.chat 镜像)/ storage / adapter / develop(开发模式启用热重载)分组预写;官方 adapter
|
|
30
|
+
- **package.json**:`type: module`、`packageManager` 钉 Bun、workspaces 含 `plugins/*` 与 `external/*`;dependencies 预装全套 `@koishi-ce` 常用插件(console、config、market、auth、admin、help、sandbox、database-sqlite 等),并预置四行 npm alias 钉住上游名——`koishi` / `@koishijs/core` / `@koishijs/loader` 指向 `@koishi-ce/koishi-shim`、`@koishijs/plugin-console` 指向 `@koishi-ce/console-shim`,社区插件由此全部解析到 CE 框架;
|
|
31
|
+
- **koishi.yml**:按 server(端口 5140-5149)/ basic / console(自动打开、市场指向 registry.koishi.chat 镜像)/ storage / adapter / develop(开发模式启用热重载)分组预写;官方 adapter 插件以 `~` 停用条目预写、不预装,装好后在控制台按需启用;数据库开箱即用——`database-sqlite` 默认启用,mongo / mysql / postgres 等其余 database 不预写占位条目,市场安装后自动出现;
|
|
32
32
|
- **开发工具链**:devDependencies 预置 `@koishi-ce/client`、`@koishi-ce/plugin-hmr` 与 `@koishi-ce/scripts`(`koishi-scripts setup` 可在 external/ 下继续创建插件项目)。
|
|
33
33
|
|
|
34
|
-
不预装任何 adapter
|
|
34
|
+
不预装任何 adapter——官方 adapter 可后续从市场安装,peer 已被 alias 钉住不会形成双实例;数据库已由默认启用的 `database-sqlite` 覆盖,其他官方 database 同样可从市场安装。
|
|
35
35
|
|
|
36
36
|
## 许可证
|
|
37
37
|
|
|
@@ -66,11 +66,11 @@ Interactive flow: project name; confirm cleaning a non-empty target directory; c
|
|
|
66
66
|
|
|
67
67
|
## Generated project
|
|
68
68
|
|
|
69
|
-
- **package.json** — `type: module`, `packageManager` pinned to Bun, workspaces covering `plugins/*` and `external/*`; dependencies preinstall the common `@koishi-ce` plugin set (console, config, market, auth, admin, help, sandbox, etc.) plus the four npm-alias lines pinning the upstream names (`koishi` / `@koishijs/core` / `@koishijs/loader` to `@koishi-ce/koishi-shim`, `@koishijs/plugin-console` to `@koishi-ce/console-shim`), so community plugins all resolve to the CE framework.
|
|
70
|
-
- **koishi.yml** — grouped prewrites for server (ports 5140-5149), basic, console (auto-open, market pointed at the registry.koishi.chat mirror), storage, adapter and develop (HMR in development mode); official adapter
|
|
69
|
+
- **package.json** — `type: module`, `packageManager` pinned to Bun, workspaces covering `plugins/*` and `external/*`; dependencies preinstall the common `@koishi-ce` plugin set (console, config, market, auth, admin, help, sandbox, database-sqlite, etc.) plus the four npm-alias lines pinning the upstream names (`koishi` / `@koishijs/core` / `@koishijs/loader` to `@koishi-ce/koishi-shim`, `@koishijs/plugin-console` to `@koishi-ce/console-shim`), so community plugins all resolve to the CE framework.
|
|
70
|
+
- **koishi.yml** — grouped prewrites for server (ports 5140-5149), basic, console (auto-open, market pointed at the registry.koishi.chat mirror), storage, adapter and develop (HMR in development mode); official adapter plugins are prewritten as `~`-disabled entries without being installed — enable them in the console after installing; the database works out of the box (`database-sqlite` enabled by default), while other official databases (mongo / mysql / postgres, ...) leave no placeholder entries and appear automatically once installed from the market.
|
|
71
71
|
- **Toolchain** — devDependencies include `@koishi-ce/client`, `@koishi-ce/plugin-hmr` and `@koishi-ce/scripts` (`koishi-scripts setup` scaffolds further plugin projects under external/).
|
|
72
72
|
|
|
73
|
-
No adapter
|
|
73
|
+
No adapter is preinstalled — official adapters can be installed from the market later, with their peers already pinned by the aliases; the database is covered by the default-enabled `database-sqlite`, and other official databases are equally one market install away.
|
|
74
74
|
|
|
75
75
|
## License
|
|
76
76
|
|
package/lib/bin.mjs
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -21,20 +21,20 @@ interface Manifest {
|
|
|
21
21
|
* 改写模板的 package.json(纯函数,导出供单测):替换项目名、标记
|
|
22
22
|
* private、版本归零。
|
|
23
23
|
*/
|
|
24
|
-
declare function renderManifest(source: Manifest, project: string, prod: boolean): string;
|
|
24
|
+
export declare function renderManifest(source: Manifest, project: string, prod: boolean): string;
|
|
25
25
|
//#endregion
|
|
26
26
|
//#region src/registry.d.ts
|
|
27
27
|
/**
|
|
28
28
|
* 从单个 .npmrc 文件提取 registry 配置项(非注释、非 scoped 的 registry= 行)。
|
|
29
29
|
* 文件不存在或未配置时返回 undefined,读文件异常一律静默吞掉。
|
|
30
30
|
*/
|
|
31
|
-
declare function readNpmrcRegistry(file: string): string | undefined;
|
|
31
|
+
export declare function readNpmrcRegistry(file: string): string | undefined;
|
|
32
32
|
/**
|
|
33
33
|
* 读取本机 npm registry 配置(优先级对齐 npm 自身:环境变量 > 项目
|
|
34
34
|
* .npmrc > 用户 ~/.npmrc)。任何一步都拿不到时返回 undefined,由调用方
|
|
35
35
|
* 回落官方源。
|
|
36
36
|
*/
|
|
37
|
-
declare function getLocalRegistry(cwd?: string, userHome?: string): string | undefined;
|
|
37
|
+
export declare function getLocalRegistry(cwd?: string, userHome?: string): string | undefined;
|
|
38
38
|
//#endregion
|
|
39
39
|
//#region src/utils.d.ts
|
|
40
40
|
/**
|
|
@@ -42,13 +42,13 @@ declare function getLocalRegistry(cwd?: string, userHome?: string): string | und
|
|
|
42
42
|
* 生态习惯;其余场景(npm、bun 及探测不到 user-agent)一律走 bun——
|
|
43
43
|
* 本 CLI 自身以 bun 为运行时(bin shebang),能执行即已具备 bun 环境。
|
|
44
44
|
*/
|
|
45
|
-
declare function detectAgent(): string;
|
|
45
|
+
export declare function detectAgent(): string;
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/index.d.ts
|
|
48
48
|
/**
|
|
49
49
|
* CLI 主流程:--help 打印用法后即返回;否则依次执行
|
|
50
50
|
* 项目名询问 → prepare(目录准备)→ scaffold(模板解包)→ initGit → install。
|
|
51
51
|
*/
|
|
52
|
-
declare function start(): Promise<void>;
|
|
52
|
+
export declare function start(): Promise<void>;
|
|
53
53
|
//#endregion
|
|
54
|
-
export {
|
|
54
|
+
export type { Manifest };
|
package/lib/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as renderManifest, i as readNpmrcRegistry, n as detectAgent, r as getLocalRegistry, t as start } from "./src-
|
|
1
|
+
import { a as renderManifest, i as readNpmrcRegistry, n as detectAgent, r as getLocalRegistry, t as start } from "./src-C3GEQDVG.mjs";
|
|
2
2
|
export { detectAgent, getLocalRegistry, readNpmrcRegistry, renderManifest, start };
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { basename,
|
|
2
|
+
import { basename, join, relative } from "node:path";
|
|
4
3
|
import { parseArgs } from "node:util";
|
|
5
4
|
import * as p from "@clack/prompts";
|
|
6
5
|
import pc from "picocolors";
|
|
7
6
|
import { homedir } from "node:os";
|
|
8
7
|
import { downloadTemplate } from "giget";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
8
|
//#region package.json
|
|
11
9
|
var package_default = {
|
|
12
10
|
name: "create-koishi-ce",
|
|
13
11
|
description: "Setup a Koishi application",
|
|
14
|
-
version: "1.
|
|
12
|
+
version: "1.6.0",
|
|
15
13
|
type: "module",
|
|
16
14
|
main: "./lib/index.mjs",
|
|
17
15
|
module: "./lib/index.mjs",
|
|
@@ -100,7 +98,7 @@ function readNpmrcRegistry(file) {
|
|
|
100
98
|
*/
|
|
101
99
|
function getLocalRegistry(cwd = process.cwd(), userHome = homedir()) {
|
|
102
100
|
const candidates = [
|
|
103
|
-
|
|
101
|
+
Bun.env["npm_config_registry"],
|
|
104
102
|
readNpmrcRegistry(join(cwd, ".npmrc")),
|
|
105
103
|
readNpmrcRegistry(join(userHome, ".npmrc"))
|
|
106
104
|
];
|
|
@@ -198,11 +196,13 @@ function writePackageJson(rootDir, project, prod) {
|
|
|
198
196
|
* 不会拉入 npm 官方全家桶形成第二份框架副本;市场安装亦不改写该声明
|
|
199
197
|
* (installer 的 isGuardedRequest 护栏将 npm:@koishi-ce alias 与
|
|
200
198
|
* workspace: 同等保护);
|
|
201
|
-
* - koishi.yml
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
199
|
+
* - koishi.yml 采用配置页导出形态:插件键带 uid 实例后缀,分组带中文
|
|
200
|
+
* $label 与 $collapsed 元数据;database-sqlite 默认启用,开箱即得数
|
|
201
|
+
* 据库;依赖数据库但非必需的插件(admin / bind / auth / broadcast /
|
|
202
|
+
* callme)保持 ~ 禁用;本仓不再分发的 adapter 官方插件只以 ~ 禁用条
|
|
203
|
+
* 目预写、不预装——loader 跳过禁用条目,装好后在控制台启用(mongo /
|
|
204
|
+
* mysql / postgres 等未再分发的 database 不再预写占位条目,市场装后
|
|
205
|
+
* 自动出现);
|
|
206
206
|
* - 依赖版本统一 ^1.0.0 区间(安装时取最新 1.x),shim 版本例外(冻结线)。
|
|
207
207
|
*/
|
|
208
208
|
/**
|
|
@@ -211,7 +211,7 @@ function writePackageJson(rootDir, project, prod) {
|
|
|
211
211
|
* 恒存在)。
|
|
212
212
|
*/
|
|
213
213
|
function locateTemplateDir() {
|
|
214
|
-
const base =
|
|
214
|
+
const base = import.meta.dir;
|
|
215
215
|
for (const dir of [join(base, "template"), join(base, "../src/template")]) if (existsSync(dir)) return dir;
|
|
216
216
|
throw new Error("create-koishi-ce 内置模板目录缺失(src/template)");
|
|
217
217
|
}
|
|
@@ -239,7 +239,12 @@ function baseManifest() {
|
|
|
239
239
|
scripts: {
|
|
240
240
|
start: "koishi start",
|
|
241
241
|
dev: "NODE_ENV=development koishi start",
|
|
242
|
-
new: "koishi-scripts setup"
|
|
242
|
+
new: "koishi-scripts setup",
|
|
243
|
+
clone: "koishi-scripts clone",
|
|
244
|
+
build: "koishi-scripts build",
|
|
245
|
+
"release:version": "koishi-scripts version",
|
|
246
|
+
"release:dryrun": "bun run release:version && bun run build && koishi-scripts publish --dry-run",
|
|
247
|
+
release: "bun run release:version && bun run build && koishi-scripts publish"
|
|
243
248
|
},
|
|
244
249
|
dependencies: {
|
|
245
250
|
"@koishi-ce/koishi": "^1.0.0",
|
|
@@ -303,23 +308,28 @@ function baseManifest() {
|
|
|
303
308
|
* 本 CLI 自身以 bun 为运行时(bin shebang),能执行即已具备 bun 环境。
|
|
304
309
|
*/
|
|
305
310
|
function detectAgent() {
|
|
306
|
-
const ua =
|
|
311
|
+
const ua = Bun.env["npm_config_user_agent"] ?? "";
|
|
307
312
|
if (ua.startsWith("yarn")) return "yarn";
|
|
308
313
|
if (ua.startsWith("pnpm")) return "pnpm";
|
|
309
314
|
return "bun";
|
|
310
315
|
}
|
|
311
316
|
/** 静默执行命令探测其是否可用(如 git --version),失败即视为不可用 */
|
|
312
317
|
function supports(command) {
|
|
313
|
-
return spawnSync(
|
|
318
|
+
return Bun.spawnSync({
|
|
319
|
+
cmd: [...command],
|
|
320
|
+
stdout: "ignore",
|
|
321
|
+
stderr: "ignore"
|
|
322
|
+
}).success;
|
|
314
323
|
}
|
|
315
324
|
/** 读 git 全局配置单项(读不到 → 空串) */
|
|
316
325
|
function gitConfig(key) {
|
|
317
|
-
const res = spawnSync(
|
|
326
|
+
const res = Bun.spawnSync([
|
|
327
|
+
"git",
|
|
318
328
|
"config",
|
|
319
329
|
"--get",
|
|
320
330
|
key
|
|
321
|
-
]
|
|
322
|
-
return res.
|
|
331
|
+
]);
|
|
332
|
+
return res.success ? res.stdout.toString().trim() : "";
|
|
323
333
|
}
|
|
324
334
|
/** 递归清空目录内容(目录本身保留)。 */
|
|
325
335
|
function emptyDir(root) {
|
|
@@ -473,12 +483,15 @@ async function scaffold() {
|
|
|
473
483
|
async function initGit() {
|
|
474
484
|
if (!argv.git || !supports(["git", "--version"])) return;
|
|
475
485
|
const branch = gitConfig("init.defaultBranch") || "main";
|
|
476
|
-
spawnSync(
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
486
|
+
Bun.spawnSync({
|
|
487
|
+
cmd: [
|
|
488
|
+
"git",
|
|
489
|
+
"init",
|
|
490
|
+
"-b",
|
|
491
|
+
branch
|
|
492
|
+
],
|
|
493
|
+
stdout: "ignore",
|
|
494
|
+
stderr: "ignore",
|
|
482
495
|
cwd: rootDir
|
|
483
496
|
});
|
|
484
497
|
console.log(pc.green(` 已初始化 git 仓库(分支 ${branch})。\n`));
|
|
@@ -492,15 +505,19 @@ async function install() {
|
|
|
492
505
|
const agent = detectAgent();
|
|
493
506
|
const startArgs = agent === "yarn" ? ["start"] : ["run", "start"];
|
|
494
507
|
if (await confirm("现在安装依赖并启动吗?")) {
|
|
495
|
-
if (spawnSync(
|
|
496
|
-
|
|
508
|
+
if (!Bun.spawnSync({
|
|
509
|
+
cmd: [agent, "install"],
|
|
510
|
+
stdout: "inherit",
|
|
511
|
+
stderr: "inherit",
|
|
497
512
|
cwd: rootDir
|
|
498
|
-
}).
|
|
513
|
+
}).success) {
|
|
499
514
|
console.log(pc.red(" 依赖安装失败,请检查上方日志。"));
|
|
500
515
|
return;
|
|
501
516
|
}
|
|
502
|
-
spawnSync(
|
|
503
|
-
|
|
517
|
+
Bun.spawnSync({
|
|
518
|
+
cmd: [agent, ...startArgs],
|
|
519
|
+
stdout: "inherit",
|
|
520
|
+
stderr: "inherit",
|
|
504
521
|
cwd: rootDir
|
|
505
522
|
});
|
|
506
523
|
} else {
|
package/package.json
CHANGED
|
@@ -46,13 +46,13 @@ test("renderManifest prod 模式:删除 workspaces 与 devDependencies", () =>
|
|
|
46
46
|
|
|
47
47
|
test("detectAgent:yarn / pnpm 跟随探测,其余一律 bun", () => {
|
|
48
48
|
const key = "npm_config_user_agent";
|
|
49
|
-
|
|
49
|
+
Bun.env[key] =
|
|
50
50
|
"npm/10.9.2 node/v22.14.0 x64 workspaces/false";
|
|
51
51
|
expect(detectAgent()).toBe("bun");
|
|
52
|
-
|
|
52
|
+
Bun.env[key] = "yarn/1.22.22 npm/? node/v22.14.0 x64";
|
|
53
53
|
expect(detectAgent()).toBe("yarn");
|
|
54
|
-
|
|
54
|
+
Bun.env[key] = "pnpm/10.12.1 npm/? node/v22.14.0 x64";
|
|
55
55
|
expect(detectAgent()).toBe("pnpm");
|
|
56
|
-
delete
|
|
56
|
+
delete Bun.env[key];
|
|
57
57
|
expect(detectAgent()).toBe("bun");
|
|
58
58
|
});
|
|
@@ -45,22 +45,22 @@ test("getLocalRegistry:环境变量优先且校验协议,非法值跳过", (
|
|
|
45
45
|
const isolated = mkdtempSync(
|
|
46
46
|
join(tmpdir(), "ckc-npmrc-"),
|
|
47
47
|
);
|
|
48
|
-
|
|
48
|
+
Bun.env[key] = "https://registry.example.com/";
|
|
49
49
|
expect(getLocalRegistry(isolated, isolated)).toBe(
|
|
50
50
|
"https://registry.example.com/",
|
|
51
51
|
);
|
|
52
52
|
// 非 http(s) 的值视为未配置,继续走后续候选
|
|
53
|
-
|
|
53
|
+
Bun.env[key] = "ftp://not-a-registry/";
|
|
54
54
|
expect(
|
|
55
55
|
getLocalRegistry(isolated, isolated),
|
|
56
56
|
).toBeUndefined();
|
|
57
|
-
delete
|
|
57
|
+
delete Bun.env[key];
|
|
58
58
|
rmSync(isolated, { recursive: true, force: true });
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
test("getLocalRegistry:项目 .npmrc 优先于用户级 .npmrc,均无配置时返回 undefined", () => {
|
|
62
62
|
const key = "npm_config_registry";
|
|
63
|
-
delete
|
|
63
|
+
delete Bun.env[key];
|
|
64
64
|
const dir = mkdtempSync(join(tmpdir(), "ckc-npmrc-"));
|
|
65
65
|
const emptyHome = mkdtempSync(
|
|
66
66
|
join(tmpdir(), "ckc-home-"),
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
expect,
|
|
9
9
|
it,
|
|
10
10
|
mock,
|
|
11
|
+
spyOn,
|
|
11
12
|
} from "bun:test";
|
|
12
|
-
import { spawnSync } from "node:child_process";
|
|
13
13
|
import {
|
|
14
14
|
existsSync,
|
|
15
15
|
mkdirSync,
|
|
@@ -39,9 +39,9 @@ const previousArgv = process.argv.slice();
|
|
|
39
39
|
const previousExit = process.exit;
|
|
40
40
|
// 保持无 user-agent 的探测环境(detectAgent 走 bun 分支)
|
|
41
41
|
const userAgentKey = "npm_config_user_agent";
|
|
42
|
-
const hadUserAgent = userAgentKey in
|
|
43
|
-
const previousUserAgent =
|
|
44
|
-
delete
|
|
42
|
+
const hadUserAgent = userAgentKey in Bun.env;
|
|
43
|
+
const previousUserAgent = Bun.env[userAgentKey];
|
|
44
|
+
delete Bun.env[userAgentKey];
|
|
45
45
|
|
|
46
46
|
/** @clack/prompts 的可编程答案队列(按 prompt 类型分发) */
|
|
47
47
|
const nameAnswers: string[] = [];
|
|
@@ -62,58 +62,109 @@ const spawnLog: Array<{
|
|
|
62
62
|
}> = [];
|
|
63
63
|
let installExit = 0;
|
|
64
64
|
|
|
65
|
-
// 本文件求值时
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
const realSpawnSync = spawnSync;
|
|
65
|
+
// 本文件求值时 Bun.spawnSync 恒为真实实现(本仓测试中唯一的子进程 mock
|
|
66
|
+
// 注册点),持有真实引用供未知命令透传;mock 改写会波及其它测试文件经
|
|
67
|
+
// 全局命名空间发起的调用(如 setup 的 git init、release 的子进程测试),
|
|
68
|
+
// 透传保证它们拿到真实行为
|
|
69
|
+
const realSpawnSync = Bun.spawnSync;
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
71
|
+
/** 构造 SyncSubprocess 形态的假结果(补全 resourceUsage / pid 等必要字段) */
|
|
72
|
+
function fakeResult(exitCode: number, stdout = "") {
|
|
73
|
+
return {
|
|
74
|
+
exitCode,
|
|
75
|
+
success: exitCode === 0,
|
|
76
|
+
stdout,
|
|
77
|
+
stderr: "",
|
|
78
|
+
} as unknown as ReturnType<typeof Bun.spawnSync>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Bun.spawnSync 调用选项中被本 mock 关心的子集(透传时原样保留) */
|
|
82
|
+
interface SpawnOptionsLike {
|
|
83
|
+
cmd?: readonly string[];
|
|
84
|
+
cwd?: string;
|
|
85
|
+
stdout?: "ignore" | "inherit" | "pipe";
|
|
86
|
+
stderr?: "ignore" | "inherit" | "pipe";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 把 Bun.spawnSync 的两种调用形态归一为 { cmd, args, cwd, passthrough }。
|
|
91
|
+
* invoke 为 unknown 数组(重载联合无法静态表达),内部运行时收窄。
|
|
92
|
+
*/
|
|
93
|
+
function normalize(invoke: readonly unknown[]): {
|
|
94
|
+
cmd: string;
|
|
95
|
+
args: string[];
|
|
96
|
+
cwd?: string;
|
|
97
|
+
passthrough: () => ReturnType<typeof Bun.spawnSync>;
|
|
98
|
+
} {
|
|
99
|
+
const first = invoke[0];
|
|
100
|
+
const options = (
|
|
101
|
+
Array.isArray(first) ? (invoke[1] ?? {}) : first
|
|
102
|
+
) as SpawnOptionsLike;
|
|
103
|
+
const argv = (
|
|
104
|
+
Array.isArray(first) ? first : (options.cmd ?? [])
|
|
105
|
+
) as readonly string[];
|
|
106
|
+
const cmd = argv[0] ?? "";
|
|
107
|
+
const args = [...argv.slice(1)];
|
|
108
|
+
return {
|
|
109
|
+
cmd,
|
|
110
|
+
args,
|
|
111
|
+
...(options.cwd === undefined
|
|
112
|
+
? {}
|
|
113
|
+
: { cwd: options.cwd }),
|
|
114
|
+
passthrough: () =>
|
|
115
|
+
realSpawnSync({
|
|
116
|
+
cmd: [cmd, ...args],
|
|
117
|
+
...(options.stdout === undefined
|
|
118
|
+
? {}
|
|
119
|
+
: { stdout: options.stdout }),
|
|
120
|
+
...(options.stderr === undefined
|
|
121
|
+
? {}
|
|
122
|
+
: { stderr: options.stderr }),
|
|
123
|
+
...(options.cwd === undefined
|
|
124
|
+
? {}
|
|
125
|
+
: { cwd: options.cwd }),
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 拦截 Bun.spawnSync:git 探测 / 配置读取走桩,其余记录或透传真实执行 */
|
|
131
|
+
spyOn(Bun, "spawnSync").mockImplementation(((
|
|
132
|
+
...invoke: readonly unknown[]
|
|
133
|
+
) => {
|
|
134
|
+
const { cmd, args, cwd, passthrough } = normalize(invoke);
|
|
135
|
+
if (cmd === "git" && args[0] === "--version")
|
|
136
|
+
return fakeResult(0);
|
|
137
|
+
if (
|
|
138
|
+
cmd === "git" &&
|
|
139
|
+
args[0] === "config" &&
|
|
140
|
+
args[1] === "--get"
|
|
141
|
+
) {
|
|
142
|
+
const key = args[2];
|
|
143
|
+
const table: Record<string, string> = {
|
|
144
|
+
"user.name": "Tester",
|
|
145
|
+
"user.email": "t@e.st",
|
|
146
|
+
"init.defaultBranch": "trunk",
|
|
147
|
+
};
|
|
148
|
+
const value =
|
|
149
|
+
key !== undefined ? table[key] : undefined;
|
|
150
|
+
return fakeResult(
|
|
151
|
+
value === undefined ? 1 : 0,
|
|
152
|
+
value === undefined ? "" : `${value}\n`,
|
|
114
153
|
);
|
|
115
|
-
}
|
|
116
|
-
|
|
154
|
+
}
|
|
155
|
+
// exactOptionalPropertyTypes:cwd 未传时不落键,避免显式写入 undefined
|
|
156
|
+
spawnLog.push({
|
|
157
|
+
cmd,
|
|
158
|
+
args,
|
|
159
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
160
|
+
});
|
|
161
|
+
// 依赖安装可编程;agent 启动不真实拉起
|
|
162
|
+
if (args[0] === "install") return fakeResult(installExit);
|
|
163
|
+
if (cmd === "bun" && args[0] === "run")
|
|
164
|
+
return fakeResult(0);
|
|
165
|
+
// 其余命令透传真实执行(副作用均在各自测试的临时目录内)
|
|
166
|
+
return passthrough();
|
|
167
|
+
}) as never);
|
|
117
168
|
|
|
118
169
|
/** process.exit 替换为异常,供断言「直接退出」的分支 */
|
|
119
170
|
// erasableSyntaxOnly 禁用参数属性,改为显式字段声明
|
|
@@ -156,9 +207,9 @@ afterAll(() => {
|
|
|
156
207
|
process.exit = previousExit;
|
|
157
208
|
process.argv = previousArgv;
|
|
158
209
|
if (hadUserAgent) {
|
|
159
|
-
|
|
210
|
+
Bun.env[userAgentKey] = previousUserAgent;
|
|
160
211
|
} else {
|
|
161
|
-
delete
|
|
212
|
+
delete Bun.env[userAgentKey];
|
|
162
213
|
}
|
|
163
214
|
rmSync(workspaceRoot, { recursive: true, force: true });
|
|
164
215
|
});
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
* 端到端链路间接验证。
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { gzipSync } from "node:zlib";
|
|
15
|
-
|
|
16
14
|
const BLOCK = 512;
|
|
17
15
|
const encoder = new TextEncoder();
|
|
18
16
|
|
|
@@ -207,5 +205,5 @@ export async function tarPack(
|
|
|
207
205
|
}
|
|
208
206
|
|
|
209
207
|
// gzip 压缩为 .tgz(与 npm registry 的 tarball 同构)
|
|
210
|
-
return gzipSync(raw);
|
|
208
|
+
return Bun.gzipSync(raw);
|
|
211
209
|
}
|
|
@@ -48,6 +48,27 @@ test("内置模板 packageManager 钉 Bun,脚本用 Bun Shell 环境变量前
|
|
|
48
48
|
);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
test("内置模板提供插件开发全链脚本入口(koishi-scripts 子命令形态)", () => {
|
|
52
|
+
const { scripts } = baseManifest() as unknown as {
|
|
53
|
+
scripts: Record<string, string>;
|
|
54
|
+
};
|
|
55
|
+
// 插件开发链 = devDep @koishi-ce/scripts 的 koishi-scripts CLI 子命令;
|
|
56
|
+
// new 用主命令名 setup(new/create/init 是 koishi-scripts 的别名)
|
|
57
|
+
expect(scripts["new"]).toBe("koishi-scripts setup");
|
|
58
|
+
expect(scripts["clone"]).toBe("koishi-scripts clone");
|
|
59
|
+
expect(scripts["build"]).toBe("koishi-scripts build");
|
|
60
|
+
expect(scripts["release:version"]).toBe(
|
|
61
|
+
"koishi-scripts version",
|
|
62
|
+
);
|
|
63
|
+
// release 链 = version → build → publish 三环串联(前环自引用脚本)
|
|
64
|
+
expect(scripts["release"]).toBe(
|
|
65
|
+
"bun run release:version && bun run build && koishi-scripts publish",
|
|
66
|
+
);
|
|
67
|
+
expect(scripts["release:dryrun"]).toBe(
|
|
68
|
+
"bun run release:version && bun run build && koishi-scripts publish --dry-run",
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
51
72
|
test("内置模板以 npm alias 钉住 koishi 裸名,版本保持 4.18.x 冻结线", () => {
|
|
52
73
|
const request = baseManifest().dependencies?.["koishi"];
|
|
53
74
|
expect(typeof request).toBe("string");
|
|
@@ -92,51 +113,56 @@ test("内置模板静态文件齐备,koishi.yml 预配 market 镜像源", () =
|
|
|
92
113
|
);
|
|
93
114
|
});
|
|
94
115
|
|
|
95
|
-
test("koishi.yml
|
|
116
|
+
test("koishi.yml 预写策略:sqlite 默认启用开箱数据库,非必需件与官方 adapter 保持 ~ 禁用", () => {
|
|
96
117
|
const yml = templateFiles["koishi.yml"] ?? "";
|
|
97
|
-
//
|
|
118
|
+
// 配置页导出形态:插件键带 uid 实例后缀,分组带 $collapsed / $label 元数据
|
|
119
|
+
expect(yml).toMatch(/database-sqlite:[a-z0-9]{6}:/);
|
|
120
|
+
expect(yml).toContain("$collapsed: true");
|
|
121
|
+
expect(yml).toContain("$label: ");
|
|
122
|
+
// 依赖数据库但非必需 / 暂无需启用的 CE 插件:预装但 ~ 禁用
|
|
98
123
|
for (const name of [
|
|
99
124
|
"~admin",
|
|
100
125
|
"~bind",
|
|
101
126
|
"~broadcast",
|
|
102
127
|
"~callme",
|
|
103
|
-
"~analytics",
|
|
104
128
|
"~auth",
|
|
105
|
-
"~dataview",
|
|
106
|
-
"~rate-limit",
|
|
107
129
|
"~inspect",
|
|
108
130
|
"~server-temp",
|
|
109
131
|
"~mock",
|
|
110
132
|
]) {
|
|
111
133
|
expect(yml).toContain(name);
|
|
112
134
|
}
|
|
113
|
-
//
|
|
135
|
+
// 随模板默认启用的 CE 插件(含 sqlite 数据库):条目存在且无 ~ 前缀
|
|
114
136
|
for (const name of [
|
|
115
|
-
"
|
|
137
|
+
"database-sqlite",
|
|
138
|
+
"dataview",
|
|
139
|
+
"analytics",
|
|
140
|
+
"rate-limit",
|
|
116
141
|
"echo",
|
|
117
|
-
"
|
|
142
|
+
"assets-local",
|
|
118
143
|
"status",
|
|
119
144
|
"sandbox",
|
|
145
|
+
"theme-vanilla",
|
|
120
146
|
]) {
|
|
121
147
|
expect(yml).toContain(name);
|
|
148
|
+
expect(yml).not.toContain(`~${name}`);
|
|
122
149
|
}
|
|
123
|
-
// database-sqlite
|
|
150
|
+
// database-sqlite 依赖在册(随模板预装,启用即得数据库)
|
|
124
151
|
expect(
|
|
125
152
|
baseManifest().dependencies?.[
|
|
126
153
|
"@koishi-ce/plugin-database-sqlite"
|
|
127
154
|
],
|
|
128
155
|
).toBe("^1.0.0");
|
|
129
|
-
|
|
130
|
-
//
|
|
156
|
+
// 官方 adapter 只以 ~ 禁用条目预写(未预装,市场装后启用);
|
|
157
|
+
// 未再分发的 database(mongo / mysql / postgres)不再预写占位条目
|
|
131
158
|
for (const name of [
|
|
132
159
|
"~adapter-discord",
|
|
133
160
|
"~adapter-telegram",
|
|
134
161
|
"~adapter-qq",
|
|
135
|
-
"~database-mongo",
|
|
136
|
-
"~database-postgres",
|
|
137
162
|
]) {
|
|
138
163
|
expect(yml).toContain(name);
|
|
139
164
|
}
|
|
165
|
+
expect(yml).not.toContain("database-mongo");
|
|
140
166
|
// 模板依赖里不得出现官方 adapter / database 包名(只预写不预装)
|
|
141
167
|
expect(JSON.stringify(baseManifest())).not.toContain(
|
|
142
168
|
"adapter-",
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
* - remote.ts --template 远程模板的下载-解包-改写全流程
|
|
23
23
|
* - template.ts 内置 @koishi-ce 模板(静态文件与 baseManifest)
|
|
24
24
|
*/
|
|
25
|
-
import { spawnSync } from "node:child_process";
|
|
26
25
|
import {
|
|
27
26
|
existsSync,
|
|
28
27
|
mkdirSync,
|
|
@@ -216,8 +215,10 @@ async function scaffold() {
|
|
|
216
215
|
async function initGit() {
|
|
217
216
|
if (!argv.git || !supports(["git", "--version"])) return;
|
|
218
217
|
const branch = gitConfig("init.defaultBranch") || "main";
|
|
219
|
-
spawnSync(
|
|
220
|
-
|
|
218
|
+
Bun.spawnSync({
|
|
219
|
+
cmd: ["git", "init", "-b", branch],
|
|
220
|
+
stdout: "ignore",
|
|
221
|
+
stderr: "ignore",
|
|
221
222
|
cwd: rootDir,
|
|
222
223
|
});
|
|
223
224
|
console.log(
|
|
@@ -238,18 +239,22 @@ async function install() {
|
|
|
238
239
|
agent === "yarn" ? ["start"] : ["run", "start"];
|
|
239
240
|
const yes = await confirm("现在安装依赖并启动吗?");
|
|
240
241
|
if (yes) {
|
|
241
|
-
const installed = spawnSync(
|
|
242
|
-
|
|
242
|
+
const installed = Bun.spawnSync({
|
|
243
|
+
cmd: [agent, "install"],
|
|
244
|
+
stdout: "inherit",
|
|
245
|
+
stderr: "inherit",
|
|
243
246
|
cwd: rootDir,
|
|
244
247
|
});
|
|
245
|
-
if (installed.
|
|
248
|
+
if (!installed.success) {
|
|
246
249
|
console.log(
|
|
247
250
|
pc.red(" 依赖安装失败,请检查上方日志。"),
|
|
248
251
|
);
|
|
249
252
|
return;
|
|
250
253
|
}
|
|
251
|
-
spawnSync(
|
|
252
|
-
|
|
254
|
+
Bun.spawnSync({
|
|
255
|
+
cmd: [agent, ...startArgs],
|
|
256
|
+
stdout: "inherit",
|
|
257
|
+
stderr: "inherit",
|
|
253
258
|
cwd: rootDir,
|
|
254
259
|
});
|
|
255
260
|
} else {
|
package/src/registry.ts
CHANGED
|
@@ -45,7 +45,7 @@ export function getLocalRegistry(
|
|
|
45
45
|
userHome: string = homedir(),
|
|
46
46
|
): string | undefined {
|
|
47
47
|
const candidates = [
|
|
48
|
-
|
|
48
|
+
Bun.env["npm_config_registry"],
|
|
49
49
|
readNpmrcRegistry(join(cwd, ".npmrc")),
|
|
50
50
|
readNpmrcRegistry(join(userHome, ".npmrc")),
|
|
51
51
|
];
|
package/src/template/README.md
CHANGED
|
@@ -18,9 +18,9 @@ bun run dev # 启动(开发模式,启用 HMR 热更新)
|
|
|
18
18
|
|
|
19
19
|
## 预装与预写
|
|
20
20
|
|
|
21
|
-
- **已预装**:基础与控制台插件(@koishi-ce
|
|
22
|
-
- **数据库开箱即用**:`@koishi-ce/plugin-database-sqlite`
|
|
23
|
-
- **只预写、未预装**:adapter(discord / telegram / qq
|
|
21
|
+
- **已预装**:基础与控制台插件(@koishi-ce 全家桶)。`koishi.yml` 为配置页导出形态:插件键带 uid 实例后缀,分组带中文 `$label` 标签。其中依赖数据库但非必需的(admin / bind / broadcast / callme / auth)与暂无需启用的(inspect / server-temp / mock)以 `~` 前缀保持禁用——数据库已默认启用,去掉对应 `~` 即可。
|
|
22
|
+
- **数据库开箱即用**:`@koishi-ce/plugin-database-sqlite` 已预装并默认启用,数据落在 `data/koishi.db`。
|
|
23
|
+
- **只预写、未预装**:adapter(discord / telegram / qq …)官方插件以 `~` 禁用条目预写在 `koishi.yml` 的 `group:adapter`——loader 会跳过禁用条目,不安装也能正常启动;需要时在控制台「插件市场」搜索安装,回到配置页点击启用即可(mongo / mysql / postgres 等其他数据库插件未预写占位,市场安装后自动出现条目)。
|
|
24
24
|
|
|
25
25
|
## 安装插件
|
|
26
26
|
|
|
@@ -30,4 +30,18 @@ bun run dev # 启动(开发模式,启用 HMR 热更新)
|
|
|
30
30
|
|
|
31
31
|
## 自定义插件
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
手动在 `plugins/` 目录下创建插件包,或经脚手架进入 `external/` 工作区:
|
|
34
|
+
|
|
35
|
+
- `bun run new <名称>` —— 生成插件骨架到 `external/`(`--console` 附带控制台前端扩展)
|
|
36
|
+
- `bun run clone <仓库>` —— 克隆已有插件源码到 `external/` 本地联动调试
|
|
37
|
+
|
|
38
|
+
两种来源的插件都在 `koishi.yml` 中以相对路径引用(如 `./plugins/my-plugin`、`./external/my-plugin`)即可启用;Bun 直接加载 TypeScript 源码,无需预编译。
|
|
39
|
+
|
|
40
|
+
## 插件构建与发布
|
|
41
|
+
|
|
42
|
+
针对 `external/` 工作区的插件项目:
|
|
43
|
+
|
|
44
|
+
- `bun run build` —— 串行构建 `external/` 下全部可构建项目
|
|
45
|
+
- `bun run release:version` —— 消费 pending changeset 提升版本号
|
|
46
|
+
- `bun run release:dryrun` —— 完整发布链演练(version → build → publish --dry-run)
|
|
47
|
+
- `bun run release` —— 完整发布链(version → build → publish)
|
package/src/template/koishi.yml
CHANGED
|
@@ -1,69 +1,76 @@
|
|
|
1
1
|
plugins:
|
|
2
2
|
group:server:
|
|
3
|
-
|
|
3
|
+
$collapsed: true
|
|
4
|
+
$label: 服务器
|
|
5
|
+
server:nhf2rf:
|
|
4
6
|
port: 5140
|
|
5
7
|
maxPort: 5149
|
|
6
|
-
~server-temp: {}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
help: {}
|
|
15
|
-
http: {}
|
|
16
|
-
~inspect: {}
|
|
17
|
-
locales: {}
|
|
18
|
-
proxy-agent: {}
|
|
19
|
-
~rate-limit: {}
|
|
8
|
+
~server-temp:z9nmes: {}
|
|
9
|
+
~adapter-satori:euyuu6: {}
|
|
10
|
+
group:storage:
|
|
11
|
+
$collapsed: true
|
|
12
|
+
$label: 持久化
|
|
13
|
+
assets-local:0bp951: {}
|
|
14
|
+
database-sqlite:60m3ri:
|
|
15
|
+
path: data/koishi.db
|
|
20
16
|
group:console:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
config: {}
|
|
25
|
-
console:
|
|
17
|
+
$collapsed: true
|
|
18
|
+
$label: 控制台
|
|
19
|
+
console:p179rv:
|
|
26
20
|
open: true
|
|
27
|
-
~
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
market:
|
|
21
|
+
~auth:decpjz: {}
|
|
22
|
+
oobe:kw667y: {}
|
|
23
|
+
config:hrvlki: {}
|
|
24
|
+
market:n1dg4c:
|
|
32
25
|
search:
|
|
33
26
|
endpoint: https://registry.koishi.chat/index.json
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
sandbox: {}
|
|
37
|
-
status: {}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
explorer:5e7k13: {}
|
|
28
|
+
actions:ttgal7: {}
|
|
29
|
+
sandbox:bbkwu5: {}
|
|
30
|
+
status:hc51wq: {}
|
|
31
|
+
logger:akzyyp: {}
|
|
32
|
+
dataview:eklvzg: {}
|
|
33
|
+
analytics:smgmme: {}
|
|
34
|
+
insight:ozp3by: {}
|
|
35
|
+
notifier:30g7rg: {}
|
|
36
|
+
theme-vanilla:029kuh: {}
|
|
37
|
+
welcome:q7m2vx: {}
|
|
38
|
+
group:basic:
|
|
39
|
+
$collapsed: true
|
|
40
|
+
$label: 基础库
|
|
41
|
+
commands:lgrbci: {}
|
|
42
|
+
help:nrbz2h: {}
|
|
43
|
+
echo:t6r9sk: {}
|
|
44
|
+
~admin:e0sr2y: {}
|
|
45
|
+
~bind:zxx2sn: {}
|
|
46
|
+
~broadcast:q8x3jd: {}
|
|
47
|
+
~callme:m4v7wt: {}
|
|
48
|
+
~inspect:2t107r: {}
|
|
49
|
+
locales:bi9lco: {}
|
|
50
|
+
http:rwe3ck: {}
|
|
51
|
+
proxy-agent:p6jj15: {}
|
|
52
|
+
rate-limit:cghjaw: {}
|
|
49
53
|
group:adapter:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
~adapter-
|
|
53
|
-
~adapter-
|
|
54
|
-
~adapter-
|
|
55
|
-
~adapter-
|
|
56
|
-
~adapter-
|
|
57
|
-
~adapter-
|
|
58
|
-
~adapter-
|
|
59
|
-
~adapter-
|
|
60
|
-
~adapter-
|
|
61
|
-
~adapter-
|
|
62
|
-
~adapter-
|
|
63
|
-
~adapter-
|
|
64
|
-
~adapter-
|
|
54
|
+
$collapsed: true
|
|
55
|
+
$label: 适配器
|
|
56
|
+
~adapter-qq:lr3f6b: {}
|
|
57
|
+
~adapter-telegram:memc76: {}
|
|
58
|
+
~adapter-discord:4n11rk: {}
|
|
59
|
+
~adapter-kook:2s0qw0: {}
|
|
60
|
+
~adapter-wecom:euh20s: {}
|
|
61
|
+
~adapter-wechat-official:ufqejg: {}
|
|
62
|
+
~adapter-dingtalk:76isk2: {}
|
|
63
|
+
~adapter-lark:vtfsz0: {}
|
|
64
|
+
~adapter-line:hfsd6d: {}
|
|
65
|
+
~adapter-slack:ctdrr0: {}
|
|
66
|
+
~adapter-whatsapp:xn68t0: {}
|
|
67
|
+
~adapter-mail:cjnxrb: {}
|
|
68
|
+
~adapter-matrix:phadis: {}
|
|
69
|
+
~adapter-zulip:fg64tb: {}
|
|
65
70
|
group:develop:
|
|
66
|
-
$if: env.NODE_ENV === 'development'
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
$if: "env.NODE_ENV === 'development'"
|
|
72
|
+
$collapsed: true
|
|
73
|
+
$label: 开发
|
|
74
|
+
hmr:i3xkbw:
|
|
75
|
+
root: "."
|
|
76
|
+
~mock:w2y5nh: {}
|
package/src/template.ts
CHANGED
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
* 不会拉入 npm 官方全家桶形成第二份框架副本;市场安装亦不改写该声明
|
|
26
26
|
* (installer 的 isGuardedRequest 护栏将 npm:@koishi-ce alias 与
|
|
27
27
|
* workspace: 同等保护);
|
|
28
|
-
* - koishi.yml
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* - koishi.yml 采用配置页导出形态:插件键带 uid 实例后缀,分组带中文
|
|
29
|
+
* $label 与 $collapsed 元数据;database-sqlite 默认启用,开箱即得数
|
|
30
|
+
* 据库;依赖数据库但非必需的插件(admin / bind / auth / broadcast /
|
|
31
|
+
* callme)保持 ~ 禁用;本仓不再分发的 adapter 官方插件只以 ~ 禁用条
|
|
32
|
+
* 目预写、不预装——loader 跳过禁用条目,装好后在控制台启用(mongo /
|
|
33
|
+
* mysql / postgres 等未再分发的 database 不再预写占位条目,市场装后
|
|
34
|
+
* 自动出现);
|
|
33
35
|
* - 依赖版本统一 ^1.0.0 区间(安装时取最新 1.x),shim 版本例外(冻结线)。
|
|
34
36
|
*/
|
|
35
37
|
import {
|
|
@@ -37,8 +39,7 @@ import {
|
|
|
37
39
|
readdirSync,
|
|
38
40
|
readFileSync,
|
|
39
41
|
} from "node:fs";
|
|
40
|
-
import {
|
|
41
|
-
import { fileURLToPath } from "node:url";
|
|
42
|
+
import { join } from "node:path";
|
|
42
43
|
import type { Manifest } from "./manifest.ts";
|
|
43
44
|
|
|
44
45
|
/**
|
|
@@ -47,7 +48,7 @@ import type { Manifest } from "./manifest.ts";
|
|
|
47
48
|
* 恒存在)。
|
|
48
49
|
*/
|
|
49
50
|
function locateTemplateDir(): string {
|
|
50
|
-
const base =
|
|
51
|
+
const base = import.meta.dir;
|
|
51
52
|
for (const dir of [
|
|
52
53
|
join(base, "template"),
|
|
53
54
|
join(base, "../src/template"),
|
|
@@ -96,7 +97,20 @@ export function baseManifest(): Manifest {
|
|
|
96
97
|
start: "koishi start",
|
|
97
98
|
// bun run 走 Bun Shell,`NODE_ENV=...` 前缀天然跨平台,无需 cross-env
|
|
98
99
|
dev: "NODE_ENV=development koishi start",
|
|
100
|
+
// 插件开发链(devDep @koishi-ce/scripts 的 koishi-scripts CLI,操作
|
|
101
|
+
// 本工作区 plugins/ 与 external/;纯运行 bot 的项目用不到这些入口,
|
|
102
|
+
// prod 模式删 devDependencies 后它们自然失效):
|
|
103
|
+
// new 生成插件骨架到 external/,clone 克隆已有插件仓库本地联动,
|
|
104
|
+
// build / release* 构建并发布 external/ 下的插件(version → build
|
|
105
|
+
// → publish 三环,dry-run 为演练形态)
|
|
99
106
|
new: "koishi-scripts setup",
|
|
107
|
+
clone: "koishi-scripts clone",
|
|
108
|
+
build: "koishi-scripts build",
|
|
109
|
+
"release:version": "koishi-scripts version",
|
|
110
|
+
"release:dryrun":
|
|
111
|
+
"bun run release:version && bun run build && koishi-scripts publish --dry-run",
|
|
112
|
+
release:
|
|
113
|
+
"bun run release:version && bun run build && koishi-scripts publish",
|
|
100
114
|
},
|
|
101
115
|
dependencies: {
|
|
102
116
|
"@koishi-ce/koishi": "^1.0.0",
|
package/src/utils.ts
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* prepare 步骤调用,也是除 manifest.ts / registry.ts 之外可脱离交互
|
|
9
9
|
* 直接单测的函数集合。
|
|
10
10
|
*/
|
|
11
|
-
import { spawnSync } from "node:child_process";
|
|
12
11
|
import { readdirSync, rmSync } from "node:fs";
|
|
13
12
|
import { join } from "node:path";
|
|
14
13
|
|
|
@@ -18,7 +17,7 @@ import { join } from "node:path";
|
|
|
18
17
|
* 本 CLI 自身以 bun 为运行时(bin shebang),能执行即已具备 bun 环境。
|
|
19
18
|
*/
|
|
20
19
|
export function detectAgent(): string {
|
|
21
|
-
const ua =
|
|
20
|
+
const ua = Bun.env["npm_config_user_agent"] ?? "";
|
|
22
21
|
if (ua.startsWith("yarn")) return "yarn";
|
|
23
22
|
if (ua.startsWith("pnpm")) return "pnpm";
|
|
24
23
|
return "bun";
|
|
@@ -26,19 +25,22 @@ export function detectAgent(): string {
|
|
|
26
25
|
|
|
27
26
|
/** 静默执行命令探测其是否可用(如 git --version),失败即视为不可用 */
|
|
28
27
|
export function supports(command: readonly string[]) {
|
|
29
|
-
return (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
28
|
+
return Bun.spawnSync({
|
|
29
|
+
cmd: [...command],
|
|
30
|
+
stdout: "ignore",
|
|
31
|
+
stderr: "ignore",
|
|
32
|
+
}).success;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
/** 读 git 全局配置单项(读不到 → 空串) */
|
|
37
36
|
export function gitConfig(key: string): string {
|
|
38
|
-
const res = spawnSync(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
const res = Bun.spawnSync([
|
|
38
|
+
"git",
|
|
39
|
+
"config",
|
|
40
|
+
"--get",
|
|
41
|
+
key,
|
|
42
|
+
]);
|
|
43
|
+
return res.success ? res.stdout.toString().trim() : "";
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/** 递归清空目录内容(目录本身保留)。 */
|