create-koishi-ce 1.3.0 → 1.3.1

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/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { a as start } from "./src-DiZgIQu5.mjs";
2
+ import { t as start } from "./src-Dh_67mkm.mjs";
3
3
  //#region src/bin.ts
4
4
  /**
5
5
  * create-koishi-ce 的 CLI 可执行入口:shebang 由 rolldown 原样保留到
package/lib/index.d.ts CHANGED
@@ -1,7 +1,12 @@
1
- //#region src/index.d.ts
1
+ //#region src/manifest.d.ts
2
+ /**
3
+ * 模板项目 package.json 的类型与改写(纯函数域,无副作用):定义本流程
4
+ * 触碰字段的类型并提供渲染函数。内置模板(index.ts 的 scaffoldBuiltin
5
+ * 直接渲染写入)与远程模板(remote.ts 读回解包产物后改写)共用这里。
6
+ */
2
7
  /**
3
- * 模板项目的 package.json(改写目标):只需要类型化本流程触碰的字段,
4
- * 其余字段经 index signature 原样保留。
8
+ * 模板项目的 package.json(改写源 / 目标):只需要类型化本流程触碰的
9
+ * 字段,其余字段经 index signature 原样保留。
5
10
  */
6
11
  interface Manifest {
7
12
  name?: string;
@@ -13,11 +18,12 @@ interface Manifest {
13
18
  [key: string]: unknown;
14
19
  }
15
20
  /**
16
- * 探测后续安装/启动使用的包管理器(Bun-first):yarn / pnpm 用户跟随其
17
- * 生态习惯;其余场景(npm、bun 及探测不到 user-agent)一律走 bun——
18
- * 本 CLI 自身以 bun 为运行时(bin shebang),能执行即已具备 bun 环境。
21
+ * 改写模板的 package.json(纯函数,导出供单测):替换项目名、标记
22
+ * private、版本归零。
19
23
  */
20
- declare function detectAgent(): string;
24
+ declare function renderManifest(source: Manifest, project: string, prod: boolean): string;
25
+ //#endregion
26
+ //#region src/registry.d.ts
21
27
  /**
22
28
  * 从单个 .npmrc 文件提取 registry 配置项(非注释、非 scoped 的 registry= 行)。
23
29
  * 文件不存在或未配置时返回 undefined,读文件异常一律静默吞掉。
@@ -25,22 +31,24 @@ declare function detectAgent(): string;
25
31
  declare function readNpmrcRegistry(file: string): string | undefined;
26
32
  /**
27
33
  * 读取本机 npm registry 配置(优先级对齐 npm 自身:环境变量 > 项目
28
- * .npmrc > 用户 ~/.npmrc)。刻意不 spawn 子进程探测——既有实现按
29
- * user-agent 选 `bun config get registry`,而 Bun 没有 config 子命令,
30
- * bunx 场景下子进程退出码 1 直接炸掉脚手架;读 npmrc 是零依赖、零
31
- * 子进程的等价路径。任何一步都拿不到时返回 undefined,由调用方回落
32
- * 官方源。
34
+ * .npmrc > 用户 ~/.npmrc)。任何一步都拿不到时返回 undefined,由调用方
35
+ * 回落官方源。
33
36
  */
34
37
  declare function getLocalRegistry(cwd?: string, userHome?: string): string | undefined;
38
+ //#endregion
39
+ //#region src/utils.d.ts
35
40
  /**
36
- * 改写模板的 package.json(纯函数,导出供单测):替换项目名、标记
37
- * private、版本归零。
41
+ * 探测后续安装/启动使用的包管理器(Bun-first):yarn / pnpm 用户跟随其
42
+ * 生态习惯;其余场景(npm、bun 及探测不到 user-agent)一律走 bun——
43
+ * 本 CLI 自身以 bun 为运行时(bin shebang),能执行即已具备 bun 环境。
38
44
  */
39
- declare function renderManifest(source: Manifest, project: string, prod: boolean): string;
45
+ declare function detectAgent(): string;
46
+ //#endregion
47
+ //#region src/index.d.ts
40
48
  /**
41
49
  * CLI 主流程:--help 打印用法后即返回;否则依次执行
42
50
  * 项目名询问 → prepare(目录准备)→ scaffold(模板解包)→ initGit → install。
43
51
  */
44
52
  declare function start(): Promise<void>;
45
53
  //#endregion
46
- export { Manifest, detectAgent, getLocalRegistry, readNpmrcRegistry, renderManifest, start };
54
+ export { type Manifest, detectAgent, getLocalRegistry, readNpmrcRegistry, renderManifest, start };
package/lib/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as start, i as renderManifest, n as getLocalRegistry, r as readNpmrcRegistry, t as detectAgent } from "./src-DiZgIQu5.mjs";
1
+ import { a as renderManifest, i as readNpmrcRegistry, n as detectAgent, r as getLocalRegistry, t as start } from "./src-Dh_67mkm.mjs";
2
2
  export { detectAgent, getLocalRegistry, readNpmrcRegistry, renderManifest, start };
@@ -1,18 +1,17 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
- import { homedir } from "node:os";
4
3
  import { basename, dirname, join, relative } from "node:path";
5
- import { Readable } from "node:stream";
6
- import kleur from "kleur";
7
- import prompts from "prompts";
8
- import { extract } from "tar";
9
- import parse from "yargs-parser";
4
+ import { parseArgs } from "node:util";
5
+ import * as p from "@clack/prompts";
6
+ import pc from "picocolors";
7
+ import { homedir } from "node:os";
8
+ import { downloadTemplate } from "giget";
10
9
  import { fileURLToPath } from "node:url";
11
10
  //#region package.json
12
11
  var package_default = {
13
12
  name: "create-koishi-ce",
14
13
  description: "Setup a Koishi application",
15
- version: "1.3.0",
14
+ version: "1.3.1",
16
15
  type: "module",
17
16
  main: "./lib/index.mjs",
18
17
  module: "./lib/index.mjs",
@@ -41,15 +40,10 @@ var package_default = {
41
40
  "generator",
42
41
  "boilerplate"
43
42
  ],
44
- devDependencies: {
45
- "@types/prompts": "^2.4.9",
46
- "@types/yargs-parser": "^21.0.3"
47
- },
48
43
  dependencies: {
49
- "kleur": "^4.1.5",
50
- "prompts": "^2.4.2",
51
- "tar": "^7.5.22",
52
- "yargs-parser": "^22.0.0"
44
+ "@clack/prompts": "^1.7.0",
45
+ "picocolors": "^1.1.1",
46
+ "giget": "^3.3.1"
53
47
  },
54
48
  exports: {
55
49
  ".": {
@@ -62,6 +56,123 @@ var package_default = {
62
56
  }
63
57
  };
64
58
  //#endregion
59
+ //#region src/manifest.ts
60
+ /**
61
+ * 改写模板的 package.json(纯函数,导出供单测):替换项目名、标记
62
+ * private、版本归零。
63
+ */
64
+ function renderManifest(source, project, prod) {
65
+ const meta = { ...source };
66
+ meta["name"] = project;
67
+ meta["private"] = true;
68
+ meta["version"] = "0.0.0";
69
+ if (prod) {
70
+ delete meta["workspaces"];
71
+ delete meta["devDependencies"];
72
+ }
73
+ return `${JSON.stringify(meta, null, 2)}\n`;
74
+ }
75
+ //#endregion
76
+ //#region src/registry.ts
77
+ /**
78
+ * 本机 npm registry 配置探测(纯函数域,零子进程、零网络):优先级对齐
79
+ * npm 自身(环境变量 > 项目 .npmrc > 用户 ~/.npmrc)。刻意不 spawn 子进程
80
+ * 探测——既有实现按 user-agent 选 `bun config get registry`,而 Bun 没有
81
+ * config 子命令,bunx 场景下子进程退出码 1 直接炸掉脚手架;读 npmrc 是
82
+ * 零依赖、零子进程的等价路径。远程模板下载流程见 remote.ts,不在本文件。
83
+ */
84
+ /**
85
+ * 从单个 .npmrc 文件提取 registry 配置项(非注释、非 scoped 的 registry= 行)。
86
+ * 文件不存在或未配置时返回 undefined,读文件异常一律静默吞掉。
87
+ */
88
+ function readNpmrcRegistry(file) {
89
+ try {
90
+ for (const line of readFileSync(file, "utf8").split(/\r?\n/)) {
91
+ const value = /^\s*registry\s*=\s*(\S+)\s*$/.exec(line)?.[1];
92
+ if (value) return value;
93
+ }
94
+ } catch {}
95
+ }
96
+ /**
97
+ * 读取本机 npm registry 配置(优先级对齐 npm 自身:环境变量 > 项目
98
+ * .npmrc > 用户 ~/.npmrc)。任何一步都拿不到时返回 undefined,由调用方
99
+ * 回落官方源。
100
+ */
101
+ function getLocalRegistry(cwd = process.cwd(), userHome = homedir()) {
102
+ const candidates = [
103
+ process.env["npm_config_registry"],
104
+ readNpmrcRegistry(join(cwd, ".npmrc")),
105
+ readNpmrcRegistry(join(userHome, ".npmrc"))
106
+ ];
107
+ for (const candidate of candidates) if (candidate?.startsWith("https://") || candidate?.startsWith("http://")) return candidate;
108
+ }
109
+ //#endregion
110
+ //#region src/remote.ts
111
+ /**
112
+ * 远程模板分支(--template <包名>)的下载-解包-改写全流程:拉取 npm
113
+ * registry 包元数据解析目标版本,经 giget 下载 tarball 并解包到目标
114
+ * 目录,最后改写 package.json。HttpError 与 RegistryMeta 只在本流程
115
+ * 消费,就近定义不另立文件;本机 registry 配置探测见 registry.ts。
116
+ *
117
+ * 运行期状态(argv / project / rootDir)不跨文件共享——它们留在
118
+ * index.ts 顶层(e2e 测试以模块 query 隔离实例);本模块所需字段一律
119
+ * 经 RemoteOptions 显式传入,由 index.ts 的 scaffold 组装,避免子模块
120
+ * 与顶层状态的隐式耦合。
121
+ */
122
+ /** fetch 请求非 2xx 时抛出的错误,携带 HTTP 状态码与状态文本 */
123
+ var HttpError = class extends Error {
124
+ status;
125
+ statusText;
126
+ constructor(status, statusText) {
127
+ super(`HTTP ${status} ${statusText}`);
128
+ this.status = status;
129
+ this.statusText = statusText;
130
+ }
131
+ };
132
+ /**
133
+ * 远程模板下载与解包:
134
+ * 1. 拉取模板包元数据,按 dist-tags 解析目标版本(ref,默认 latest);
135
+ * 2. 下载 tarball 并解包到目标目录(解包走 giget,其按 npm tarball 惯例
136
+ * 剥离顶层 package/ 目录,等价于 strip: 1),网络错误统一以 HttpError
137
+ * 提示后退出;
138
+ * 3. 最后改写 package.json。
139
+ */
140
+ async function scaffoldRemote({ registry, template, ref, prod, project, rootDir }) {
141
+ try {
142
+ const metaRes = await fetch(`${registry}/${template}`);
143
+ if (!metaRes.ok) throw new HttpError(metaRes.status, metaRes.statusText);
144
+ const remote = await metaRes.json();
145
+ const version = remote["dist-tags"][ref];
146
+ if (version === void 0) throw new HttpError(404, `模板 ${template}@${ref} 不存在`);
147
+ const url = remote.versions[version]?.dist?.tarball;
148
+ if (url === void 0) throw new HttpError(404, `模板 ${template}@${ref} 不存在`);
149
+ await downloadTemplate(template, {
150
+ provider: "npm",
151
+ providers: { npm: async () => ({
152
+ name: template,
153
+ version,
154
+ tar: async () => {
155
+ const tarballRes = await fetch(url);
156
+ if (!tarballRes.ok || !tarballRes.body) throw new HttpError(tarballRes.status, tarballRes.statusText);
157
+ return tarballRes.body;
158
+ }
159
+ }) },
160
+ dir: rootDir
161
+ });
162
+ } catch (err) {
163
+ if (!(err instanceof HttpError)) throw err;
164
+ console.log(`${pc.red("error")} 请求失败:HTTP ${err.status} ${err.statusText}`);
165
+ process.exit(1);
166
+ }
167
+ writePackageJson(rootDir, project, prod);
168
+ }
169
+ /** 把改写结果写回下载解包出的 package.json */
170
+ function writePackageJson(rootDir, project, prod) {
171
+ const filename = join(rootDir, "package.json");
172
+ const meta = JSON.parse(readFileSync(filename, "utf8"));
173
+ writeFileSync(filename, renderManifest(meta, project, prod));
174
+ }
175
+ //#endregion
65
176
  //#region src/template.ts
66
177
  /**
67
178
  * 内置项目模板(create-koishi-ce 的默认模板)。
@@ -70,7 +181,7 @@ var package_default = {
70
181
  * 生成项目,产物依赖全是 npm 官方包(koishi / @koishijs/*),完全绕开了
71
182
  * 本仓的 @koishi-ce 再分发生态(官方 market 还带 Bun 下必炸的 get-registry)。
72
183
  * 默认模板改为内置的纯 @koishi-ce 依赖集;确需上游官方模板时用
73
- * --template <包名> 走 npm 远程下载(见 index.ts 的 scaffoldRemote)。
184
+ * --template <包名> 走 npm 远程下载(见 remote.ts 的 scaffoldRemote)。
74
185
  *
75
186
  * 模板的静态文本一律放在 src/template/ 下的真实文件里(本模块只负责
76
187
  * 定位与读取,不再内嵌字符串常量)。npm 包的 files 含 src,因此 lib
@@ -171,44 +282,14 @@ function baseManifest() {
171
282
  };
172
283
  }
173
284
  //#endregion
174
- //#region src/index.ts
285
+ //#region src/utils.ts
175
286
  /**
176
- * create-koishi-ce 脚手架(npm 包名 create-koishi-ce,目录名为
177
- * apps/koishi-create,二者不一致是历史遗留,以目录名为准)。
178
- *
179
- * 通过 `bunx create-koishi-ce [name]`(npx 亦可)交互式创建 Koishi 机器人
180
- * 应用项目:确定项目名 → 准备目标目录 → 写入内置 @koishi-ce 模板(默认,
181
- * 见 src/template.ts;--template <包名> 可改用 npm registry 远程模板,如
182
- * 上游官方 @koishijs/boilerplate)→ 按需初始化 git → 询问是否立即安装
183
- * 依赖并启动。CLI 可执行入口在 src/bin.ts(构建产物 lib/bin.mjs,bin 字段
184
- * 指向它);本文件只承载主流程与可单测的纯函数(范式对齐
185
- * @koishi-ce/scripts)。
287
+ * 平台小工具(无业务状态、不触碰 CLI 全局状态的零散函数):包管理器
288
+ * 探测(detectAgent)、git 可用性与配置读取(supports / gitConfig)、
289
+ * 目录清空(emptyDir)。分别被 index.ts 主流程的 install / initGit /
290
+ * prepare 步骤调用,也是除 manifest.ts / registry.ts 之外可脱离交互
291
+ * 直接单测的函数集合。
186
292
  */
187
- const { version } = package_default;
188
- /** fetch 请求非 2xx 时抛出的错误,携带 HTTP 状态码与状态文本 */
189
- var HttpError = class extends Error {
190
- status;
191
- statusText;
192
- constructor(status, statusText) {
193
- super(`HTTP ${status} ${statusText}`);
194
- this.status = status;
195
- this.statusText = statusText;
196
- }
197
- };
198
- const argv = parse(process.argv.slice(2), { alias: {
199
- ref: ["r"],
200
- forced: ["f"],
201
- git: ["g"],
202
- prod: ["p"],
203
- template: ["t"],
204
- yes: ["y"],
205
- help: ["h"]
206
- } });
207
- /** 项目目录名(rootDir 的最后一段,写入生成项目的 package.json 的 name) */
208
- let project;
209
- /** 目标目录的绝对路径(由用户输入的项目名拼接 cwd 得到) */
210
- let rootDir;
211
- const cwd = process.cwd();
212
293
  /**
213
294
  * 探测后续安装/启动使用的包管理器(Bun-first):yarn / pnpm 用户跟随其
214
295
  * 生态习惯;其余场景(npm、bun 及探测不到 user-agent)一律走 bun——
@@ -220,34 +301,6 @@ function detectAgent() {
220
301
  if (ua.startsWith("pnpm")) return "pnpm";
221
302
  return "bun";
222
303
  }
223
- /**
224
- * 从单个 .npmrc 文件提取 registry 配置项(非注释、非 scoped 的 registry= 行)。
225
- * 文件不存在或未配置时返回 undefined,读文件异常一律静默吞掉。
226
- */
227
- function readNpmrcRegistry(file) {
228
- try {
229
- for (const line of readFileSync(file, "utf8").split(/\r?\n/)) {
230
- const value = /^\s*registry\s*=\s*(\S+)\s*$/.exec(line)?.[1];
231
- if (value) return value;
232
- }
233
- } catch {}
234
- }
235
- /**
236
- * 读取本机 npm registry 配置(优先级对齐 npm 自身:环境变量 > 项目
237
- * .npmrc > 用户 ~/.npmrc)。刻意不 spawn 子进程探测——既有实现按
238
- * user-agent 选 `bun config get registry`,而 Bun 没有 config 子命令,
239
- * bunx 场景下子进程退出码 1 直接炸掉脚手架;读 npmrc 是零依赖、零
240
- * 子进程的等价路径。任何一步都拿不到时返回 undefined,由调用方回落
241
- * 官方源。
242
- */
243
- function getLocalRegistry(cwd = process.cwd(), userHome = homedir()) {
244
- const candidates = [
245
- process.env["npm_config_registry"],
246
- readNpmrcRegistry(join(cwd, ".npmrc")),
247
- readNpmrcRegistry(join(userHome, ".npmrc"))
248
- ];
249
- for (const candidate of candidates) if (candidate?.startsWith("https://") || candidate?.startsWith("http://")) return candidate;
250
- }
251
304
  /** 静默执行命令探测其是否可用(如 git --version),失败即视为不可用 */
252
305
  function supports(command) {
253
306
  return spawnSync(command[0] ?? "", command.slice(1), { stdio: "ignore" }).status === 0;
@@ -261,36 +314,105 @@ function gitConfig(key) {
261
314
  ], { encoding: "utf8" });
262
315
  return res.status === 0 ? res.stdout?.trim() ?? "" : "";
263
316
  }
317
+ /** 递归清空目录内容(目录本身保留)。 */
318
+ function emptyDir(root) {
319
+ for (const file of readdirSync(root)) rmSync(join(root, file), {
320
+ recursive: true,
321
+ force: true
322
+ });
323
+ }
324
+ //#endregion
325
+ //#region src/index.ts
326
+ /**
327
+ * create-koishi-ce 脚手架(npm 包名 create-koishi-ce,目录名为
328
+ * apps/koishi-create,二者不一致是历史遗留,以目录名为准)。
329
+ *
330
+ * 通过 `bunx create-koishi-ce [name]`(npx 亦可)交互式创建 Koishi 机器人
331
+ * 应用项目:确定项目名 → 准备目标目录 → 写入内置 @koishi-ce 模板(默认,
332
+ * 见 src/template.ts;--template <包名> 可改用 npm registry 远程模板,如
333
+ * 上游官方 @koishijs/boilerplate)→ 按需初始化 git → 询问是否立即安装
334
+ * 依赖并启动。CLI 可执行入口在 src/bin.ts(构建产物 lib/bin.mjs,bin 字段
335
+ * 指向它)。
336
+ *
337
+ * 本文件是 CLI 的编排层:承载参数解析与运行期状态(argv / cwd /
338
+ * project / rootDir —— 它们被 e2e 测试以模块 query 隔离实例,必须留在
339
+ * 顶层模块内)与交互主流程。纯函数与单一职责的子流程按域拆到同目录
340
+ * 子模块,公共导出面在文件尾原样 re-export(bin 与测试的导入路径不变):
341
+ * - manifest.ts package.json 改写(Manifest / renderManifest)
342
+ * - registry.ts 本机 npm registry 配置探测
343
+ * - utils.ts 平台小工具(包管理器 / git 探测、清空目录)
344
+ * - remote.ts --template 远程模板的下载-解包-改写全流程
345
+ * - template.ts 内置 @koishi-ce 模板(静态文件与 baseManifest)
346
+ */
347
+ const { version } = package_default;
348
+ const { values, positionals } = parseArgs({
349
+ args: process.argv.slice(2),
350
+ options: {
351
+ registry: { type: "string" },
352
+ ref: {
353
+ type: "string",
354
+ short: "r"
355
+ },
356
+ forced: {
357
+ type: "boolean",
358
+ short: "f"
359
+ },
360
+ git: {
361
+ type: "boolean",
362
+ short: "g"
363
+ },
364
+ prod: {
365
+ type: "boolean",
366
+ short: "p"
367
+ },
368
+ template: {
369
+ type: "string",
370
+ short: "t"
371
+ },
372
+ yes: {
373
+ type: "boolean",
374
+ short: "y"
375
+ },
376
+ help: {
377
+ type: "boolean",
378
+ short: "h"
379
+ }
380
+ },
381
+ allowPositionals: true
382
+ });
383
+ const argv = {
384
+ ...values,
385
+ _: positionals
386
+ };
387
+ /** 项目目录名(rootDir 的最后一段,写入生成项目的 package.json 的 name) */
388
+ let project;
389
+ /** 目标目录的绝对路径(由用户输入的项目名拼接 cwd 得到) */
390
+ let rootDir;
391
+ const cwd = process.cwd();
264
392
  /**
265
393
  * 获取项目名:优先取第一个位置参数,否则交互式询问(默认 koishi-app)。
266
- * 用户取消或输入为空时直接退出(不强行兜底默认值)。
394
+ * 用户取消(Ctrl+C)或输入为空时直接退出(不强行兜底默认值)。
267
395
  */
268
396
  async function getName() {
269
397
  if (argv._[0]) return `${argv._[0]}`;
270
- const trimmed = (await prompts({
271
- type: "text",
272
- name: "name",
398
+ const answer = await p.text({
273
399
  message: "项目名:",
274
- initial: "koishi-app"
275
- })).name?.trim();
400
+ initialValue: "koishi-app",
401
+ validate: (value) => value?.trim() ? void 0 : "项目名不能为空"
402
+ });
403
+ if (p.isCancel(answer)) process.exit(0);
404
+ const trimmed = answer.trim();
276
405
  if (!trimmed) process.exit(0);
277
406
  return trimmed;
278
407
  }
279
- /** 递归清空目录内容(目录本身保留)。 */
280
- function emptyDir(root) {
281
- for (const file of readdirSync(root)) rmSync(join(root, file), {
282
- recursive: true,
283
- force: true
284
- });
285
- }
286
- /** 交互式确认框:返回用户是否选择了「是」(取消视为否) */
408
+ /** 交互式确认框:返回用户是否选择了「是」(Ctrl+C 取消直接退出) */
287
409
  async function confirm(message) {
288
- return (await prompts({
289
- type: "confirm",
290
- name: "yes",
291
- initial: true,
292
- message
293
- })).yes === true;
410
+ const answer = await p.confirm({
411
+ message,
412
+ initialValue: true
413
+ });
414
+ if (p.isCancel(answer)) process.exit(0);
415
+ return answer === true;
294
416
  }
295
417
  /**
296
418
  * 准备目标目录:不存在则创建;已存在且非空时,未指定 --forced / --yes
@@ -303,33 +425,12 @@ async function prepare() {
303
425
  }
304
426
  if (!readdirSync(rootDir).length) return;
305
427
  if (!argv.forced && !argv.yes) {
306
- console.log(kleur.yellow(` 目标目录 "${project}" 非空。`));
428
+ console.log(pc.yellow(` 目标目录 "${project}" 非空。`));
307
429
  if (!await confirm("清空现有文件并继续?")) process.exit(0);
308
430
  }
309
431
  emptyDir(rootDir);
310
432
  }
311
433
  /**
312
- * 改写模板的 package.json(纯函数,导出供单测):替换项目名、标记
313
- * private、版本归零。
314
- */
315
- function renderManifest(source, project, prod) {
316
- const meta = { ...source };
317
- meta["name"] = project;
318
- meta["private"] = true;
319
- meta["version"] = "0.0.0";
320
- if (prod) {
321
- delete meta["workspaces"];
322
- delete meta["devDependencies"];
323
- }
324
- return `${JSON.stringify(meta, null, 2)}\n`;
325
- }
326
- /** 把改写结果写回生成项目的 package.json */
327
- function writePackageJson() {
328
- const filename = join(rootDir, "package.json");
329
- const meta = JSON.parse(readFileSync(filename, "utf8"));
330
- writeFileSync(filename, renderManifest(meta, project, argv.prod === true));
331
- }
332
- /**
333
434
  * 写入内置模板(默认路径):静态文件(src/template.ts 的 templateFiles)
334
435
  * 加上由 baseManifest() 渲染出的 package.json。纯本地写入,无网络请求。
335
436
  */
@@ -338,52 +439,25 @@ function scaffoldBuiltin() {
338
439
  writeFileSync(join(rootDir, "package.json"), renderManifest(baseManifest(), project, argv.prod === true));
339
440
  }
340
441
  /**
341
- * 远程模板下载与解包(--template 指定时):
342
- * 1. 拉取模板包元数据,按 dist-tags 解析目标版本(--ref,默认 latest);
343
- * 2. 流式下载 tarball 并解包到目标目录(strip: 1 去掉包根目录层级),
344
- * 网络错误统一以 HttpError 提示后退出;
345
- * 3. 最后改写 package.json。
346
- */
347
- async function scaffoldRemote(registry) {
348
- const template = argv.template;
349
- const ref = argv.ref || "latest";
350
- try {
351
- const metaRes = await fetch(`${registry}/${template}`);
352
- if (!metaRes.ok) throw new HttpError(metaRes.status, metaRes.statusText);
353
- const remote = await metaRes.json();
354
- const version = remote["dist-tags"][ref];
355
- const url = version === void 0 ? void 0 : remote.versions[version]?.dist?.tarball;
356
- if (url === void 0) throw new HttpError(404, `模板 ${template}@${ref} 不存在`);
357
- const tarballRes = await fetch(url);
358
- const body = tarballRes.body;
359
- if (!tarballRes.ok || !body) throw new HttpError(tarballRes.status, tarballRes.statusText);
360
- await new Promise((resolve, reject) => {
361
- Readable.fromWeb(body).pipe(extract({
362
- cwd: rootDir,
363
- newer: true,
364
- strip: 1
365
- })).on("finish", resolve).on("error", reject);
366
- });
367
- } catch (err) {
368
- if (!(err instanceof HttpError)) throw err;
369
- console.log(`${kleur.red("error")} 请求失败:HTTP ${err.status} ${err.statusText}`);
370
- process.exit(1);
371
- }
372
- writePackageJson();
373
- }
374
- /**
375
442
  * 生成项目的主入口:默认写内置 @koishi-ce 模板(scaffoldBuiltin);
376
- * --template <包名> 时改为从 npm registry 下载远程模板(scaffoldRemote),
377
- * registry 取值 --registry 参数 > 本机 npm 配置 > 官方源。
443
+ * --template <包名> 时改为从 npm registry 下载远程模板(remote.ts 的
444
+ * scaffoldRemote),registry 取值 --registry 参数 > 本机 npm 配置 > 官方源。
378
445
  */
379
446
  async function scaffold() {
380
- console.log(kleur.dim(" 正在 ") + project + kleur.dim(" 中生成项目 ..."));
447
+ console.log(pc.dim(" 正在 ") + project + pc.dim(" 中生成项目 ..."));
381
448
  if (argv.template) {
382
449
  const registry = (argv.registry || getLocalRegistry() || "https://registry.npmjs.org").replace(/\/$/, "");
383
- console.log(kleur.dim(` 使用 registry:${registry}\n`));
384
- await scaffoldRemote(registry);
450
+ console.log(pc.dim(` 使用 registry:${registry}\n`));
451
+ await scaffoldRemote({
452
+ registry,
453
+ template: argv.template,
454
+ ref: argv.ref || "latest",
455
+ prod: argv.prod === true,
456
+ project,
457
+ rootDir
458
+ });
385
459
  } else scaffoldBuiltin();
386
- console.log(kleur.green(" 完成。\n"));
460
+ console.log(pc.green(" 完成。\n"));
387
461
  }
388
462
  /**
389
463
  * 初始化 git 仓库:仅在显式传入 --git 且本机装有 git 时执行,分支名取
@@ -400,7 +474,7 @@ async function initGit() {
400
474
  stdio: "ignore",
401
475
  cwd: rootDir
402
476
  });
403
- console.log(kleur.green(` 已初始化 git 仓库(分支 ${branch})。\n`));
477
+ console.log(pc.green(` 已初始化 git 仓库(分支 ${branch})。\n`));
404
478
  }
405
479
  /**
406
480
  * 收尾交互:询问是否立即安装依赖并启动,包管理器由 detectAgent()
@@ -415,7 +489,7 @@ async function install() {
415
489
  stdio: "inherit",
416
490
  cwd: rootDir
417
491
  }).status !== 0) {
418
- console.log(kleur.red(" 依赖安装失败,请检查上方日志。"));
492
+ console.log(pc.red(" 依赖安装失败,请检查上方日志。"));
419
493
  return;
420
494
  }
421
495
  spawnSync(agent, startArgs, {
@@ -423,13 +497,13 @@ async function install() {
423
497
  cwd: rootDir
424
498
  });
425
499
  } else {
426
- console.log(kleur.dim(" 稍后可以这样启动:\n"));
500
+ console.log(pc.dim(" 稍后可以这样启动:\n"));
427
501
  if (rootDir !== cwd) {
428
502
  const related = relative(cwd, rootDir);
429
- console.log(kleur.blue(` cd ${kleur.bold(related)}`));
503
+ console.log(pc.blue(` cd ${pc.bold(related)}`));
430
504
  }
431
- console.log(kleur.blue(` ${agent === "yarn" ? "yarn" : `${agent} install`}`));
432
- console.log(kleur.blue(` ${agent === "yarn" ? "yarn" : `${agent} run`} start`));
505
+ console.log(pc.blue(` ${agent === "yarn" ? "yarn" : `${agent} install`}`));
506
+ console.log(pc.blue(` ${agent === "yarn" ? "yarn" : `${agent} run`} start`));
433
507
  console.log();
434
508
  }
435
509
  }
@@ -455,7 +529,7 @@ async function start() {
455
529
  return;
456
530
  }
457
531
  console.log();
458
- console.log(` ${kleur.bold("Create Koishi")} ${kleur.blue(`v${version}`)}`);
532
+ console.log(` ${pc.bold("Create Koishi")} ${pc.blue(`v${version}`)}`);
459
533
  console.log();
460
534
  const name = await getName();
461
535
  rootDir = join(cwd, name);
@@ -466,4 +540,4 @@ async function start() {
466
540
  await install();
467
541
  }
468
542
  //#endregion
469
- export { start as a, renderManifest as i, getLocalRegistry as n, readNpmrcRegistry as r, detectAgent as t };
543
+ export { renderManifest as a, readNpmrcRegistry as i, detectAgent as n, getLocalRegistry as r, start as t };