create-skweb 2.0.0 → 3.0.1
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/.turbo/turbo-build.log +9 -10
- package/.turbo/turbo-check.log +5 -4
- package/.turbo/turbo-lint.log +12 -4
- package/CHANGELOG.md +63 -0
- package/dist/cli.js +110 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +14 -19
- package/rollup.config.mjs +19 -19
- package/scripts/inject-shebang.mjs +39 -0
- package/scripts/sync-template-versions.mjs +201 -0
- package/src/cli.ts +7 -7
- package/src/index.ts +2 -2
- package/templates/cjs/package.json +18 -18
- package/templates/cjs/src/app.js +16 -11
- package/templates/cjs/src/cronWorker.js +7 -9
- package/templates/mjs/package.json +18 -18
- package/templates/mjs/src/app.js +8 -10
- package/templates/mjs/src/cronWorker.js +7 -9
- package/templates/ts/package.json +26 -23
- package/templates/ts/rollup.config.mjs +4 -0
- package/templates/ts/src/app.ts +9 -10
- package/templates/ts/src/cronWorker.ts +9 -11
- package/templates/ts/tsconfig.json +4 -5
- package/test/index.mjs +24 -13
- package/tsconfig.json +3 -4
- package/dist/cli.cjs +0 -261
- package/dist/cli.cjs.map +0 -1
- package/dist/index.cjs +0 -123
- package/dist/index.cjs.map +0 -1
- package/scripts/inject-shebang.cjs +0 -14
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//
|
|
3
|
+
// sync-template-versions.mjs
|
|
4
|
+
//
|
|
5
|
+
// 把本工程 packages 下的 skweb-* 包版本号
|
|
6
|
+
// 同步到 tools/create-skweb/templates 下的 package.json。
|
|
7
|
+
//
|
|
8
|
+
// 用法:
|
|
9
|
+
// node scripts/sync-template-versions.mjs # 默认 dry-run,仅打印
|
|
10
|
+
// node scripts/sync-template-versions.mjs --apply # 写回文件
|
|
11
|
+
// node scripts/sync-template-versions.mjs --strict # 严格模式:发现不一致退出码 1
|
|
12
|
+
// node scripts/sync-template-versions.mjs --help # 查看帮助
|
|
13
|
+
//
|
|
14
|
+
// 同步规则:
|
|
15
|
+
// - workspace 的 X.Y.Z 映射到模板的 ^X.Y.0
|
|
16
|
+
// - 仅修改 skweb-* 开头的依赖(其他依赖不动)
|
|
17
|
+
// - 仅处理 templates 目录下 .json 文件
|
|
18
|
+
//
|
|
19
|
+
// 退出码:
|
|
20
|
+
// 0 全部一致,或已 --apply 写回
|
|
21
|
+
// 1 --strict 模式下发现不一致且未 --apply
|
|
22
|
+
// 2 执行过程异常
|
|
23
|
+
//
|
|
24
|
+
// 建议在以下时机执行:
|
|
25
|
+
// - 每次 changeset version 之后
|
|
26
|
+
// - 提交前
|
|
27
|
+
// - CI 发布前
|
|
28
|
+
//
|
|
29
|
+
|
|
30
|
+
import fs from 'node:fs'
|
|
31
|
+
import path from 'node:path'
|
|
32
|
+
import { fileURLToPath } from 'node:url'
|
|
33
|
+
|
|
34
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
35
|
+
const ROOT = path.join(__dirname, '..', '..', '..') // -> skweb-mono/
|
|
36
|
+
const PACKAGES_DIR = path.join(ROOT, 'packages')
|
|
37
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates')
|
|
38
|
+
|
|
39
|
+
// ANSI 颜色(仅在 TTY 启用,避免污染 CI 日志)
|
|
40
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR
|
|
41
|
+
const color = (code) => (useColor ? `\x1b[${code}m` : '')
|
|
42
|
+
const cBold = color('1')
|
|
43
|
+
const cYellow = color('33')
|
|
44
|
+
const cRed = color('31')
|
|
45
|
+
const cGreen = color('32')
|
|
46
|
+
const cCyan = color('36')
|
|
47
|
+
const cReset = color('0')
|
|
48
|
+
|
|
49
|
+
function showHelp() {
|
|
50
|
+
console.log(`${cBold}sync-template-versions${cReset}
|
|
51
|
+
把 packages/ 下的 skweb-* 版本号同步到 templates/ 下的 package.json。
|
|
52
|
+
|
|
53
|
+
${cBold}用法${cReset}
|
|
54
|
+
${cCyan}node scripts/sync-template-versions.mjs${cReset} ${cYellow}# 默认 dry-run${cReset}
|
|
55
|
+
${cCyan}node scripts/sync-template-versions.mjs --apply${cReset} # 写回文件
|
|
56
|
+
${cCyan}node scripts/sync-template-versions.mjs --strict${cReset} # 严格模式(CI 推荐)
|
|
57
|
+
${cCyan}node scripts/sync-template-versions.mjs --help${cReset} # 查看帮助
|
|
58
|
+
|
|
59
|
+
${cBold}同步规则${cReset}
|
|
60
|
+
- workspace 的 X.Y.Z 映射到模板的 ^X.Y.0
|
|
61
|
+
- 仅修改 skweb-* 开头的依赖
|
|
62
|
+
- 仅处理 templates 目录下 *.json
|
|
63
|
+
|
|
64
|
+
${cBold}退出码${cReset}
|
|
65
|
+
0 全部一致,或已 --apply 写回
|
|
66
|
+
1 --strict 模式下发现不一致且未 --apply
|
|
67
|
+
2 执行过程异常
|
|
68
|
+
|
|
69
|
+
${cBold}同步规则说明${cReset}
|
|
70
|
+
使用 ${cCyan}--apply${cReset} 真正写入文件;不带 --apply 永远只打印,不会改动模板。`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const APPLY = process.argv.includes('--apply')
|
|
74
|
+
const STRICT = process.argv.includes('--strict')
|
|
75
|
+
const HELP = process.argv.includes('--help') || process.argv.includes('-h')
|
|
76
|
+
|
|
77
|
+
if (HELP) {
|
|
78
|
+
showHelp()
|
|
79
|
+
process.exit(0)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 扫描 packages/ 下所有 package.json,返回 { 包名: 版本 } 映射。
|
|
83
|
+
function loadWorkspaceVersions() {
|
|
84
|
+
const result = {}
|
|
85
|
+
if (!fs.existsSync(PACKAGES_DIR)) {
|
|
86
|
+
throw new Error(`packages dir not found: ${PACKAGES_DIR}`)
|
|
87
|
+
}
|
|
88
|
+
for (const dir of fs.readdirSync(PACKAGES_DIR)) {
|
|
89
|
+
const pkgJsonPath = path.join(PACKAGES_DIR, dir, 'package.json')
|
|
90
|
+
if (!fs.existsSync(pkgJsonPath)) continue
|
|
91
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
|
|
92
|
+
if (pkg.name && pkg.version) {
|
|
93
|
+
result[pkg.name] = pkg.version
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return result
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 把 X.Y.Z 转换为 ^X.Y.0 范围,允许 minor/patch 升级。
|
|
100
|
+
function toRange(version) {
|
|
101
|
+
const parts = version.split('.')
|
|
102
|
+
if (parts.length < 2) {
|
|
103
|
+
throw new Error(`unexpected version format: ${version}`)
|
|
104
|
+
}
|
|
105
|
+
return `^${parts[0]}.${parts[1]}.0`
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 同步单个模板文件,返回变更列表。
|
|
109
|
+
function syncTemplate(templatePath, workspaceVersions) {
|
|
110
|
+
const raw = fs.readFileSync(templatePath, 'utf-8')
|
|
111
|
+
const pkg = JSON.parse(raw)
|
|
112
|
+
const deps = pkg.dependencies || {}
|
|
113
|
+
const changes = []
|
|
114
|
+
|
|
115
|
+
for (const depName of Object.keys(deps)) {
|
|
116
|
+
if (!depName.startsWith('skweb-')) continue
|
|
117
|
+
if (!workspaceVersions[depName]) continue
|
|
118
|
+
const newRange = toRange(workspaceVersions[depName])
|
|
119
|
+
const oldRange = deps[depName]
|
|
120
|
+
if (oldRange !== newRange) {
|
|
121
|
+
changes.push({ dep: depName, old: oldRange, new: newRange })
|
|
122
|
+
deps[depName] = newRange
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (changes.length > 0 && APPLY) {
|
|
127
|
+
const updated = JSON.stringify(pkg, null, 2) + '\n'
|
|
128
|
+
fs.writeFileSync(templatePath, updated, 'utf-8')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return changes
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function main() {
|
|
135
|
+
// 在最顶部打印运行模式,避免用户漏看后续信息
|
|
136
|
+
if (APPLY) {
|
|
137
|
+
console.log(`${cGreen}[sync] mode: APPLY (will write to files)${cReset}`)
|
|
138
|
+
} else {
|
|
139
|
+
console.log(`${cYellow}[sync] mode: DRY-RUN (no files will be modified; pass --apply to write)${cReset}`)
|
|
140
|
+
}
|
|
141
|
+
if (STRICT) {
|
|
142
|
+
console.log(`${cYellow}[sync] mode: STRICT (will exit 1 on pending changes)${cReset}`)
|
|
143
|
+
}
|
|
144
|
+
console.log('')
|
|
145
|
+
|
|
146
|
+
const workspaceVersions = loadWorkspaceVersions()
|
|
147
|
+
console.log(`[sync] workspace versions: ${Object.keys(workspaceVersions).length} packages`)
|
|
148
|
+
for (const [name, ver] of Object.entries(workspaceVersions)) {
|
|
149
|
+
console.log(` ${name}: ${ver} -> ${toRange(ver)}`)
|
|
150
|
+
}
|
|
151
|
+
console.log('')
|
|
152
|
+
|
|
153
|
+
if (!fs.existsSync(TEMPLATES_DIR)) {
|
|
154
|
+
throw new Error(`templates dir not found: ${TEMPLATES_DIR}`)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let totalChanges = 0
|
|
158
|
+
const templateDirs = fs.readdirSync(TEMPLATES_DIR).filter(d => {
|
|
159
|
+
const full = path.join(TEMPLATES_DIR, d)
|
|
160
|
+
return fs.statSync(full).isDirectory()
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
for (const tpl of templateDirs) {
|
|
164
|
+
const pkgJson = path.join(TEMPLATES_DIR, tpl, 'package.json')
|
|
165
|
+
if (!fs.existsSync(pkgJson)) continue
|
|
166
|
+
const changes = syncTemplate(pkgJson, workspaceVersions)
|
|
167
|
+
if (changes.length === 0) {
|
|
168
|
+
console.log(`[sync] ${tpl}/package.json: in sync`)
|
|
169
|
+
} else {
|
|
170
|
+
totalChanges += changes.length
|
|
171
|
+
const tag = APPLY ? `${cGreen}[APPLIED]${cReset}` : `${cYellow}[dry-run]${cReset}`
|
|
172
|
+
console.log(`[sync] ${tpl}/package.json: ${changes.length} update(s) ${tag}`)
|
|
173
|
+
for (const c of changes) {
|
|
174
|
+
console.log(` ${c.dep}: ${c.old} -> ${c.new}`)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log('')
|
|
180
|
+
if (totalChanges === 0) {
|
|
181
|
+
console.log(`${cGreen}[sync] All templates are in sync with workspace versions.${cReset}`)
|
|
182
|
+
} else if (APPLY) {
|
|
183
|
+
console.log(`${cGreen}[sync] Applied ${totalChanges} update(s).${cReset}`)
|
|
184
|
+
} else {
|
|
185
|
+
console.log(
|
|
186
|
+
`${cYellow}[sync] ${totalChanges} update(s) pending.${cReset} ` +
|
|
187
|
+
`${cBold}Re-run with ${cCyan}--apply${cReset}${cBold} to write.${cReset}`
|
|
188
|
+
)
|
|
189
|
+
if (STRICT) {
|
|
190
|
+
console.log(`${cRed}[sync] STRICT mode: exiting with code 1.${cReset}`)
|
|
191
|
+
process.exit(1)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
main()
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.error('[sync] FAILED:', err.message)
|
|
200
|
+
process.exit(2)
|
|
201
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
import { Command } from 'commander'
|
|
4
4
|
import inquirer from 'inquirer'
|
|
5
5
|
import ora from 'ora'
|
|
6
|
-
import {
|
|
7
|
-
createProject,
|
|
8
|
-
installDependencies,
|
|
9
|
-
initGit,
|
|
10
|
-
printBanner,
|
|
11
|
-
printSuccess
|
|
12
|
-
} from './index'
|
|
6
|
+
import {
|
|
7
|
+
createProject,
|
|
8
|
+
installDependencies,
|
|
9
|
+
initGit,
|
|
10
|
+
printBanner,
|
|
11
|
+
printSuccess
|
|
12
|
+
} from './index.js'
|
|
13
13
|
|
|
14
14
|
async function main() {
|
|
15
15
|
const program = new Command()
|
package/src/index.ts
CHANGED
|
@@ -82,7 +82,7 @@ export async function initGit(dest: string, name?: string): Promise<void> {
|
|
|
82
82
|
export function printBanner(): void {
|
|
83
83
|
console.log(`
|
|
84
84
|
╔════════════════════════════════════════════════════════════════════╗
|
|
85
|
-
║
|
|
85
|
+
║ Create SKWeb Project ║
|
|
86
86
|
╚════════════════════════════════════════════════════════════════════╝
|
|
87
87
|
`)
|
|
88
88
|
}
|
|
@@ -91,7 +91,7 @@ export function printSuccess(name: string, dest: string): void {
|
|
|
91
91
|
const projectPath = path.join(path.resolve(dest), name)
|
|
92
92
|
console.log(`
|
|
93
93
|
╔════════════════════════════════════════════════════════════════════╗
|
|
94
|
-
║
|
|
94
|
+
║ 项目创建成功! ║
|
|
95
95
|
╚════════════════════════════════════════════════════════════════════╝
|
|
96
96
|
|
|
97
97
|
项目名称: ${name}
|
|
@@ -20,27 +20,27 @@
|
|
|
20
20
|
"clean:temp": "rm -rf .rollup.cache"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@dotenvx/dotenvx": "^1.
|
|
24
|
-
"http-errors": "^2.0.
|
|
25
|
-
"skweb-express": "^
|
|
26
|
-
"skweb-express-jwt": "^
|
|
27
|
-
"skweb-express-logger": "^
|
|
28
|
-
"skweb-sequelize": "^
|
|
29
|
-
"skweb-redis": "^
|
|
30
|
-
"skweb-cron": "^
|
|
31
|
-
"skweb-framework": "^
|
|
32
|
-
"skweb-plugin": "^
|
|
33
|
-
"skweb-util": "^
|
|
34
|
-
"winston": "^3.
|
|
35
|
-
"sequelize": "^6.37.
|
|
36
|
-
"swagger-jsdoc": "^6.
|
|
23
|
+
"@dotenvx/dotenvx": "^1.75.1",
|
|
24
|
+
"http-errors": "^2.0.1",
|
|
25
|
+
"skweb-express": "^2.0.0",
|
|
26
|
+
"skweb-express-jwt": "^3.0.0",
|
|
27
|
+
"skweb-express-logger": "^2.0.0",
|
|
28
|
+
"skweb-sequelize": "^2.0.0",
|
|
29
|
+
"skweb-redis": "^2.0.0",
|
|
30
|
+
"skweb-cron": "^2.0.0",
|
|
31
|
+
"skweb-framework": "^2.0.0",
|
|
32
|
+
"skweb-plugin": "^2.0.0",
|
|
33
|
+
"skweb-util": "^2.0.0",
|
|
34
|
+
"winston": "^3.19.0",
|
|
35
|
+
"sequelize": "^6.37.8",
|
|
36
|
+
"swagger-jsdoc": "^6.3.0",
|
|
37
37
|
"swagger-ui-express": "^5.0.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"chai": "^6.2.
|
|
41
|
-
"mocha": "^11.7.
|
|
42
|
-
"eslint": "^
|
|
43
|
-
"@eslint/js": "^
|
|
40
|
+
"chai": "^6.2.2",
|
|
41
|
+
"mocha": "^11.7.6",
|
|
42
|
+
"eslint": "^10.5.0",
|
|
43
|
+
"@eslint/js": "^10.0.1"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=20.18.1"
|
package/templates/cjs/src/app.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
require('dotenv/config')
|
|
2
2
|
|
|
3
3
|
const path = require('path')
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// skweb-express / skweb-express-logger 的 CJS 产物形态是
|
|
5
|
+
// `{ default: fn, errorHandler, ... }`。直接 `require('skweb-express')` 拿到的是
|
|
6
|
+
// 命名空间对象,`express()` 会 TypeError。这里走 .default。
|
|
7
|
+
const express = require('skweb-express').default
|
|
8
|
+
const expressLogger = require('skweb-express-logger').default
|
|
9
|
+
const expressErrorHandler = require('skweb-express-logger').errorHandler
|
|
6
10
|
const swaggerUi = require('swagger-ui-express')
|
|
7
11
|
const { WebFramework } = require('skweb-framework')
|
|
8
12
|
const { logger } = require('./utils/logger.js')
|
|
@@ -126,20 +130,21 @@ async function bootstrap() {
|
|
|
126
130
|
await framework.loadControllers(path.join(__dirname, './controllers'))
|
|
127
131
|
await framework.loadPluginControllers()
|
|
128
132
|
|
|
133
|
+
// 错误日志中间件:挂在所有路由之后,记录业务错误并继续传递给默认错误处理。
|
|
134
|
+
app.use(expressErrorHandler())
|
|
135
|
+
|
|
129
136
|
framework.start(Number(process.env.PORT) || 3000)
|
|
130
137
|
|
|
131
138
|
logger.info('[app] Initialization complete (web process)')
|
|
132
139
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
process.exit(
|
|
140
|
+
// 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
|
|
141
|
+
process.on('uncaughtException', (err) => {
|
|
142
|
+
logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
|
|
143
|
+
process.exit(1)
|
|
137
144
|
})
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
await framework.stop()
|
|
142
|
-
process.exit(0)
|
|
145
|
+
process.on('unhandledRejection', (reason) => {
|
|
146
|
+
logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
147
|
+
process.exit(1)
|
|
143
148
|
})
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -62,16 +62,14 @@ async function startCronWorker() {
|
|
|
62
62
|
framework.start()
|
|
63
63
|
logger.info('[cron-worker] Cron worker started')
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
process.exit(
|
|
65
|
+
// 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
|
|
66
|
+
process.on('uncaughtException', (err) => {
|
|
67
|
+
logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
|
|
68
|
+
process.exit(1)
|
|
69
69
|
})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
await framework.stop()
|
|
74
|
-
process.exit(0)
|
|
70
|
+
process.on('unhandledRejection', (reason) => {
|
|
71
|
+
logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
72
|
+
process.exit(1)
|
|
75
73
|
})
|
|
76
74
|
}
|
|
77
75
|
|
|
@@ -20,27 +20,27 @@
|
|
|
20
20
|
"clean:temp": "rm -rf .rollup.cache"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@dotenvx/dotenvx": "^1.
|
|
24
|
-
"http-errors": "^2.0.
|
|
25
|
-
"skweb-express": "^
|
|
26
|
-
"skweb-express-jwt": "^
|
|
27
|
-
"skweb-express-logger": "^
|
|
28
|
-
"skweb-sequelize": "^
|
|
29
|
-
"skweb-redis": "^
|
|
30
|
-
"skweb-cron": "^
|
|
31
|
-
"skweb-framework": "^
|
|
32
|
-
"skweb-plugin": "^
|
|
33
|
-
"skweb-util": "^
|
|
34
|
-
"winston": "^3.
|
|
35
|
-
"sequelize": "^6.37.
|
|
36
|
-
"swagger-jsdoc": "^6.
|
|
23
|
+
"@dotenvx/dotenvx": "^1.75.1",
|
|
24
|
+
"http-errors": "^2.0.1",
|
|
25
|
+
"skweb-express": "^2.0.0",
|
|
26
|
+
"skweb-express-jwt": "^3.0.0",
|
|
27
|
+
"skweb-express-logger": "^2.0.0",
|
|
28
|
+
"skweb-sequelize": "^2.0.0",
|
|
29
|
+
"skweb-redis": "^2.0.0",
|
|
30
|
+
"skweb-cron": "^2.0.0",
|
|
31
|
+
"skweb-framework": "^2.0.0",
|
|
32
|
+
"skweb-plugin": "^2.0.0",
|
|
33
|
+
"skweb-util": "^2.0.0",
|
|
34
|
+
"winston": "^3.19.0",
|
|
35
|
+
"sequelize": "^6.37.8",
|
|
36
|
+
"swagger-jsdoc": "^6.3.0",
|
|
37
37
|
"swagger-ui-express": "^5.0.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"chai": "^6.2.
|
|
41
|
-
"mocha": "^11.7.
|
|
42
|
-
"eslint": "^
|
|
43
|
-
"@eslint/js": "^
|
|
40
|
+
"chai": "^6.2.2",
|
|
41
|
+
"mocha": "^11.7.6",
|
|
42
|
+
"eslint": "^10.5.0",
|
|
43
|
+
"@eslint/js": "^10.0.1"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=20.18.1"
|
package/templates/mjs/src/app.js
CHANGED
|
@@ -3,7 +3,7 @@ import 'dotenv/config'
|
|
|
3
3
|
import path from 'path'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
import express from 'skweb-express'
|
|
6
|
-
import expressLogger from 'skweb-express-logger'
|
|
6
|
+
import expressLogger, { errorHandler as expressErrorHandler } from 'skweb-express-logger'
|
|
7
7
|
import swaggerUi from 'swagger-ui-express'
|
|
8
8
|
import logger from './utils/logger.js'
|
|
9
9
|
import { checkRequiredEnv } from './utils/config.js'
|
|
@@ -135,16 +135,14 @@ async function bootstrap() {
|
|
|
135
135
|
|
|
136
136
|
logger.info('[app] Initialization complete (web process)')
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
process.exit(
|
|
138
|
+
// 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
|
|
139
|
+
process.on('uncaughtException', (err) => {
|
|
140
|
+
logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
|
|
141
|
+
process.exit(1)
|
|
142
142
|
})
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
await framework.stop()
|
|
147
|
-
process.exit(0)
|
|
143
|
+
process.on('unhandledRejection', (reason) => {
|
|
144
|
+
logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
145
|
+
process.exit(1)
|
|
148
146
|
})
|
|
149
147
|
}
|
|
150
148
|
|
|
@@ -67,16 +67,14 @@ async function startCronWorker() {
|
|
|
67
67
|
framework.start()
|
|
68
68
|
logger.info('[cron-worker] Cron worker started')
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
process.exit(
|
|
70
|
+
// 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
|
|
71
|
+
process.on('uncaughtException', (err) => {
|
|
72
|
+
logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
|
|
73
|
+
process.exit(1)
|
|
74
74
|
})
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await framework.stop()
|
|
79
|
-
process.exit(0)
|
|
75
|
+
process.on('unhandledRejection', (reason) => {
|
|
76
|
+
logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
77
|
+
process.exit(1)
|
|
80
78
|
})
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -22,38 +22,41 @@
|
|
|
22
22
|
"clean:temp": "rm -rf .rollup.cache"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@dotenvx/dotenvx": "^1.
|
|
26
|
-
"http-errors": "^2.0.
|
|
27
|
-
"skweb-express": "^
|
|
28
|
-
"skweb-express-jwt": "^
|
|
29
|
-
"skweb-express-logger": "^
|
|
30
|
-
"skweb-sequelize": "^
|
|
31
|
-
"skweb-redis": "^
|
|
32
|
-
"skweb-cron": "^
|
|
33
|
-
"skweb-framework": "^
|
|
34
|
-
"skweb-plugin": "^
|
|
35
|
-
"skweb-util": "^
|
|
36
|
-
"winston": "^3.
|
|
37
|
-
"sequelize": "^6.37.
|
|
38
|
-
"swagger-jsdoc": "^6.
|
|
25
|
+
"@dotenvx/dotenvx": "^1.75.1",
|
|
26
|
+
"http-errors": "^2.0.1",
|
|
27
|
+
"skweb-express": "^2.0.0",
|
|
28
|
+
"skweb-express-jwt": "^3.0.0",
|
|
29
|
+
"skweb-express-logger": "^2.0.0",
|
|
30
|
+
"skweb-sequelize": "^2.0.0",
|
|
31
|
+
"skweb-redis": "^2.0.0",
|
|
32
|
+
"skweb-cron": "^2.0.0",
|
|
33
|
+
"skweb-framework": "^2.0.0",
|
|
34
|
+
"skweb-plugin": "^2.0.0",
|
|
35
|
+
"skweb-util": "^2.0.0",
|
|
36
|
+
"winston": "^3.19.0",
|
|
37
|
+
"sequelize": "^6.37.8",
|
|
38
|
+
"swagger-jsdoc": "^6.3.0",
|
|
39
39
|
"swagger-ui-express": "^5.0.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@types/node": "^
|
|
42
|
+
"@types/node": "^26.0.0",
|
|
43
43
|
"@types/mocha": "^10.0.10",
|
|
44
|
-
"@types/http-errors": "^2.0.
|
|
44
|
+
"@types/http-errors": "^2.0.5",
|
|
45
45
|
"@types/swagger-jsdoc": "^6.0.4",
|
|
46
46
|
"@types/swagger-ui-express": "^4.1.8",
|
|
47
|
-
"chai": "^6.2.
|
|
48
|
-
"mocha": "^11.7.
|
|
47
|
+
"chai": "^6.2.2",
|
|
48
|
+
"mocha": "^11.7.6",
|
|
49
49
|
"tsx": "^4.22.4",
|
|
50
|
-
"typescript": "^
|
|
51
|
-
"
|
|
50
|
+
"typescript": "^6.0.3",
|
|
51
|
+
"tslib": "^2.8.1",
|
|
52
|
+
"rollup": "^4.62.2",
|
|
52
53
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
54
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
53
55
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
54
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
55
|
-
"
|
|
56
|
-
"
|
|
56
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
57
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
58
|
+
"eslint": "^10.5.0",
|
|
59
|
+
"typescript-eslint": "^8.62.0"
|
|
57
60
|
},
|
|
58
61
|
"engines": {
|
|
59
62
|
"node": ">=20.18.1"
|
|
@@ -2,6 +2,7 @@ import resolve from '@rollup/plugin-node-resolve'
|
|
|
2
2
|
import commonjs from '@rollup/plugin-commonjs'
|
|
3
3
|
import typescript from '@rollup/plugin-typescript'
|
|
4
4
|
import terser from '@rollup/plugin-terser'
|
|
5
|
+
import json from '@rollup/plugin-json'
|
|
5
6
|
|
|
6
7
|
const isProduction = process.env.NODE_ENV === 'production'
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ export default {
|
|
|
15
16
|
plugins: [
|
|
16
17
|
resolve(),
|
|
17
18
|
commonjs(),
|
|
19
|
+
json(),
|
|
18
20
|
typescript({ tsconfig: './tsconfig.json' }),
|
|
19
21
|
isProduction && terser()
|
|
20
22
|
],
|
|
@@ -33,6 +35,8 @@ export default {
|
|
|
33
35
|
'express',
|
|
34
36
|
'sequelize',
|
|
35
37
|
'ioredis',
|
|
38
|
+
'swagger-ui-express',
|
|
39
|
+
'swagger-jsdoc',
|
|
36
40
|
'path',
|
|
37
41
|
'fs'
|
|
38
42
|
]
|
package/templates/ts/src/app.ts
CHANGED
|
@@ -3,7 +3,7 @@ import 'dotenv/config'
|
|
|
3
3
|
import path from 'path'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
import express from 'skweb-express'
|
|
6
|
-
import expressLogger from 'skweb-express-logger'
|
|
6
|
+
import expressLogger, { errorHandler as expressErrorHandler } from 'skweb-express-logger'
|
|
7
7
|
import { WebFramework } from 'skweb-framework'
|
|
8
8
|
import type { JWTOptions } from 'skweb-express-jwt'
|
|
9
9
|
import swaggerUi from 'swagger-ui-express'
|
|
@@ -142,16 +142,15 @@ async function bootstrap(): Promise<void> {
|
|
|
142
142
|
|
|
143
143
|
logger.info('[app] Initialization complete (web process)')
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
// 未捕获异常:打印堆栈后直接退出。OS / 进程管理器(pm2 / docker / k8s)
|
|
146
|
+
// 负责回收 socket / fd,框架不在此路径上做资源清理——简单可靠。
|
|
147
|
+
process.on('uncaughtException', (err) => {
|
|
148
|
+
logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
|
|
149
|
+
process.exit(1)
|
|
149
150
|
})
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
await framework.stop()
|
|
154
|
-
process.exit(0)
|
|
151
|
+
process.on('unhandledRejection', (reason) => {
|
|
152
|
+
logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
153
|
+
process.exit(1)
|
|
155
154
|
})
|
|
156
155
|
}
|
|
157
156
|
|
|
@@ -37,7 +37,7 @@ async function startCronWorker(): Promise<void> {
|
|
|
37
37
|
channel: 'cron:task:cmd',
|
|
38
38
|
logger,
|
|
39
39
|
onSaveTask: async (taskData: { taskId: string; cache: unknown; data: unknown }) => {
|
|
40
|
-
const SysCronTask = framework.
|
|
40
|
+
const SysCronTask = framework.dbInstance?.sequelize?.models.SysCronTask as any
|
|
41
41
|
if (SysCronTask) {
|
|
42
42
|
await SysCronTask.update(
|
|
43
43
|
{ cache: taskData.cache, data: taskData.data },
|
|
@@ -46,7 +46,7 @@ async function startCronWorker(): Promise<void> {
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
loadTasks: async () => {
|
|
49
|
-
const SysCronTask = framework.
|
|
49
|
+
const SysCronTask = framework.dbInstance?.sequelize?.models.SysCronTask as any
|
|
50
50
|
if (!SysCronTask) return []
|
|
51
51
|
return (await SysCronTask.findAll()).map((t: any) => ({
|
|
52
52
|
id: String(t.id),
|
|
@@ -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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
process.exit(
|
|
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
|
-
|
|
76
|
-
|
|
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": "
|
|
3
|
+
"target": "ES2022",
|
|
4
4
|
"module": "ESNext",
|
|
5
|
-
"lib": ["
|
|
6
|
-
"moduleResolution": "
|
|
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"]
|