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 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 (deps["@pangza/express"]) return dir;
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/express";
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/express";
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/express";
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
- Resource,
119
- } from "@pangza/express";
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
- /** 可选能力模块(当前均由单包 @pangza/express 提供,差异在依赖与脚手架文件) */
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/express": "^1.0.0",
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
- * 生成空项目入口:只起 HTTP,模块相关注释提示用户自行接入。
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, Logger } from "@pangza/express";',
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
- notes.push(
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
- notes.push("// Redis: 配置 REDIS_* 后由框架自动连接(见文档)");
278
+ imports.push('import Redis, { EnableRedis } from "@pangza/redis";');
166
279
  }
167
280
  if (set.has("auth")) {
168
- notes.push(
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
- notes.push("// Log: configureLog({ writer }) 后配合 @Log 装饰器");
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: 从 @pangza/express 导入 AESUtil 等");
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 body = [
186
- ...imports,
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
- files["src/entity/.gitkeep"] = "";
206
- files["src/mapper/.gitkeep"] = "";
207
- files["src/service/.gitkeep"] = "";
304
+ chain.push(" .registry(EnableOrm())");
208
305
  }
209
- if (set.has("schedule")) {
210
- files["src/schedule/.gitkeep"] = "";
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
- return files;
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.0.0",
4
- "description": "Scaffold & generate for @pangza/express (init / g controller|service|mapper|entity|all)",
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
  },
@@ -4,6 +4,10 @@
4
4
  "module": "CommonJS",
5
5
  "outDir": "dist",
6
6
  "rootDir": "src",
7
+ "baseUrl": ".",
8
+ "paths": {
9
+ "@/*": ["src/*"]
10
+ },
7
11
  "strict": true,
8
12
  "esModuleInterop": true,
9
13
  "skipLibCheck": true,