create-pangza 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/README.md +6 -1
- package/lib/generate.js +12 -6
- package/lib/modules.js +154 -40
- package/lib/scaffold.js +12 -9
- package/package.json +2 -2
- package/template/base/tsconfig.json +4 -0
package/README.md
CHANGED
|
@@ -10,7 +10,12 @@ npx create-pangza init my-app
|
|
|
10
10
|
npm create pangza@latest -- init my-app
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
交互勾选模块:`orm` / `auth` / `redis` / `log` / `schedule` / `encrypt` / `validate`。
|
|
13
|
+
交互勾选模块:`orm` / `auth` / `redis` / `log` / `schedule` / `encrypt` / `validate`。
|
|
14
|
+
勾选 `orm` / `redis` 时会写入 `EnableOrm()` / `EnableRedis()`;未勾选则启动不连库、不因缺配置报错。不生成 `.gitkeep`。
|
|
15
|
+
|
|
16
|
+
初始化还会生成:
|
|
17
|
+
- `application.yaml`:默认 `server.port: 5270`;按所选模块追加 `redis` / `datasource`+`orm` 等
|
|
18
|
+
- `tsconfig.json`:`@/*` → `src/*`(dev:`tsconfig-paths`;build:`tsc-alias`)
|
|
14
19
|
|
|
15
20
|
```bash
|
|
16
21
|
npx create-pangza init my-app --modules=orm,auth,redis --db=mysql
|
package/lib/generate.js
CHANGED
|
@@ -31,7 +31,13 @@ function findProjectRoot(cwd = process.cwd()) {
|
|
|
31
31
|
...(json.dependencies || {}),
|
|
32
32
|
...(json.devDependencies || {}),
|
|
33
33
|
};
|
|
34
|
-
if (
|
|
34
|
+
if (
|
|
35
|
+
deps["@pangza/express"] ||
|
|
36
|
+
deps["@pangza/application"] ||
|
|
37
|
+
deps["@pangza/core"]
|
|
38
|
+
) {
|
|
39
|
+
return dir;
|
|
40
|
+
}
|
|
35
41
|
} catch {
|
|
36
42
|
/* ignore */
|
|
37
43
|
}
|
|
@@ -59,7 +65,7 @@ function writeFile(filePath, content, { force }) {
|
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
function tplEntity({ Pascal, snake }) {
|
|
62
|
-
return `import { TableField, TableId, TableName } from "@pangza/
|
|
68
|
+
return `import { TableField, TableId, TableName } from "@pangza/orm";
|
|
63
69
|
|
|
64
70
|
@TableName("${snake}")
|
|
65
71
|
export default class ${Pascal}Entity {
|
|
@@ -89,7 +95,7 @@ export default class ${Pascal}Entity {
|
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
function tplMapper({ Pascal, fileBase }) {
|
|
92
|
-
return `import { Mapper, MapperType } from "@pangza/
|
|
98
|
+
return `import { Mapper, MapperType } from "@pangza/orm";
|
|
93
99
|
import ${Pascal}Entity from "../entity/${fileBase}";
|
|
94
100
|
|
|
95
101
|
@Mapper(${Pascal}Entity)
|
|
@@ -98,7 +104,7 @@ export default class ${Pascal}Mapper extends MapperType {}
|
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
function tplService({ Pascal, fileBase }) {
|
|
101
|
-
return `import { BaseService, Service } from "@pangza/
|
|
107
|
+
return `import { BaseService, Service } from "@pangza/core";
|
|
102
108
|
import ${Pascal}Mapper from "../mapper/${fileBase}";
|
|
103
109
|
|
|
104
110
|
@Service(${Pascal}Mapper)
|
|
@@ -115,8 +121,8 @@ function tplController({ Pascal, fileBase, apiPath, camel }) {
|
|
|
115
121
|
PutMapping,
|
|
116
122
|
RequestBody,
|
|
117
123
|
RequestPath,
|
|
118
|
-
|
|
119
|
-
} from "@pangza/
|
|
124
|
+
} from "@pangza/application";
|
|
125
|
+
import { Resource } from "@pangza/core";
|
|
120
126
|
import ${Pascal}Service from "../../service/${fileBase}";
|
|
121
127
|
|
|
122
128
|
@Controller("/api/${apiPath}")
|
package/lib/modules.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** 可选能力模块(按分包安装;入口按模块分包导入) */
|
|
4
4
|
const MODULES = [
|
|
5
5
|
{
|
|
6
6
|
id: "orm",
|
|
@@ -35,7 +35,7 @@ const MODULES = [
|
|
|
35
35
|
{
|
|
36
36
|
id: "validate",
|
|
37
37
|
label: "Validate",
|
|
38
|
-
hint: "
|
|
38
|
+
hint: "参数校验装饰器(含在 @pangza/core)",
|
|
39
39
|
},
|
|
40
40
|
];
|
|
41
41
|
|
|
@@ -81,7 +81,8 @@ const DB_ENV = {
|
|
|
81
81
|
/** @param {string[]} moduleIds @param {string} [dbType] */
|
|
82
82
|
function resolveDependencies(moduleIds, dbType = "mysql") {
|
|
83
83
|
const deps = {
|
|
84
|
-
"@pangza/
|
|
84
|
+
"@pangza/application": "^1.1.0",
|
|
85
|
+
"@pangza/core": "^1.1.0",
|
|
85
86
|
cors: "^2.8.5",
|
|
86
87
|
express: "^5.2.1",
|
|
87
88
|
inversify: "^8.2.3",
|
|
@@ -90,18 +91,28 @@ function resolveDependencies(moduleIds, dbType = "mysql") {
|
|
|
90
91
|
const set = new Set(moduleIds);
|
|
91
92
|
|
|
92
93
|
if (set.has("orm")) {
|
|
94
|
+
deps["@pangza/orm"] = "^1.1.0";
|
|
93
95
|
Object.assign(deps, DB_DEPS[dbType] || DB_DEPS.mysql);
|
|
94
96
|
}
|
|
95
97
|
if (set.has("redis") || set.has("auth")) {
|
|
98
|
+
deps["@pangza/redis"] = "^1.1.0";
|
|
96
99
|
deps.redis = "^6.2.1";
|
|
97
100
|
}
|
|
98
101
|
if (set.has("auth")) {
|
|
102
|
+
deps["@pangza/auth"] = "^1.1.0";
|
|
99
103
|
deps.jsonwebtoken = "^9.0.3";
|
|
100
104
|
deps.bcryptjs = "^3.0.3";
|
|
101
105
|
}
|
|
106
|
+
if (set.has("log")) {
|
|
107
|
+
deps["@pangza/log"] = "^1.1.0";
|
|
108
|
+
}
|
|
102
109
|
if (set.has("schedule")) {
|
|
110
|
+
deps["@pangza/schedule"] = "^1.1.0";
|
|
103
111
|
deps["node-cron"] = "^4.6.0";
|
|
104
112
|
}
|
|
113
|
+
if (set.has("encrypt")) {
|
|
114
|
+
deps["@pangza/encrypt"] = "^1.1.0";
|
|
115
|
+
}
|
|
105
116
|
|
|
106
117
|
return deps;
|
|
107
118
|
}
|
|
@@ -142,74 +153,177 @@ function resolveDirs(moduleIds) {
|
|
|
142
153
|
return dirs;
|
|
143
154
|
}
|
|
144
155
|
|
|
156
|
+
const DB_YAML = {
|
|
157
|
+
mysql: {
|
|
158
|
+
type: "mysql",
|
|
159
|
+
port: 3306,
|
|
160
|
+
user: "root",
|
|
161
|
+
database: "pangza",
|
|
162
|
+
},
|
|
163
|
+
postgresql: {
|
|
164
|
+
type: "postgresql",
|
|
165
|
+
port: 5432,
|
|
166
|
+
user: "postgres",
|
|
167
|
+
database: "pangza",
|
|
168
|
+
},
|
|
169
|
+
mssql: {
|
|
170
|
+
type: "sqlserver",
|
|
171
|
+
port: 1433,
|
|
172
|
+
user: "sa",
|
|
173
|
+
database: "pangza",
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
145
177
|
/**
|
|
146
|
-
*
|
|
178
|
+
* 按所选模块生成 application.yaml(始终含 server.port=5270)。
|
|
179
|
+
* @param {string[]} moduleIds
|
|
180
|
+
* @param {string} [dbType]
|
|
181
|
+
*/
|
|
182
|
+
function generateApplicationYaml(moduleIds, dbType = "mysql") {
|
|
183
|
+
const set = new Set(moduleIds);
|
|
184
|
+
const lines = [
|
|
185
|
+
"# Generated by create-pangza init",
|
|
186
|
+
"# profile: PANGZA_PROFILE / APP_ENV / NODE_ENV(默认 development)",
|
|
187
|
+
"",
|
|
188
|
+
"server:",
|
|
189
|
+
" port: 5270",
|
|
190
|
+
" trustProxy: false",
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
if (set.has("redis") || set.has("auth")) {
|
|
194
|
+
lines.push(
|
|
195
|
+
"",
|
|
196
|
+
"redis:",
|
|
197
|
+
" host: 127.0.0.1",
|
|
198
|
+
" port: 6379",
|
|
199
|
+
" password: \"\"",
|
|
200
|
+
" db: 0",
|
|
201
|
+
" keyPrefix: \"pangza:\"",
|
|
202
|
+
" mode: standalone",
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (set.has("orm")) {
|
|
207
|
+
const db = DB_YAML[dbType] || DB_YAML.mysql;
|
|
208
|
+
lines.push(
|
|
209
|
+
"",
|
|
210
|
+
"datasource:",
|
|
211
|
+
" default:",
|
|
212
|
+
` type: ${db.type}`,
|
|
213
|
+
" host: 127.0.0.1",
|
|
214
|
+
` port: ${db.port}`,
|
|
215
|
+
` user: ${db.user}`,
|
|
216
|
+
" password: \"\"",
|
|
217
|
+
` database: ${db.database}`,
|
|
218
|
+
" connectionLimit: 10",
|
|
219
|
+
"",
|
|
220
|
+
"orm:",
|
|
221
|
+
" logicDelete: true",
|
|
222
|
+
" logicDeleteField: delete_flag",
|
|
223
|
+
" logicDeleteValue: 1",
|
|
224
|
+
" logicNotDeleteValue: 0",
|
|
225
|
+
" idType: ASSIGN_ID",
|
|
226
|
+
" mapUnderscoreToCamelCase: true",
|
|
227
|
+
" mapperLocations: []",
|
|
228
|
+
" sqlLog:",
|
|
229
|
+
" enabled: false",
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (set.has("encrypt")) {
|
|
234
|
+
lines.push(
|
|
235
|
+
"",
|
|
236
|
+
"# encrypt: 密钥建议放代码或环境变量,勿提交生产密钥到仓库",
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (set.has("schedule")) {
|
|
241
|
+
lines.push("", "# schedule: 任务写在 src/schedule,由 Application.scan 加载");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (set.has("log")) {
|
|
245
|
+
lines.push("", "# log: 在入口 registry(Log({ writer })) 中配置落库");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (set.has("auth")) {
|
|
249
|
+
lines.push(
|
|
250
|
+
"",
|
|
251
|
+
"# auth: jwtSecret 等在入口 Auth({ ... }) 配置,勿写进 yaml 明文(除非自管)",
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
lines.push("");
|
|
256
|
+
return lines.join("\n");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 生成空项目入口:只起 HTTP;选了 redis/orm 才 Enable*,否则启动不连库。
|
|
147
261
|
* @param {string[]} moduleIds
|
|
148
262
|
*/
|
|
149
263
|
function generateMainTs(moduleIds) {
|
|
150
264
|
const set = new Set(moduleIds);
|
|
265
|
+
|
|
151
266
|
const imports = [
|
|
152
267
|
'import "reflect-metadata";',
|
|
153
268
|
'import cors from "cors";',
|
|
154
269
|
'import { json } from "express";',
|
|
155
|
-
'import { Application
|
|
270
|
+
'import { Application } from "@pangza/application";',
|
|
271
|
+
'import { Logger } from "@pangza/core";',
|
|
156
272
|
];
|
|
157
273
|
|
|
158
|
-
const notes = [];
|
|
159
274
|
if (set.has("orm")) {
|
|
160
|
-
|
|
161
|
-
"// ORM: 配置 .env 中的数据库变量后即可使用 @Mapper / QueryWrapper",
|
|
162
|
-
);
|
|
275
|
+
imports.push('import { EnableOrm } from "@pangza/orm";');
|
|
163
276
|
}
|
|
164
277
|
if (set.has("redis") || set.has("auth")) {
|
|
165
|
-
|
|
278
|
+
imports.push('import Redis, { EnableRedis } from "@pangza/redis";');
|
|
166
279
|
}
|
|
167
280
|
if (set.has("auth")) {
|
|
168
|
-
|
|
169
|
-
'// Auth: Application.registry(Auth({ ignore: ["/api/pub/**"] }))',
|
|
170
|
-
);
|
|
281
|
+
imports.push('import Auth from "@pangza/auth";');
|
|
171
282
|
}
|
|
172
283
|
if (set.has("log")) {
|
|
173
|
-
|
|
284
|
+
imports.push('import Log from "@pangza/log";');
|
|
174
285
|
}
|
|
286
|
+
|
|
287
|
+
const notes = [];
|
|
175
288
|
if (set.has("schedule")) {
|
|
176
|
-
notes.push("// Schedule: 在 src/schedule
|
|
289
|
+
notes.push("// Schedule: 在 src/schedule 编写任务并由扫描加载(@pangza/schedule)");
|
|
177
290
|
}
|
|
178
291
|
if (set.has("encrypt")) {
|
|
179
|
-
notes.push("// Encrypt:
|
|
292
|
+
notes.push("// Encrypt: import { AESUtil } from \"@pangza/encrypt\"");
|
|
180
293
|
}
|
|
181
294
|
if (set.has("validate")) {
|
|
182
|
-
notes.push("// Validate: 在 Controller
|
|
295
|
+
notes.push("// Validate: 在 Controller 参数上使用 @pangza/core 校验装饰器");
|
|
183
296
|
}
|
|
184
297
|
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
"",
|
|
188
|
-
...(notes.length ? [...notes, ""] : []),
|
|
189
|
-
"Application.registry(Logger({ level: \"info\" }))",
|
|
298
|
+
const chain = [
|
|
299
|
+
'Application.registry(Logger({ level: "info" }))',
|
|
190
300
|
" .registry(cors())",
|
|
191
301
|
" .registry(json())",
|
|
192
|
-
' .scan("src")',
|
|
193
|
-
" .start();",
|
|
194
|
-
"",
|
|
195
302
|
];
|
|
196
|
-
return body.join("\n");
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function generateGitkeepReadme(moduleIds) {
|
|
200
|
-
const set = new Set(moduleIds);
|
|
201
|
-
const files = {
|
|
202
|
-
"src/controller/.gitkeep": "",
|
|
203
|
-
};
|
|
204
303
|
if (set.has("orm")) {
|
|
205
|
-
|
|
206
|
-
files["src/mapper/.gitkeep"] = "";
|
|
207
|
-
files["src/service/.gitkeep"] = "";
|
|
304
|
+
chain.push(" .registry(EnableOrm())");
|
|
208
305
|
}
|
|
209
|
-
if (set.has("
|
|
210
|
-
|
|
306
|
+
if (set.has("redis") || set.has("auth")) {
|
|
307
|
+
chain.push(" .registry(EnableRedis())");
|
|
308
|
+
}
|
|
309
|
+
if (set.has("auth")) {
|
|
310
|
+
chain.push(
|
|
311
|
+
' .registry(Auth({ ignore: ["/api/pub/**"], redis: Redis }))',
|
|
312
|
+
);
|
|
211
313
|
}
|
|
212
|
-
|
|
314
|
+
if (set.has("log")) {
|
|
315
|
+
chain.push(" .registry(Log())");
|
|
316
|
+
}
|
|
317
|
+
chain.push(' .scan("src")');
|
|
318
|
+
chain.push(" .start();");
|
|
319
|
+
|
|
320
|
+
return [
|
|
321
|
+
...imports,
|
|
322
|
+
"",
|
|
323
|
+
...(notes.length ? [...notes, ""] : []),
|
|
324
|
+
...chain,
|
|
325
|
+
"",
|
|
326
|
+
].join("\n");
|
|
213
327
|
}
|
|
214
328
|
|
|
215
329
|
module.exports = {
|
|
@@ -218,6 +332,6 @@ module.exports = {
|
|
|
218
332
|
resolveDependencies,
|
|
219
333
|
resolveEnvLines,
|
|
220
334
|
resolveDirs,
|
|
335
|
+
generateApplicationYaml,
|
|
221
336
|
generateMainTs,
|
|
222
|
-
generateGitkeepReadme,
|
|
223
337
|
};
|
package/lib/scaffold.js
CHANGED
|
@@ -6,8 +6,8 @@ const {
|
|
|
6
6
|
resolveDependencies,
|
|
7
7
|
resolveEnvLines,
|
|
8
8
|
resolveDirs,
|
|
9
|
+
generateApplicationYaml,
|
|
9
10
|
generateMainTs,
|
|
10
|
-
generateGitkeepReadme,
|
|
11
11
|
} = require("./modules");
|
|
12
12
|
|
|
13
13
|
const ROOT = path.join(__dirname, "..");
|
|
@@ -45,7 +45,6 @@ function scaffold(opts) {
|
|
|
45
45
|
|
|
46
46
|
ensureDir(targetDir);
|
|
47
47
|
|
|
48
|
-
// base static files
|
|
49
48
|
copyFile(
|
|
50
49
|
path.join(BASE_TEMPLATE, "tsconfig.json"),
|
|
51
50
|
path.join(targetDir, "tsconfig.json"),
|
|
@@ -66,7 +65,7 @@ function scaffold(opts) {
|
|
|
66
65
|
description: "Pangza Express app",
|
|
67
66
|
scripts: {
|
|
68
67
|
dev: "ts-node -r tsconfig-paths/register src/main.ts",
|
|
69
|
-
build: "tsc -p tsconfig.json",
|
|
68
|
+
build: "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
|
|
70
69
|
start: "node dist/main.js",
|
|
71
70
|
},
|
|
72
71
|
dependencies: resolveDependencies(modules, dbType),
|
|
@@ -75,6 +74,7 @@ function scaffold(opts) {
|
|
|
75
74
|
"@types/express": "^5.0.6",
|
|
76
75
|
"@types/node": "^22.0.0",
|
|
77
76
|
"ts-node": "^10.9.2",
|
|
77
|
+
"tsc-alias": "^1.8.16",
|
|
78
78
|
"tsconfig-paths": "^4.2.0",
|
|
79
79
|
typescript: "^5.9.3",
|
|
80
80
|
},
|
|
@@ -88,13 +88,12 @@ function scaffold(opts) {
|
|
|
88
88
|
path.join(targetDir, ".env.example"),
|
|
89
89
|
resolveEnvLines(modules, dbType).join("\n") + "\n",
|
|
90
90
|
);
|
|
91
|
+
writeFile(
|
|
92
|
+
path.join(targetDir, "application.yaml"),
|
|
93
|
+
generateApplicationYaml(modules, dbType),
|
|
94
|
+
);
|
|
91
95
|
writeFile(path.join(targetDir, "src", "main.ts"), generateMainTs(modules));
|
|
92
96
|
|
|
93
|
-
const extras = generateGitkeepReadme(modules);
|
|
94
|
-
for (const [rel, content] of Object.entries(extras)) {
|
|
95
|
-
writeFile(path.join(targetDir, rel), content);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
97
|
writeFile(
|
|
99
98
|
path.join(targetDir, "README.md"),
|
|
100
99
|
`# ${projectName}
|
|
@@ -109,10 +108,14 @@ ${modules.includes("orm") ? `Database: **${dbType}**\n` : ""}
|
|
|
109
108
|
## Run
|
|
110
109
|
|
|
111
110
|
\`\`\`bash
|
|
112
|
-
cp .env.example .env
|
|
111
|
+
cp .env.example .env # 可选;端口与库配置以 application.yaml 为准
|
|
113
112
|
npm install
|
|
114
113
|
npm run dev
|
|
115
114
|
\`\`\`
|
|
115
|
+
|
|
116
|
+
- 默认端口:\`application.yaml\` → \`server.port: 5270\`
|
|
117
|
+
- 路径别名:\`@/*\` → \`src/*\`(dev 用 tsconfig-paths,build 用 tsc-alias)
|
|
118
|
+
- 需要 Redis / 数据库时,入口已按模块写入 \`EnableRedis()\` / \`EnableOrm()\`
|
|
116
119
|
`,
|
|
117
120
|
);
|
|
118
121
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pangza",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Scaffold & generate for @pangza
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Scaffold & generate for @pangza/* (init / g controller|service|mapper|entity|all)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-pangza": "./bin/create-pangza.js"
|
|
7
7
|
},
|