create-skweb 2.1.0 → 3.0.3

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.
Files changed (43) hide show
  1. package/{dist/cli.cjs → .rollup.cache/Users/stayknight/projects/skweb-mono/tools/create-skweb/dist/cli.js} +7 -125
  2. package/.rollup.cache/Users/stayknight/projects/skweb-mono/tools/create-skweb/dist/index.js +106 -0
  3. package/.rollup.cache/Users/stayknight/projects/skweb-mono/tools/create-skweb/tsconfig.tsbuildinfo +1 -0
  4. package/.turbo/turbo-build.log +9 -10
  5. package/.turbo/turbo-check.log +5 -4
  6. package/.turbo/turbo-lint.log +12 -4
  7. package/CHANGELOG.md +48 -0
  8. package/dist/cli.d.ts +0 -1
  9. package/dist/cli.js +109 -2
  10. package/dist/cli.js.map +1 -1
  11. package/dist/index.d.ts +0 -1
  12. package/dist/index.js +4 -5
  13. package/dist/index.js.map +1 -1
  14. package/package.json +10 -18
  15. package/rollup.config.mjs +23 -22
  16. package/scripts/inject-shebang.mjs +1 -1
  17. package/src/cli.ts +7 -7
  18. package/src/index.ts +2 -2
  19. package/templates/cjs/package.json +18 -18
  20. package/templates/cjs/src/app.js +16 -11
  21. package/templates/cjs/src/controllers/hello.js +43 -31
  22. package/templates/cjs/src/controllers/user.js +199 -86
  23. package/templates/cjs/src/cronWorker.js +7 -9
  24. package/templates/mjs/package.json +18 -18
  25. package/templates/mjs/src/app.js +8 -10
  26. package/templates/mjs/src/controllers/hello.js +43 -31
  27. package/templates/mjs/src/controllers/user.js +199 -86
  28. package/templates/mjs/src/cronWorker.js +7 -9
  29. package/templates/ts/package.json +24 -24
  30. package/templates/ts/rollup.config.mjs +2 -0
  31. package/templates/ts/src/app.ts +9 -10
  32. package/templates/ts/src/controllers/hello.ts +43 -31
  33. package/templates/ts/src/controllers/user.ts +199 -86
  34. package/templates/ts/src/cronWorker.ts +7 -9
  35. package/templates/ts/tsconfig.json +4 -5
  36. package/test/index.mjs +24 -13
  37. package/tsconfig.json +6 -6
  38. package/tsconfig.tsbuildinfo +1 -0
  39. package/dist/cli.cjs.map +0 -1
  40. package/dist/cli.d.ts.map +0 -1
  41. package/dist/index.cjs +0 -123
  42. package/dist/index.cjs.map +0 -1
  43. package/dist/index.d.ts.map +0 -1
@@ -3,99 +3,212 @@ import { db } from '../services/database.js'
3
3
 
4
4
  const { SysUser } = db.sequelize.models
5
5
 
6
- export default [
7
- {
8
- method: 'get',
9
- path: '/api/users',
10
- async handler(req: any) {
11
- const { page = 1, limit = 10 } = req.query
12
- const users = await SysUser.findAndCountAll({
13
- offset: (Number(page) - 1) * Number(limit),
14
- limit: Number(limit)
15
- })
16
- return {
17
- list: users.rows,
18
- total: users.count
19
- }
6
+ /**
7
+ * @swagger
8
+ * /api/users:
9
+ * get:
10
+ * summary: 获取用户列表
11
+ * tags: [Users]
12
+ * parameters:
13
+ * - in: query
14
+ * name: page
15
+ * schema: { type: integer, minimum: 1, default: 1 }
16
+ * description: 页码
17
+ * - in: query
18
+ * name: limit
19
+ * schema: { type: integer, minimum: 1, default: 10 }
20
+ * description: 每页条数
21
+ * responses:
22
+ * 200:
23
+ * description: OK
24
+ */
25
+ const userList = {
26
+ method: 'get',
27
+ path: '/api/users',
28
+ async handler(req: any) {
29
+ const { page = 1, limit = 10 } = req.query
30
+ const users = await SysUser.findAndCountAll({
31
+ offset: (Number(page) - 1) * Number(limit),
32
+ limit: Number(limit)
33
+ })
34
+ return {
35
+ list: users.rows,
36
+ total: users.count
20
37
  }
38
+ }
39
+ }
40
+
41
+ /**
42
+ * @swagger
43
+ * /api/users/{id}:
44
+ * get:
45
+ * summary: 获取单个用户
46
+ * tags: [Users]
47
+ * parameters:
48
+ * - in: path
49
+ * name: id
50
+ * required: true
51
+ * schema: { type: integer, minimum: 1 }
52
+ * responses:
53
+ * 200:
54
+ * description: OK
55
+ * 404:
56
+ * description: 用户不存在
57
+ */
58
+ const userGet = {
59
+ method: 'get',
60
+ path: '/api/users/:id',
61
+ paramsSchema: {
62
+ type: 'object',
63
+ required: ['id'],
64
+ properties: { id: { type: 'integer', minimum: 1 } }
21
65
  },
22
- {
23
- method: 'get',
24
- path: '/api/users/:id',
25
- paramsSchema: {
26
- type: 'object',
27
- required: ['id'],
28
- properties: { id: { type: 'integer', minimum: 1 } }
29
- },
30
- async handler(req: any) {
31
- const { id } = req.validatedParams
32
- const user = await SysUser.findByPk(id)
33
- if (!user) {
34
- throw new createHttpError.NotFound('User not found')
35
- }
36
- return user
66
+ async handler(req: any) {
67
+ const { id } = req.validatedParams
68
+ const user = await SysUser.findByPk(id)
69
+ if (!user) {
70
+ throw new createHttpError.NotFound('User not found')
71
+ }
72
+ return user
73
+ }
74
+ }
75
+
76
+ /**
77
+ * @swagger
78
+ * /api/users:
79
+ * post:
80
+ * summary: 创建用户
81
+ * tags: [Users]
82
+ * requestBody:
83
+ * required: true
84
+ * content:
85
+ * application/json:
86
+ * schema:
87
+ * type: object
88
+ * required: [username, password]
89
+ * properties:
90
+ * username: { type: string, minLength: 3 }
91
+ * password: { type: string, minLength: 6 }
92
+ * email: { type: string, format: email }
93
+ * phone: { type: string }
94
+ * responses:
95
+ * 200:
96
+ * description: 创建成功
97
+ */
98
+ const userCreate = {
99
+ method: 'post',
100
+ path: '/api/users',
101
+ bodySchema: {
102
+ type: 'object',
103
+ required: ['username', 'password'],
104
+ properties: {
105
+ username: { type: 'string', minLength: 3 },
106
+ password: { type: 'string', minLength: 6 },
107
+ email: { type: 'string', format: 'email' },
108
+ phone: { type: 'string' }
37
109
  }
38
110
  },
39
- {
40
- method: 'post',
41
- path: '/api/users',
42
- bodySchema: {
43
- type: 'object',
44
- required: ['username', 'password'],
45
- properties: {
46
- username: { type: 'string', minLength: 3 },
47
- password: { type: 'string', minLength: 6 },
48
- email: { type: 'string', format: 'email' },
49
- phone: { type: 'string' }
50
- }
51
- },
52
- async handler(req: any) {
53
- return await SysUser.create(req.validatedBody)
111
+ async handler(req: any) {
112
+ return await SysUser.create(req.validatedBody)
113
+ }
114
+ }
115
+
116
+ /**
117
+ * @swagger
118
+ * /api/users/{id}:
119
+ * put:
120
+ * summary: 更新用户
121
+ * tags: [Users]
122
+ * parameters:
123
+ * - in: path
124
+ * name: id
125
+ * required: true
126
+ * schema: { type: integer, minimum: 1 }
127
+ * requestBody:
128
+ * required: true
129
+ * content:
130
+ * application/json:
131
+ * schema:
132
+ * type: object
133
+ * properties:
134
+ * password: { type: string, minLength: 6 }
135
+ * email: { type: string, format: email }
136
+ * phone: { type: string }
137
+ * status: { type: integer }
138
+ * responses:
139
+ * 200:
140
+ * description: OK
141
+ * 404:
142
+ * description: 用户不存在
143
+ */
144
+ const userUpdate = {
145
+ method: 'put',
146
+ path: '/api/users/:id',
147
+ paramsSchema: {
148
+ type: 'object',
149
+ required: ['id'],
150
+ properties: { id: { type: 'integer', minimum: 1 } }
151
+ },
152
+ bodySchema: {
153
+ type: 'object',
154
+ properties: {
155
+ password: { type: 'string', minLength: 6 },
156
+ email: { type: 'string', format: 'email' },
157
+ phone: { type: 'string' },
158
+ status: { type: 'integer' }
54
159
  }
55
160
  },
56
- {
57
- method: 'put',
58
- path: '/api/users/:id',
59
- paramsSchema: {
60
- type: 'object',
61
- required: ['id'],
62
- properties: { id: { type: 'integer', minimum: 1 } }
63
- },
64
- bodySchema: {
65
- type: 'object',
66
- properties: {
67
- password: { type: 'string', minLength: 6 },
68
- email: { type: 'string', format: 'email' },
69
- phone: { type: 'string' },
70
- status: { type: 'integer' }
71
- }
72
- },
73
- async handler(req: any) {
74
- const { id } = req.validatedParams
75
- const user = await SysUser.findByPk(id)
76
- if (!user) {
77
- throw new createHttpError.NotFound('User not found')
78
- }
79
- await user.update(req.validatedBody)
80
- return user
161
+ async handler(req: any) {
162
+ const { id } = req.validatedParams
163
+ const user = await SysUser.findByPk(id)
164
+ if (!user) {
165
+ throw new createHttpError.NotFound('User not found')
81
166
  }
167
+ await user.update(req.validatedBody)
168
+ return user
169
+ }
170
+ }
171
+
172
+ /**
173
+ * @swagger
174
+ * /api/users/{id}:
175
+ * delete:
176
+ * summary: 删除用户
177
+ * tags: [Users]
178
+ * parameters:
179
+ * - in: path
180
+ * name: id
181
+ * required: true
182
+ * schema: { type: integer, minimum: 1 }
183
+ * responses:
184
+ * 200:
185
+ * description: OK
186
+ * 404:
187
+ * description: 用户不存在
188
+ */
189
+ const userDelete = {
190
+ method: 'delete',
191
+ path: '/api/users/:id',
192
+ paramsSchema: {
193
+ type: 'object',
194
+ required: ['id'],
195
+ properties: { id: { type: 'integer', minimum: 1 } }
82
196
  },
83
- {
84
- method: 'delete',
85
- path: '/api/users/:id',
86
- paramsSchema: {
87
- type: 'object',
88
- required: ['id'],
89
- properties: { id: { type: 'integer', minimum: 1 } }
90
- },
91
- async handler(req: any) {
92
- const { id } = req.validatedParams
93
- const user = await SysUser.findByPk(id)
94
- if (!user) {
95
- throw new createHttpError.NotFound('User not found')
96
- }
97
- await user.destroy()
98
- return { message: 'User deleted' }
197
+ async handler(req: any) {
198
+ const { id } = req.validatedParams
199
+ const user = await SysUser.findByPk(id)
200
+ if (!user) {
201
+ throw new createHttpError.NotFound('User not found')
99
202
  }
203
+ await user.destroy()
204
+ return { message: 'User deleted' }
100
205
  }
101
- ]
206
+ }
207
+
208
+ export default {
209
+ userList,
210
+ userGet,
211
+ userCreate,
212
+ userUpdate,
213
+ userDelete
214
+ }
@@ -66,16 +66,14 @@ async function startCronWorker(): Promise<void> {
66
66
  framework.start()
67
67
  logger.info('[cron-worker] Cron worker started')
68
68
 
69
- process.on('SIGINT', async () => {
70
- logger.info('[cron-worker] Received SIGINT, shutting down...')
71
- await framework.stop()
72
- process.exit(0)
69
+ // 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
70
+ process.on('uncaughtException', (err) => {
71
+ logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
72
+ process.exit(1)
73
73
  })
74
-
75
- process.on('SIGTERM', async () => {
76
- logger.info('[cron-worker] Received SIGTERM, shutting down...')
77
- await framework.stop()
78
- process.exit(0)
74
+ process.on('unhandledRejection', (reason) => {
75
+ logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
76
+ process.exit(1)
79
77
  })
80
78
  }
81
79
 
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2020",
3
+ "target": "ES2022",
4
4
  "module": "ESNext",
5
- "lib": ["ES2020"],
6
- "moduleResolution": "Node",
5
+ "lib": ["ES2022"],
6
+ "moduleResolution": "Bundler",
7
7
  "strict": true,
8
8
  "esModuleInterop": true,
9
9
  "skipLibCheck": true,
@@ -12,8 +12,7 @@
12
12
  "declaration": true,
13
13
  "sourceMap": true,
14
14
  "rootDir": "src",
15
- "outDir": "dist",
16
- "baseUrl": "."
15
+ "outDir": "dist"
17
16
  },
18
17
  "include": ["src/**/*.ts"],
19
18
  "exclude": ["node_modules", "dist", "test"]
package/test/index.mjs CHANGED
@@ -8,6 +8,12 @@ import { createProject, printBanner, printSuccess } from '../dist/index.js'
8
8
  const __filename = fileURLToPath(import.meta.url)
9
9
  const __dirname = path.dirname(__filename)
10
10
 
11
+ // helper: createProject 把项目放在 <dest>/<name> 下,
12
+ // 测试断言用的所有路径都要走 <dest>/<name>。
13
+ function projectPath(tmpDir, name = 'my-app') {
14
+ return path.join(tmpDir, name)
15
+ }
16
+
11
17
  describe('create-skweb', () => {
12
18
  describe('createProject()', () => {
13
19
  let tmpDir
@@ -21,42 +27,47 @@ describe('create-skweb', () => {
21
27
  })
22
28
 
23
29
  it('should create TypeScript project from template', async () => {
30
+ const root = projectPath(tmpDir)
24
31
  await createProject({
25
32
  name: 'my-app',
26
33
  template: 'ts',
27
34
  dest: tmpDir
28
35
  })
29
36
 
30
- expect(fs.existsSync(path.join(tmpDir, 'package.json'))).to.be.true
31
- expect(fs.existsSync(path.join(tmpDir, 'src', 'app.ts'))).to.be.true
32
- expect(fs.existsSync(path.join(tmpDir, 'src', 'utils', 'logger.ts'))).to.be.true
37
+ expect(fs.existsSync(path.join(root, 'package.json'))).to.be.true
38
+ expect(fs.existsSync(path.join(root, 'src', 'app.ts'))).to.be.true
39
+ expect(fs.existsSync(path.join(root, 'src', 'utils', 'logger.ts'))).to.be.true
33
40
  })
34
41
 
35
42
  it('should create MJS project from template', async () => {
43
+ const root = projectPath(tmpDir)
36
44
  await createProject({
37
45
  name: 'my-app',
38
46
  template: 'mjs',
39
47
  dest: tmpDir
40
48
  })
41
49
 
42
- expect(fs.existsSync(path.join(tmpDir, 'package.json'))).to.be.true
43
- expect(fs.existsSync(path.join(tmpDir, 'src', 'app.js'))).to.be.true
50
+ expect(fs.existsSync(path.join(root, 'package.json'))).to.be.true
51
+ expect(fs.existsSync(path.join(root, 'src', 'app.js'))).to.be.true
44
52
  })
45
53
 
46
54
  it('should create CJS project from template', async () => {
55
+ const root = projectPath(tmpDir)
47
56
  await createProject({
48
57
  name: 'my-app',
49
58
  template: 'cjs',
50
59
  dest: tmpDir
51
60
  })
52
61
 
53
- expect(fs.existsSync(path.join(tmpDir, 'package.json'))).to.be.true
54
- expect(fs.existsSync(path.join(tmpDir, 'src', 'app.js'))).to.be.true
62
+ expect(fs.existsSync(path.join(root, 'package.json'))).to.be.true
63
+ expect(fs.existsSync(path.join(root, 'src', 'app.js'))).to.be.true
55
64
  })
56
65
 
57
66
  it('should reject non-empty destination directory', async () => {
58
- // tmpDir 中创建一个文件
59
- fs.writeFileSync(path.join(tmpDir, 'existing.txt'), 'x')
67
+ // 在目标项目目录 (tmpDir/my-app) 里创建一个文件,createProject 应拒绝
68
+ const target = projectPath(tmpDir)
69
+ fs.mkdirsSync(target)
70
+ fs.writeFileSync(path.join(target, 'existing.txt'), 'x')
60
71
 
61
72
  try {
62
73
  await createProject({
@@ -79,7 +90,7 @@ describe('create-skweb', () => {
79
90
  template,
80
91
  dest: tmpDir
81
92
  })
82
- const pkg = JSON.parse(fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'))
93
+ const pkg = JSON.parse(fs.readFileSync(path.join(projectPath(tmpDir), 'package.json'), 'utf-8'))
83
94
  expect(pkg.dependencies.winston, `${template} should have winston dep`).to.exist
84
95
  }
85
96
  })
@@ -93,7 +104,7 @@ describe('create-skweb', () => {
93
104
  template,
94
105
  dest: tmpDir
95
106
  })
96
- const pkg = JSON.parse(fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'))
107
+ const pkg = JSON.parse(fs.readFileSync(path.join(projectPath(tmpDir), 'package.json'), 'utf-8'))
97
108
  expect(pkg.dependencies['@dotenvx/dotenvx'], `${template} should have dotenvx`).to.exist
98
109
  }
99
110
  })
@@ -107,7 +118,7 @@ describe('create-skweb', () => {
107
118
  template,
108
119
  dest: tmpDir
109
120
  })
110
- expect(fs.existsSync(path.join(tmpDir, 'ecosystem.config.js')), `${template} should have pm2 ecosystem`).to.be.true
121
+ expect(fs.existsSync(path.join(projectPath(tmpDir), 'ecosystem.config.js')), `${template} should have pm2 ecosystem`).to.be.true
111
122
  }
112
123
  })
113
124
 
@@ -117,7 +128,7 @@ describe('create-skweb', () => {
117
128
  template: 'ts',
118
129
  dest: tmpDir
119
130
  })
120
- const pkg = JSON.parse(fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'))
131
+ const pkg = JSON.parse(fs.readFileSync(path.join(projectPath(tmpDir, 'my-custom-app'), 'package.json'), 'utf-8'))
121
132
  expect(pkg.name).to.equal('my-custom-app')
122
133
  expect(pkg.name).to.not.match(/\{\{name\}\}/)
123
134
  })
package/tsconfig.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2020",
3
+ "composite": true,
4
+ "target": "ES2022",
4
5
  "module": "ESNext",
5
- "lib": ["ES2020"],
6
- "moduleResolution": "Node",
6
+ "lib": ["ES2022"],
7
+ "moduleResolution": "Bundler",
7
8
  "strict": true,
8
9
  "esModuleInterop": true,
9
10
  "skipLibCheck": true,
10
11
  "forceConsistentCasingInFileNames": true,
11
12
  "resolveJsonModule": true,
12
13
  "declaration": true,
13
- "declarationMap": true,
14
- "sourceMap": true,
14
+ "declarationMap": false,
15
+ "sourceMap": false,
15
16
  "rootDir": "src",
16
17
  "outDir": "dist",
17
- "baseUrl": ".",
18
18
  "types": ["node"]
19
19
  },
20
20
  "include": ["src/**/*.ts"],