create-koa-boot 1.0.2 → 1.0.4
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 +110 -0
- package/index.js +103 -36
- package/interactive.js +112 -0
- package/package.json +2 -1
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# create-koa-boot
|
|
2
|
+
|
|
3
|
+
基于 Koa 3 + Prisma + tsoa 的 **AI 驱动全栈框架**脚手架 CLI。
|
|
4
|
+
|
|
5
|
+
一键生成全栈工程,内置 **AI 辅助命令**(`ai:sync` / `ai:model` / `ai:api` / `ai:vue-page`):AI 通过扫描项目生成 `.ai/` 元数据理解代码库,再一键生成后端模型、CRUD 接口与前端页面,形成"AI 读懂项目 → 按规范生成 → 人工审核微调"的落地工作流。
|
|
6
|
+
|
|
7
|
+
## 快速开始
|
|
8
|
+
|
|
9
|
+
### 交互式(推荐,create-vue 风格)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx create-koa-boot
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
按提示依次选择即可:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
? Project name (my-app): my-app
|
|
19
|
+
? Include Vue 3 + Element Plus frontend? (Y/n): Yes
|
|
20
|
+
? Include uniapp mobile app? (y/N): No
|
|
21
|
+
? Auto install dependencies? (y/N): No
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- 后端(Koa 3)**始终生成**,前端 / uniapp 按需勾选;
|
|
25
|
+
- 选 `Auto install dependencies` 会自动安装依赖(自动检测 pnpm / npm)。
|
|
26
|
+
|
|
27
|
+
### 非交互(脚本 / CI)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx create-koa-boot my-app # 纯后端骨架
|
|
31
|
+
npx create-koa-boot my-app --example # 三端完整示例(backend + frontend + uniapp)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
生成后:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd my-app
|
|
38
|
+
npm install
|
|
39
|
+
# 编辑 .env 配置 DATABASE_URL(MySQL),然后:
|
|
40
|
+
npx prisma db push # 建表(首次启动自动创建 admin/admin123)
|
|
41
|
+
npm run dev # http://localhost:3003
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
生成项目的端点:
|
|
45
|
+
|
|
46
|
+
- 系统文档:`http://localhost:3003/api-docs/system`
|
|
47
|
+
- 业务文档:`http://localhost:3003/api-docs/business`
|
|
48
|
+
- 登录:`POST /api/system/user/login`(`admin` / `admin123`)
|
|
49
|
+
|
|
50
|
+
## AI 辅助命令
|
|
51
|
+
|
|
52
|
+
在**已有项目根目录**执行(`npx create-koa-boot ai:*`):
|
|
53
|
+
|
|
54
|
+
| 命令 | 作用 |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `ai:sync` | 扫描项目生成 `.ai/` 元数据(`entities.json` 模型清单 / `api-list.json` 接口清单 / `permissions.json` 权限清单),供 AI 理解代码库 |
|
|
57
|
+
| `ai:model --name <m> --model <M> [--fields "f:t,f2:t?"]` | 生成业务模型 + DTO(Create 必填中文校验 / Update 全可选,`?` 后缀生成可选字段) |
|
|
58
|
+
| `ai:api --name <m> --model <M>` | 生成 tsoa controller + service(CRUD + 搜索 + 分页六件套,遵循框架规范) |
|
|
59
|
+
| `ai:vue-page --name <m> --model <M>` | 生成前端 API 封装 + CRUD 页面(Element Plus,定位到 `frontend/src/`) |
|
|
60
|
+
|
|
61
|
+
示例:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
create-koa-boot ai:sync
|
|
65
|
+
create-koa-boot ai:model --name product --model Product --fields "name:string,price:int,stock:int?"
|
|
66
|
+
create-koa-boot ai:api --name product --model Product
|
|
67
|
+
create-koa-boot ai:vue-page --name product --model Product
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**推荐工作流**:
|
|
71
|
+
|
|
72
|
+
1. 先 `ai:sync` 刷新 `.ai/` 元数据;
|
|
73
|
+
2. AI 依据 `.ai/entities.json` 理解模型,用 `ai:model` / `ai:api` / `ai:vue-page` 生成代码;
|
|
74
|
+
3. 复杂业务手写时,严格参照 `src/business/demo/` 蓝本(模型校验、分页、响应包装、异常处理)。
|
|
75
|
+
|
|
76
|
+
生成项目自带 `AGENTS.md`(AI 开发规则),约束 AI 的目录约定、分层规范、权限模型与代码风格;修改 Prisma schema 后运行 `pnpm gen:types` → `pnpm db:push`,新增 controller 后运行 `pnpm doc` 重新生成路由。
|
|
77
|
+
|
|
78
|
+
## 生成的项目结构
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
my-app/
|
|
82
|
+
├── backend/ # Koa 3 + tsoa 后端(--example 时)
|
|
83
|
+
│ ├── prisma/schema.prisma # 合并后的 Schema(系统 15 表 + 业务表)
|
|
84
|
+
│ └── src/
|
|
85
|
+
│ ├── business/ # 业务模块(demo 为蓝本)
|
|
86
|
+
│ ├── init/ # 初始化种子(菜单/权限)
|
|
87
|
+
│ └── build/ # 生成的 swagger.json / routes
|
|
88
|
+
├── frontend/ # Vue 3 + Element Plus + Vite 前端(--example 时)
|
|
89
|
+
├── uniapp/ # 移动端(--example 时)
|
|
90
|
+
└── .ai/ # ai:sync 生成的元数据(entities/api-list/permissions)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 技术栈
|
|
94
|
+
|
|
95
|
+
- **运行时**:Koa 3 + @koa/router + tsoa 6.6(OpenAPI 代码生成)
|
|
96
|
+
- **ORM**:Prisma 5.19(MySQL)
|
|
97
|
+
- **认证**:JWT + RBAC 角色权限(DB 表 `sys_permissions` 路径匹配)
|
|
98
|
+
- **缓存/队列**:Redis(可降级)
|
|
99
|
+
- **前端**:Vue 3 + Element Plus + Vite + Pinia + axios
|
|
100
|
+
- **语言**:TypeScript 5.9
|
|
101
|
+
|
|
102
|
+
## 环境要求
|
|
103
|
+
|
|
104
|
+
- Node.js >= 18.19
|
|
105
|
+
- MySQL 8.x(运行时数据)
|
|
106
|
+
- Redis(可选,缺失时自动降级)
|
|
107
|
+
|
|
108
|
+
## 相关
|
|
109
|
+
|
|
110
|
+
- 运行时核心:`koa-boot-runtime`
|
package/index.js
CHANGED
|
@@ -10,16 +10,19 @@
|
|
|
10
10
|
* 3. 生成 package.json / tsconfig / tsoa.json / .env.example 等
|
|
11
11
|
*
|
|
12
12
|
* 用法:
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* create-koa-boot # 交互式(create-vue 风格提问)
|
|
14
|
+
* create-koa-boot <project-name> # 纯后端骨架
|
|
15
|
+
* create-koa-boot <project-name> --example # 三端完整示例(backend + frontend + uniapp)
|
|
15
16
|
*/
|
|
16
17
|
const fs = require('fs');
|
|
17
18
|
const path = require('path');
|
|
19
|
+
const { spawnSync } = require('child_process');
|
|
20
|
+
const { runInteractive, c } = require('./interactive');
|
|
18
21
|
|
|
19
22
|
const TEMPLATE_DIR = path.join(__dirname, 'templates');
|
|
20
23
|
|
|
21
|
-
//
|
|
22
|
-
const SKIP_BASE = new Set(['node_modules', 'dist', 'build', '.git']);
|
|
24
|
+
// 复制时排除的目录/文件(生成物由用户项目自行产生;example 仅 --example/交互选择时单独复制)
|
|
25
|
+
const SKIP_BASE = new Set(['node_modules', 'dist', 'build', '.git', 'example']);
|
|
23
26
|
// 三端示例额外排除:本机 workaround 产物、构建产物、敏感配置
|
|
24
27
|
const SKIP_EXAMPLE = new Set([
|
|
25
28
|
'node_modules', 'dist', 'build', '.git',
|
|
@@ -111,8 +114,72 @@ function parseArgs(argv) {
|
|
|
111
114
|
};
|
|
112
115
|
}
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
// 自动安装依赖:优先 pnpm,其次 npm(检测本机可用性)
|
|
118
|
+
function runInstall(targetDir) {
|
|
119
|
+
const detect = (cmd) => {
|
|
120
|
+
const r = spawnSync(cmd, ['--version'], { shell: true, stdio: 'ignore' });
|
|
121
|
+
return !r.error && r.status === 0;
|
|
122
|
+
};
|
|
123
|
+
const pm = detect('pnpm') ? 'pnpm' : 'npm';
|
|
124
|
+
console.log(`\n ⚡ 正在使用 ${pm} 安装依赖(可能需要几分钟)...\n`);
|
|
125
|
+
const r = spawnSync(pm, ['install'], { cwd: targetDir, shell: true, stdio: 'inherit' });
|
|
126
|
+
if (r.error || r.status !== 0) {
|
|
127
|
+
console.log(`\n ${c.red('✖')} 自动安装失败,请手动执行:`);
|
|
128
|
+
console.log(` cd ${path.basename(targetDir)}`);
|
|
129
|
+
console.log(` ${pm} install\n`);
|
|
130
|
+
} else {
|
|
131
|
+
console.log(`\n ${c.green('✓')} 依赖安装完成\n`);
|
|
132
|
+
}
|
|
133
|
+
return pm;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 按选择的端打印后续步骤(useSubdir=true 表示三端布局,后端在 backend/ 子目录)
|
|
137
|
+
function printNextSteps(opts) {
|
|
138
|
+
const { projectName, frontend, uniapp, pm, useSubdir } = opts;
|
|
139
|
+
console.log('\n ✅ 项目生成完成!下一步:\n');
|
|
140
|
+
console.log(` cd ${projectName}`);
|
|
141
|
+
console.log(` ${pm} install # 或 npm install,安装全部依赖`);
|
|
142
|
+
if (useSubdir) {
|
|
143
|
+
console.log(' # 后端:');
|
|
144
|
+
console.log(' cd backend && cp .env.example .env # Windows: copy .env.example .env');
|
|
145
|
+
console.log(' 编辑 .env 配置 DATABASE_URL(MySQL)后:');
|
|
146
|
+
console.log(' pnpm gen:types && pnpm db:push && pnpm dev');
|
|
147
|
+
console.log(' API 文档: http://localhost:3003/api-docs/business\n');
|
|
148
|
+
} else {
|
|
149
|
+
console.log(' 编辑 .env 配置 DATABASE_URL(MySQL)后:');
|
|
150
|
+
console.log(' npx prisma db push # 建表(自动创建 admin/admin123)');
|
|
151
|
+
console.log(' npm run dev # 启动: http://localhost:3003');
|
|
152
|
+
console.log(' API 文档: http://localhost:3003/api-docs/business');
|
|
153
|
+
console.log(' 系统文档: http://localhost:3003/api-docs/system\n');
|
|
154
|
+
}
|
|
155
|
+
if (frontend) {
|
|
156
|
+
console.log(' # 前端(另开终端):');
|
|
157
|
+
console.log(' cd frontend && pnpm dev # http://localhost:5175\n');
|
|
158
|
+
}
|
|
159
|
+
if (uniapp) {
|
|
160
|
+
console.log(' # uniapp:用 HBuilderX 打开 uniapp 目录启动预览\n');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function main() {
|
|
165
|
+
const argv = process.argv.slice(2);
|
|
166
|
+
|
|
167
|
+
// 无参数 → 交互式(create-vue 风格)
|
|
168
|
+
let opts;
|
|
169
|
+
if (argv.length === 0) {
|
|
170
|
+
const ans = await runInteractive();
|
|
171
|
+
opts = {
|
|
172
|
+
projectName: ans.projectName,
|
|
173
|
+
frontend: ans.frontend,
|
|
174
|
+
uniapp: ans.uniapp,
|
|
175
|
+
autoInstall: ans.autoInstall,
|
|
176
|
+
skipInstall: false,
|
|
177
|
+
};
|
|
178
|
+
} else {
|
|
179
|
+
opts = parseArgs(argv);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { projectName, skipInstall, example = false, frontend = false, uniapp = false, autoInstall = false } = opts;
|
|
116
183
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
117
184
|
|
|
118
185
|
if (fs.existsSync(targetDir)) {
|
|
@@ -120,34 +187,30 @@ function main() {
|
|
|
120
187
|
process.exit(1);
|
|
121
188
|
}
|
|
122
189
|
|
|
123
|
-
|
|
124
|
-
|
|
190
|
+
// --example 等价于「后端 + 前端 + uniapp」全选
|
|
191
|
+
const withExample = example || frontend || uniapp;
|
|
192
|
+
|
|
193
|
+
if (withExample) {
|
|
194
|
+
console.log(`\n ⚡ 正在生成 koa-boot 项目: ${projectName}\n`);
|
|
195
|
+
|
|
196
|
+
copyDir(path.join(TEMPLATE_DIR, 'example', 'backend'), path.join(targetDir, 'backend'), SKIP_EXAMPLE);
|
|
197
|
+
console.log(' ✓ backend 模板已复制(koa-boot-runtime ^1.0.0)');
|
|
125
198
|
|
|
126
|
-
|
|
127
|
-
copyDir(path.join(TEMPLATE_DIR, 'example',
|
|
199
|
+
if (frontend || example) {
|
|
200
|
+
copyDir(path.join(TEMPLATE_DIR, 'example', 'frontend'), path.join(targetDir, 'frontend'), SKIP_EXAMPLE);
|
|
201
|
+
console.log(' ✓ frontend 模板已复制(Vue 3 + Element Plus)');
|
|
202
|
+
}
|
|
203
|
+
if (uniapp || example) {
|
|
204
|
+
copyDir(path.join(TEMPLATE_DIR, 'example', 'uniapp'), path.join(targetDir, 'uniapp'), SKIP_EXAMPLE);
|
|
205
|
+
console.log(' ✓ uniapp 模板已复制(含 uni_modules 组件库)');
|
|
128
206
|
}
|
|
129
207
|
if (fs.existsSync(path.join(TEMPLATE_DIR, 'example', 'AGENTS.md'))) {
|
|
130
208
|
fs.copyFileSync(path.join(TEMPLATE_DIR, 'example', 'AGENTS.md'), path.join(targetDir, 'AGENTS.md'));
|
|
131
209
|
console.log(' ✓ AGENTS.md(AI 开发规则)已生成');
|
|
132
210
|
}
|
|
133
|
-
console.log(' ✓ backend 模板已复制(koa-boot-runtime ^1.0.0)');
|
|
134
|
-
console.log(' ✓ frontend 模板已复制');
|
|
135
|
-
console.log(' ✓ uniapp 模板已复制(含 uni_modules 组件库)');
|
|
136
211
|
|
|
137
212
|
patchExampleBackend(targetDir, projectName);
|
|
138
213
|
console.log(' ✓ backend/package.json 已配置');
|
|
139
|
-
|
|
140
|
-
console.log('\n ✅ 三端示例生成完成!下一步:\n');
|
|
141
|
-
console.log(` cd ${projectName}`);
|
|
142
|
-
console.log(' pnpm install # 或 npm install,安装全部依赖');
|
|
143
|
-
console.log(' # 后端:');
|
|
144
|
-
console.log(' cd backend && cp .env.example .env # Windows: copy .env.example .env');
|
|
145
|
-
console.log(' 编辑 .env 配置 DATABASE_URL(MySQL)后:');
|
|
146
|
-
console.log(' pnpm gen:types && pnpm db:push && pnpm dev');
|
|
147
|
-
console.log(' API 文档: http://localhost:3003/api-docs/business\n');
|
|
148
|
-
console.log(' # 前端(另开终端):');
|
|
149
|
-
console.log(' cd frontend && pnpm dev\n');
|
|
150
|
-
console.log(' # uniapp:用 HBuilderX 打开 uniapp 目录启动预览\n');
|
|
151
214
|
} else {
|
|
152
215
|
console.log(`\n ⚡ 正在生成 koa-boot 项目: ${projectName}\n`);
|
|
153
216
|
|
|
@@ -165,21 +228,25 @@ function main() {
|
|
|
165
228
|
pkg.name = projectName;
|
|
166
229
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
167
230
|
console.log(' ✓ package.json 已生成');
|
|
168
|
-
|
|
169
|
-
// 4. 提示后续步骤
|
|
170
|
-
console.log('\n ✅ 项目生成完成!下一步:\n');
|
|
171
|
-
console.log(` cd ${projectName}`);
|
|
172
|
-
console.log(' npm install # 或 pnpm install');
|
|
173
|
-
console.log(' 编辑 .env 配置 DATABASE_URL(MySQL)后:');
|
|
174
|
-
console.log(' npx prisma db push # 建表(自动创建 admin/admin123)');
|
|
175
|
-
console.log(' npm run dev # 启动: http://localhost:3003');
|
|
176
|
-
console.log(' API 文档: http://localhost:3003/api-docs/business');
|
|
177
|
-
console.log(' 系统文档: http://localhost:3003/api-docs/system\n');
|
|
178
231
|
}
|
|
179
232
|
|
|
233
|
+
let pm = 'pnpm';
|
|
180
234
|
if (skipInstall) {
|
|
181
235
|
console.log(' (已跳过自动安装,请手动 npm install)\n');
|
|
236
|
+
} else if (autoInstall) {
|
|
237
|
+
pm = runInstall(targetDir);
|
|
182
238
|
}
|
|
239
|
+
|
|
240
|
+
printNextSteps({
|
|
241
|
+
projectName,
|
|
242
|
+
frontend: frontend || example,
|
|
243
|
+
uniapp: uniapp || example,
|
|
244
|
+
pm,
|
|
245
|
+
useSubdir: withExample,
|
|
246
|
+
});
|
|
183
247
|
}
|
|
184
248
|
|
|
185
|
-
main()
|
|
249
|
+
main().catch((err) => {
|
|
250
|
+
console.error(err);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
});
|
package/interactive.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* create-koa-boot 交互式提示(create-vue 风格)
|
|
4
|
+
*
|
|
5
|
+
* 零依赖实现:基于 Node 内置 readline。
|
|
6
|
+
* 仅在「无参数」运行时启用;带参数运行走原有非交互逻辑。
|
|
7
|
+
*
|
|
8
|
+
* 实现说明:采用「行队列」模型——所有到达的输入行先入队,
|
|
9
|
+
* 每个问题从队列取一行消费,避免一次性 stdin(管道/脚本)下
|
|
10
|
+
* readline 同步派发行而 await 恢复滞后导致丢行的问题。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const readline = require('readline');
|
|
14
|
+
|
|
15
|
+
// 轻量 ANSI 颜色(Windows Terminal / PowerShell 7+ 均支持)
|
|
16
|
+
const c = {
|
|
17
|
+
cyan: (s) => `\x1b[36m${s}\x1b[39m`,
|
|
18
|
+
green: (s) => `\x1b[32m${s}\x1b[39m`,
|
|
19
|
+
yellow: (s) => `\x1b[33m${s}\x1b[39m`,
|
|
20
|
+
red: (s) => `\x1b[31m${s}\x1b[39m`,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function createInterface() {
|
|
24
|
+
const rl = readline.createInterface({
|
|
25
|
+
input: process.stdin,
|
|
26
|
+
output: process.stdout,
|
|
27
|
+
});
|
|
28
|
+
const lines = []; // 已到达、待消费的输入行
|
|
29
|
+
const pending = []; // 等待输入的问题回调(参数为行内容,null 表示 EOF)
|
|
30
|
+
let closed = false;
|
|
31
|
+
|
|
32
|
+
rl.on('line', (l) => {
|
|
33
|
+
const cb = pending.shift();
|
|
34
|
+
if (cb) cb(l);
|
|
35
|
+
else lines.push(l);
|
|
36
|
+
});
|
|
37
|
+
rl.on('close', () => {
|
|
38
|
+
closed = true;
|
|
39
|
+
while (pending.length) pending.shift()(null); // EOF:未答问题取默认值
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return { rl, lines, pending, closed: () => closed };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 取一行输入:优先消费缓冲行,否则注册等待;EOF/关闭时返回 defaultValue */
|
|
46
|
+
function nextLine(io, promptText, defaultValue) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
if (io.closed()) return resolve(defaultValue);
|
|
49
|
+
if (io.lines.length) {
|
|
50
|
+
process.stdout.write(promptText);
|
|
51
|
+
return resolve(io.lines.shift());
|
|
52
|
+
}
|
|
53
|
+
process.stdout.write(promptText);
|
|
54
|
+
io.pending.push((l) => resolve(l === null ? defaultValue : l));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 文本输入:返回用户输入;空输入取默认值;validate 返回错误文案时重新询问 */
|
|
59
|
+
async function askText(io, question, defaultValue, validate) {
|
|
60
|
+
const suffix = defaultValue ? ` ${c.cyan(`(${defaultValue})`)}` : '';
|
|
61
|
+
const prompt = ` ${c.cyan('?')} ${question}${suffix}: `;
|
|
62
|
+
for (;;) {
|
|
63
|
+
const line = await nextLine(io, prompt, defaultValue);
|
|
64
|
+
const val = (line || '').trim() || defaultValue;
|
|
65
|
+
if (validate) {
|
|
66
|
+
const err = validate(val);
|
|
67
|
+
if (err) {
|
|
68
|
+
console.log(` ${c.red('✖')} ${err}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return val;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** 确认输入:y/n/回车(默认值);返回 boolean */
|
|
77
|
+
async function askConfirm(io, question, defaultYes) {
|
|
78
|
+
const hint = defaultYes ? '(Y/n)' : '(y/N)';
|
|
79
|
+
const prompt = ` ${c.cyan('?')} ${question} ${c.yellow(hint)}: `;
|
|
80
|
+
for (;;) {
|
|
81
|
+
const line = await nextLine(io, prompt, defaultYes ? 'y' : 'n');
|
|
82
|
+
const a = (line || '').trim().toLowerCase();
|
|
83
|
+
if (a === 'y' || a === 'yes') return true;
|
|
84
|
+
if (a === 'n' || a === 'no') return false;
|
|
85
|
+
if (!a) return defaultYes;
|
|
86
|
+
// 无效输入:重新询问
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 交互式提问主流程,返回:
|
|
92
|
+
* { projectName, frontend, uniapp, autoInstall }
|
|
93
|
+
*/
|
|
94
|
+
async function runInteractive() {
|
|
95
|
+
const io = createInterface();
|
|
96
|
+
try {
|
|
97
|
+
const projectName = await askText(io, 'Project name', 'my-app', (v) => {
|
|
98
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9-_.]*$/.test(v)) {
|
|
99
|
+
return '仅允许字母、数字、- _ .,且不能以符号开头';
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
});
|
|
103
|
+
const frontend = await askConfirm(io, 'Include Vue 3 + Element Plus frontend?', true);
|
|
104
|
+
const uniapp = await askConfirm(io, 'Include uniapp mobile app?', false);
|
|
105
|
+
const autoInstall = await askConfirm(io, 'Auto install dependencies?', false);
|
|
106
|
+
return { projectName, frontend, uniapp, autoInstall };
|
|
107
|
+
} finally {
|
|
108
|
+
io.rl.close();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = { runInteractive, askText, askConfirm, c };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-koa-boot",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Scaffolding CLI: generate a koa-boot-runtime project with system routes, demo business modules and merged Prisma schema",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"koa",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"files": [
|
|
27
27
|
"bin",
|
|
28
28
|
"index.js",
|
|
29
|
+
"interactive.js",
|
|
29
30
|
"ai.js",
|
|
30
31
|
"ai",
|
|
31
32
|
"templates"
|