create-koishi-ce 1.0.0 → 1.1.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/lib/bin.mjs +1 -1
- package/lib/index.d.ts +17 -2
- package/lib/index.mjs +2 -2
- package/lib/{src-D9XdTOrE.mjs → src-CUSQBq1d.mjs} +219 -30
- package/package.json +1 -2
- package/src/__tests__/registry.test.ts +54 -0
- package/src/__tests__/run-default.test.ts +275 -0
- package/src/__tests__/run-help.test.ts +41 -0
- package/src/__tests__/run-remote.test.ts +242 -0
- package/src/__tests__/template.test.ts +57 -0
- package/src/index.ts +95 -24
- package/src/template.ts +167 -0
package/lib/bin.mjs
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -8,7 +8,8 @@ interface Manifest {
|
|
|
8
8
|
private?: boolean;
|
|
9
9
|
version?: string;
|
|
10
10
|
workspaces?: unknown;
|
|
11
|
-
|
|
11
|
+
dependencies?: Record<string, string>;
|
|
12
|
+
devDependencies?: Record<string, string>;
|
|
12
13
|
[key: string]: unknown;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
@@ -17,6 +18,20 @@ interface Manifest {
|
|
|
17
18
|
* 本 CLI 自身以 bun 为运行时(bin shebang),能执行即已具备 bun 环境。
|
|
18
19
|
*/
|
|
19
20
|
declare function detectAgent(): string;
|
|
21
|
+
/**
|
|
22
|
+
* 从单个 .npmrc 文件提取 registry 配置项(非注释、非 scoped 的 registry= 行)。
|
|
23
|
+
* 文件不存在或未配置时返回 undefined,读文件异常一律静默吞掉。
|
|
24
|
+
*/
|
|
25
|
+
declare function readNpmrcRegistry(file: string): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* 读取本机 npm registry 配置(优先级对齐 npm 自身:环境变量 > 项目
|
|
28
|
+
* .npmrc > 用户 ~/.npmrc)。刻意不 spawn 子进程探测——既有实现按
|
|
29
|
+
* user-agent 选 `bun config get registry`,而 Bun 没有 config 子命令,
|
|
30
|
+
* bunx 场景下子进程退出码 1 直接炸掉脚手架;读 npmrc 是零依赖、零
|
|
31
|
+
* 子进程的等价路径。任何一步都拿不到时返回 undefined,由调用方回落
|
|
32
|
+
* 官方源。
|
|
33
|
+
*/
|
|
34
|
+
declare function getLocalRegistry(cwd?: string, userHome?: string): string | undefined;
|
|
20
35
|
/**
|
|
21
36
|
* 改写模板的 package.json(纯函数,导出供单测):替换项目名、标记
|
|
22
37
|
* private、版本归零。
|
|
@@ -28,4 +43,4 @@ declare function renderManifest(source: Manifest, project: string, prod: boolean
|
|
|
28
43
|
*/
|
|
29
44
|
declare function start(): Promise<void>;
|
|
30
45
|
//#endregion
|
|
31
|
-
export { Manifest, detectAgent, renderManifest, start };
|
|
46
|
+
export { Manifest, detectAgent, getLocalRegistry, readNpmrcRegistry, renderManifest, start };
|
package/lib/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { detectAgent, renderManifest, start };
|
|
1
|
+
import { a as start, i as renderManifest, n as getLocalRegistry, r as readNpmrcRegistry, t as detectAgent } from "./src-CUSQBq1d.mjs";
|
|
2
|
+
export { detectAgent, getLocalRegistry, readNpmrcRegistry, renderManifest, start };
|
|
@@ -1,29 +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";
|
|
3
4
|
import { basename, join, relative } from "node:path";
|
|
4
5
|
import { Readable } from "node:stream";
|
|
5
|
-
import getRegistry from "get-registry";
|
|
6
6
|
import kleur from "kleur";
|
|
7
7
|
import prompts from "prompts";
|
|
8
8
|
import { extract } from "tar";
|
|
9
9
|
import parse from "yargs-parser";
|
|
10
|
-
//#
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* create-koishi-ce 脚手架(npm 包名 create-koishi-ce,目录名为
|
|
14
|
-
* apps/koishi-create,二者不一致是历史遗留,以目录名为准)。
|
|
15
|
-
*
|
|
16
|
-
* 通过 `bunx create-koishi-ce [name]`(npx 亦可)交互式创建 Koishi 机器人
|
|
17
|
-
* 应用项目:确定项目名 → 准备目标目录 → 从 npm registry 下载模板包(默认
|
|
18
|
-
* @koishijs/boilerplate,刻意沿用上游官方模板以保持与上游插件生态一致)
|
|
19
|
-
* 并解包 → 改写 package.json → 按需初始化 git → 询问是否立即安装依赖并
|
|
20
|
-
* 启动。CLI 可执行入口在 src/bin.ts(构建产物 lib/bin.mjs,bin 字段指向
|
|
21
|
-
* 它);本文件只承载主流程与可单测的纯函数(范式对齐 @koishi-ce/scripts)。
|
|
22
|
-
*/
|
|
23
|
-
const { version } = {
|
|
10
|
+
//#region package.json
|
|
11
|
+
var package_default = {
|
|
24
12
|
name: "create-koishi-ce",
|
|
25
13
|
description: "Setup a Koishi application",
|
|
26
|
-
version: "1.
|
|
14
|
+
version: "1.1.0",
|
|
27
15
|
type: "module",
|
|
28
16
|
main: "./lib/index.mjs",
|
|
29
17
|
module: "./lib/index.mjs",
|
|
@@ -57,7 +45,6 @@ const { version } = {
|
|
|
57
45
|
"@types/yargs-parser": "^21.0.3"
|
|
58
46
|
},
|
|
59
47
|
dependencies: {
|
|
60
|
-
"get-registry": "^1.1.0",
|
|
61
48
|
"kleur": "^4.1.5",
|
|
62
49
|
"prompts": "^2.4.2",
|
|
63
50
|
"tar": "^7.5.22",
|
|
@@ -73,6 +60,163 @@ const { version } = {
|
|
|
73
60
|
"./package.json": "./package.json"
|
|
74
61
|
}
|
|
75
62
|
};
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/template.ts
|
|
65
|
+
/** 模板静态文件(相对项目根的路径 → 文件内容),package.json 另行渲染 */
|
|
66
|
+
const templateFiles = {
|
|
67
|
+
".env": [
|
|
68
|
+
"GITHUB_MIRROR = https://ghproxy.com/https://github.com",
|
|
69
|
+
"GITHUB_CONTENT_MIRROR = https://ghproxy.com/https://raw.githubusercontent.com",
|
|
70
|
+
"GRAVATAR_MIRROR = https://cravatar.cn"
|
|
71
|
+
].join("\n"),
|
|
72
|
+
".gitignore": ["node_modules/", "data/"].join("\n"),
|
|
73
|
+
"koishi.yml": [
|
|
74
|
+
"plugins:",
|
|
75
|
+
" group:server:",
|
|
76
|
+
" server:",
|
|
77
|
+
" port: 5140",
|
|
78
|
+
" maxPort: 5149",
|
|
79
|
+
" group:basic:",
|
|
80
|
+
" ~admin: {}",
|
|
81
|
+
" ~bind: {}",
|
|
82
|
+
" commands: {}",
|
|
83
|
+
" help: {}",
|
|
84
|
+
" http: {}",
|
|
85
|
+
" ~inspect: {}",
|
|
86
|
+
" locales: {}",
|
|
87
|
+
" proxy-agent: {}",
|
|
88
|
+
" group:console:",
|
|
89
|
+
" actions: {}",
|
|
90
|
+
" ~analytics: {}",
|
|
91
|
+
" ~auth: {}",
|
|
92
|
+
" config: {}",
|
|
93
|
+
" console:",
|
|
94
|
+
" open: true",
|
|
95
|
+
" explorer: {}",
|
|
96
|
+
" insight: {}",
|
|
97
|
+
" logger: {}",
|
|
98
|
+
" market:",
|
|
99
|
+
" search:",
|
|
100
|
+
" endpoint: https://registry.koishi.chat/index.json",
|
|
101
|
+
" notifier: {}",
|
|
102
|
+
" oobe: {}",
|
|
103
|
+
" sandbox: {}",
|
|
104
|
+
" status: {}",
|
|
105
|
+
" group:develop:",
|
|
106
|
+
" $if: env.NODE_ENV === 'development'",
|
|
107
|
+
" hmr:",
|
|
108
|
+
" root: ."
|
|
109
|
+
].join("\n"),
|
|
110
|
+
"tsconfig.json": [
|
|
111
|
+
"{",
|
|
112
|
+
" \"compilerOptions\": {",
|
|
113
|
+
" \"target\": \"ESNext\",",
|
|
114
|
+
" \"module\": \"ESNext\",",
|
|
115
|
+
" \"moduleResolution\": \"bundler\",",
|
|
116
|
+
" \"lib\": [\"ESNext\"],",
|
|
117
|
+
" \"types\": [\"bun-types\"],",
|
|
118
|
+
" \"strict\": true,",
|
|
119
|
+
" \"skipLibCheck\": true,",
|
|
120
|
+
" \"noEmit\": true,",
|
|
121
|
+
" \"allowImportingTsExtensions\": true,",
|
|
122
|
+
" \"verbatimModuleSyntax\": true,",
|
|
123
|
+
" \"erasableSyntaxOnly\": true",
|
|
124
|
+
" },",
|
|
125
|
+
" \"include\": [\"plugins/*/src\", \"external/*/src\"]",
|
|
126
|
+
"}"
|
|
127
|
+
].join("\n"),
|
|
128
|
+
"README.md": `# Koishi 机器人项目
|
|
129
|
+
|
|
130
|
+
由 \`bun create koishi-ce\` 生成,运行于 [@koishi-ce 社区再分发版](https://github.com/Koishi-CE/koishi) 生态。
|
|
131
|
+
|
|
132
|
+
## 环境要求
|
|
133
|
+
|
|
134
|
+
- [Bun](https://bun.sh) ≥ 1.2(运行时:koishi CLI 与插件加载链以 Bun 为准)
|
|
135
|
+
|
|
136
|
+
## 快速开始
|
|
137
|
+
|
|
138
|
+
\`\`\`bash
|
|
139
|
+
bun install
|
|
140
|
+
bun run start # 启动(生产模式)
|
|
141
|
+
bun run dev # 启动(开发模式,启用 HMR 热更新)
|
|
142
|
+
\`\`\`
|
|
143
|
+
|
|
144
|
+
启动后访问控制台:<http://127.0.0.1:5140>
|
|
145
|
+
|
|
146
|
+
## 安装插件
|
|
147
|
+
|
|
148
|
+
推荐在控制台「插件市场」页安装(已预配 registry.koishi.chat 镜像源);也可以手动 \`bun add <包名>\` 后在 \`koishi.yml\` 中启用。
|
|
149
|
+
|
|
150
|
+
上游官方 adapter(如 adapter-discord / adapter-telegram)、数据库插件(如 database-sqlite)与社区 koishi-plugin-* 插件均可直接安装:根依赖中的 \`"koishi": "npm:@koishi-ce/koishi-shim@^4.18.11"\` 已把上游生态对 \`koishi\` 的依赖钉回 @koishi-ce 框架(**请勿删除或改写该行**),不会形成第二份框架副本。analytics 等依赖数据库的插件请先安装数据库插件,再去掉配置中对应的 \`~\` 前缀启用。
|
|
151
|
+
|
|
152
|
+
## 自定义插件
|
|
153
|
+
|
|
154
|
+
在 \`plugins/\` 目录下创建插件包(可用 \`bun run new <名称>\` 生成骨架),在 \`koishi.yml\` 中以相对路径引用(如 \`./plugins/my-plugin\`)即可启用;Bun 直接加载 TypeScript 源码,无需预编译。
|
|
155
|
+
`
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* 内置模板的 package.json 基础内容(name/version 会被 renderManifest 覆写,
|
|
159
|
+
* prod 模式下 workspaces 与 devDependencies 会被移除)。
|
|
160
|
+
*/
|
|
161
|
+
function baseManifest() {
|
|
162
|
+
return {
|
|
163
|
+
type: "module",
|
|
164
|
+
workspaces: ["plugins/*", "external/*"],
|
|
165
|
+
scripts: {
|
|
166
|
+
start: "koishi start",
|
|
167
|
+
dev: "cross-env NODE_ENV=development koishi start",
|
|
168
|
+
new: "koishi-scripts setup"
|
|
169
|
+
},
|
|
170
|
+
dependencies: {
|
|
171
|
+
"@koishi-ce/koishi": "^1.0.0",
|
|
172
|
+
"@koishi-ce/plugin-actions": "^1.0.0",
|
|
173
|
+
"@koishi-ce/plugin-admin": "^1.0.0",
|
|
174
|
+
"@koishi-ce/plugin-analytics": "^1.0.0",
|
|
175
|
+
"@koishi-ce/plugin-auth": "^1.0.0",
|
|
176
|
+
"@koishi-ce/plugin-bind": "^1.0.0",
|
|
177
|
+
"@koishi-ce/plugin-commands": "^1.0.0",
|
|
178
|
+
"@koishi-ce/plugin-config": "^1.0.0",
|
|
179
|
+
"@koishi-ce/plugin-console": "^1.0.0",
|
|
180
|
+
"@koishi-ce/plugin-explorer": "^1.0.0",
|
|
181
|
+
"@koishi-ce/plugin-help": "^1.0.0",
|
|
182
|
+
"@koishi-ce/plugin-http": "^1.0.0",
|
|
183
|
+
"@koishi-ce/plugin-insight": "^1.0.0",
|
|
184
|
+
"@koishi-ce/plugin-inspect": "^1.0.0",
|
|
185
|
+
"@koishi-ce/plugin-locales": "^1.0.0",
|
|
186
|
+
"@koishi-ce/plugin-logger": "^1.0.0",
|
|
187
|
+
"@koishi-ce/plugin-market": "^1.0.0",
|
|
188
|
+
"@koishi-ce/plugin-notifier": "^1.0.0",
|
|
189
|
+
"@koishi-ce/plugin-oobe": "^1.0.0",
|
|
190
|
+
"@koishi-ce/plugin-proxy-agent": "^1.0.0",
|
|
191
|
+
"@koishi-ce/plugin-sandbox": "^1.0.0",
|
|
192
|
+
"@koishi-ce/plugin-server": "^1.0.0",
|
|
193
|
+
"@koishi-ce/plugin-status": "^1.0.0",
|
|
194
|
+
koishi: "npm:@koishi-ce/koishi-shim@^4.18.11"
|
|
195
|
+
},
|
|
196
|
+
devDependencies: {
|
|
197
|
+
"@koishi-ce/client": "^1.0.0",
|
|
198
|
+
"@koishi-ce/plugin-hmr": "^1.0.0",
|
|
199
|
+
"@koishi-ce/scripts": "^1.0.0",
|
|
200
|
+
"bun-types": "^1.4.0",
|
|
201
|
+
"cross-env": "^7.0.3"
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/index.ts
|
|
207
|
+
/**
|
|
208
|
+
* create-koishi-ce 脚手架(npm 包名 create-koishi-ce,目录名为
|
|
209
|
+
* apps/koishi-create,二者不一致是历史遗留,以目录名为准)。
|
|
210
|
+
*
|
|
211
|
+
* 通过 `bunx create-koishi-ce [name]`(npx 亦可)交互式创建 Koishi 机器人
|
|
212
|
+
* 应用项目:确定项目名 → 准备目标目录 → 写入内置 @koishi-ce 模板(默认,
|
|
213
|
+
* 见 src/template.ts;--template <包名> 可改用 npm registry 远程模板,如
|
|
214
|
+
* 上游官方 @koishijs/boilerplate)→ 按需初始化 git → 询问是否立即安装
|
|
215
|
+
* 依赖并启动。CLI 可执行入口在 src/bin.ts(构建产物 lib/bin.mjs,bin 字段
|
|
216
|
+
* 指向它);本文件只承载主流程与可单测的纯函数(范式对齐
|
|
217
|
+
* @koishi-ce/scripts)。
|
|
218
|
+
*/
|
|
219
|
+
const { version } = package_default;
|
|
76
220
|
/** fetch 请求非 2xx 时抛出的错误,携带 HTTP 状态码与状态文本 */
|
|
77
221
|
var HttpError = class extends Error {
|
|
78
222
|
status;
|
|
@@ -108,6 +252,34 @@ function detectAgent() {
|
|
|
108
252
|
if (ua.startsWith("pnpm")) return "pnpm";
|
|
109
253
|
return "bun";
|
|
110
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* 从单个 .npmrc 文件提取 registry 配置项(非注释、非 scoped 的 registry= 行)。
|
|
257
|
+
* 文件不存在或未配置时返回 undefined,读文件异常一律静默吞掉。
|
|
258
|
+
*/
|
|
259
|
+
function readNpmrcRegistry(file) {
|
|
260
|
+
try {
|
|
261
|
+
for (const line of readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
262
|
+
const value = /^\s*registry\s*=\s*(\S+)\s*$/.exec(line)?.[1];
|
|
263
|
+
if (value) return value;
|
|
264
|
+
}
|
|
265
|
+
} catch {}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 读取本机 npm registry 配置(优先级对齐 npm 自身:环境变量 > 项目
|
|
269
|
+
* .npmrc > 用户 ~/.npmrc)。刻意不 spawn 子进程探测——既有实现按
|
|
270
|
+
* user-agent 选 `bun config get registry`,而 Bun 没有 config 子命令,
|
|
271
|
+
* bunx 场景下子进程退出码 1 直接炸掉脚手架;读 npmrc 是零依赖、零
|
|
272
|
+
* 子进程的等价路径。任何一步都拿不到时返回 undefined,由调用方回落
|
|
273
|
+
* 官方源。
|
|
274
|
+
*/
|
|
275
|
+
function getLocalRegistry(cwd = process.cwd(), userHome = homedir()) {
|
|
276
|
+
const candidates = [
|
|
277
|
+
process.env["npm_config_registry"],
|
|
278
|
+
readNpmrcRegistry(join(cwd, ".npmrc")),
|
|
279
|
+
readNpmrcRegistry(join(userHome, ".npmrc"))
|
|
280
|
+
];
|
|
281
|
+
for (const candidate of candidates) if (candidate?.startsWith("https://") || candidate?.startsWith("http://")) return candidate;
|
|
282
|
+
}
|
|
111
283
|
/** 静默执行命令探测其是否可用(如 git --version),失败即视为不可用 */
|
|
112
284
|
function supports(command) {
|
|
113
285
|
return spawnSync(command[0] ?? "", command.slice(1), { stdio: "ignore" }).status === 0;
|
|
@@ -190,18 +362,22 @@ function writePackageJson() {
|
|
|
190
362
|
writeFileSync(filename, renderManifest(meta, project, argv.prod === true));
|
|
191
363
|
}
|
|
192
364
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
|
|
196
|
-
|
|
365
|
+
* 写入内置模板(默认路径):静态文件(src/template.ts 的 templateFiles)
|
|
366
|
+
* 加上由 baseManifest() 渲染出的 package.json。纯本地写入,无网络请求。
|
|
367
|
+
*/
|
|
368
|
+
function scaffoldBuiltin() {
|
|
369
|
+
for (const [file, content] of Object.entries(templateFiles)) writeFileSync(join(rootDir, file), content);
|
|
370
|
+
writeFileSync(join(rootDir, "package.json"), renderManifest(baseManifest(), project, argv.prod === true));
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* 远程模板下载与解包(--template 指定时):
|
|
374
|
+
* 1. 拉取模板包元数据,按 dist-tags 解析目标版本(--ref,默认 latest);
|
|
375
|
+
* 2. 流式下载 tarball 并解包到目标目录(strip: 1 去掉包根目录层级),
|
|
197
376
|
* 网络错误统一以 HttpError 提示后退出;
|
|
198
|
-
*
|
|
377
|
+
* 3. 最后改写 package.json。
|
|
199
378
|
*/
|
|
200
|
-
async function
|
|
201
|
-
|
|
202
|
-
const registry = (argv.registry || await getRegistry() || "https://registry.npmjs.org").replace(/\/$/, "");
|
|
203
|
-
console.log(kleur.dim(` 使用 registry:${registry}\n`));
|
|
204
|
-
const template = argv.template || "@koishijs/boilerplate";
|
|
379
|
+
async function scaffoldRemote(registry) {
|
|
380
|
+
const template = argv.template;
|
|
205
381
|
const ref = argv.ref || "latest";
|
|
206
382
|
try {
|
|
207
383
|
const metaRes = await fetch(`${registry}/${template}`);
|
|
@@ -226,6 +402,19 @@ async function scaffold() {
|
|
|
226
402
|
process.exit(1);
|
|
227
403
|
}
|
|
228
404
|
writePackageJson();
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* 生成项目的主入口:默认写内置 @koishi-ce 模板(scaffoldBuiltin);
|
|
408
|
+
* --template <包名> 时改为从 npm registry 下载远程模板(scaffoldRemote),
|
|
409
|
+
* registry 取值 --registry 参数 > 本机 npm 配置 > 官方源。
|
|
410
|
+
*/
|
|
411
|
+
async function scaffold() {
|
|
412
|
+
console.log(kleur.dim(" 正在 ") + project + kleur.dim(" 中生成项目 ..."));
|
|
413
|
+
if (argv.template) {
|
|
414
|
+
const registry = (argv.registry || getLocalRegistry() || "https://registry.npmjs.org").replace(/\/$/, "");
|
|
415
|
+
console.log(kleur.dim(` 使用 registry:${registry}\n`));
|
|
416
|
+
await scaffoldRemote(registry);
|
|
417
|
+
} else scaffoldBuiltin();
|
|
229
418
|
console.log(kleur.green(" 完成。\n"));
|
|
230
419
|
}
|
|
231
420
|
/**
|
|
@@ -286,8 +475,8 @@ async function start() {
|
|
|
286
475
|
用法:create-koishi-ce [名称] [选项]
|
|
287
476
|
|
|
288
477
|
选项:
|
|
289
|
-
-t, --template <名称>
|
|
290
|
-
-r, --ref <引用>
|
|
478
|
+
-t, --template <名称> 从 npm registry 下载指定模板包(默认使用内置 @koishi-ce 模板)
|
|
479
|
+
-r, --ref <引用> 远程模板版本引用(默认 latest)
|
|
291
480
|
-f, --forced 强制清空目标目录
|
|
292
481
|
-g, --git 初始化 git 仓库
|
|
293
482
|
--registry <地址> 指定 npm registry(如 https://registry.npmmirror.com)
|
|
@@ -309,4 +498,4 @@ async function start() {
|
|
|
309
498
|
await install();
|
|
310
499
|
}
|
|
311
500
|
//#endregion
|
|
312
|
-
export { renderManifest as n,
|
|
501
|
+
export { start as a, renderManifest as i, getLocalRegistry as n, readNpmrcRegistry as r, detectAgent as t };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-koishi-ce",
|
|
3
3
|
"description": "Setup a Koishi application",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.mjs",
|
|
7
7
|
"module": "./lib/index.mjs",
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
"@types/yargs-parser": "^21.0.3"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"get-registry": "^1.1.0",
|
|
51
50
|
"kleur": "^4.1.5",
|
|
52
51
|
"prompts": "^2.4.2",
|
|
53
52
|
"tar": "^7.5.22",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { getLocalRegistry, readNpmrcRegistry } from "../index.ts";
|
|
6
|
+
|
|
7
|
+
test("readNpmrcRegistry:提取首个 registry= 行,忽略注释与空行", () => {
|
|
8
|
+
const dir = mkdtempSync(join(tmpdir(), "ckc-npmrc-"));
|
|
9
|
+
const file = join(dir, ".npmrc");
|
|
10
|
+
writeFileSync(
|
|
11
|
+
file,
|
|
12
|
+
"# 注释行\n; 另一种注释\n\nstrict-ssl=false\nregistry=https://registry.npmmirror.com/\r\n@scope:registry=https://example.com/\n",
|
|
13
|
+
);
|
|
14
|
+
expect(readNpmrcRegistry(file)).toBe("https://registry.npmmirror.com/");
|
|
15
|
+
rmSync(dir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("readNpmrcRegistry:文件不存在或未配置时返回 undefined 而不抛错", () => {
|
|
19
|
+
expect(
|
|
20
|
+
readNpmrcRegistry(join(tmpdir(), "ckc-不存在", ".npmrc")),
|
|
21
|
+
).toBeUndefined();
|
|
22
|
+
const dir = mkdtempSync(join(tmpdir(), "ckc-npmrc-"));
|
|
23
|
+
const file = join(dir, ".npmrc");
|
|
24
|
+
writeFileSync(file, "strict-ssl=false\n");
|
|
25
|
+
expect(readNpmrcRegistry(file)).toBeUndefined();
|
|
26
|
+
rmSync(dir, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("getLocalRegistry:环境变量优先且校验协议,非法值跳过", () => {
|
|
30
|
+
const key = "npm_config_registry";
|
|
31
|
+
const isolated = mkdtempSync(join(tmpdir(), "ckc-npmrc-"));
|
|
32
|
+
process.env[key] = "https://registry.example.com/";
|
|
33
|
+
expect(getLocalRegistry(isolated, isolated)).toBe(
|
|
34
|
+
"https://registry.example.com/",
|
|
35
|
+
);
|
|
36
|
+
// 非 http(s) 的值视为未配置,继续走后续候选
|
|
37
|
+
process.env[key] = "ftp://not-a-registry/";
|
|
38
|
+
expect(getLocalRegistry(isolated, isolated)).toBeUndefined();
|
|
39
|
+
delete process.env[key];
|
|
40
|
+
rmSync(isolated, { recursive: true, force: true });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("getLocalRegistry:项目 .npmrc 优先于用户级 .npmrc,均无配置时返回 undefined", () => {
|
|
44
|
+
const key = "npm_config_registry";
|
|
45
|
+
delete process.env[key];
|
|
46
|
+
const dir = mkdtempSync(join(tmpdir(), "ckc-npmrc-"));
|
|
47
|
+
const emptyHome = mkdtempSync(join(tmpdir(), "ckc-home-"));
|
|
48
|
+
writeFileSync(join(dir, ".npmrc"), "registry=http://localhost:4873/\n");
|
|
49
|
+
expect(getLocalRegistry(dir, emptyHome)).toBe("http://localhost:4873/");
|
|
50
|
+
// 目录与用户级均无 .npmrc 时返回 undefined,主流程回落官方源
|
|
51
|
+
expect(getLocalRegistry(emptyHome, emptyHome)).toBeUndefined();
|
|
52
|
+
rmSync(dir, { recursive: true, force: true });
|
|
53
|
+
rmSync(emptyHome, { recursive: true, force: true });
|
|
54
|
+
});
|