create-skweb 2.1.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 +36 -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 +10 -18
- package/rollup.config.mjs +19 -19
- package/scripts/inject-shebang.mjs +1 -1
- 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 +24 -24
- package/templates/ts/rollup.config.mjs +2 -0
- package/templates/ts/src/app.ts +9 -10
- package/templates/ts/src/cronWorker.ts +7 -9
- 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
|
@@ -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,41 +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": "^
|
|
50
|
+
"typescript": "^6.0.3",
|
|
51
51
|
"tslib": "^2.8.1",
|
|
52
|
-
"rollup": "^
|
|
52
|
+
"rollup": "^4.62.2",
|
|
53
53
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
54
54
|
"@rollup/plugin-json": "^6.1.0",
|
|
55
55
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
56
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
57
|
-
"@rollup/plugin-terser": "^0.
|
|
58
|
-
"eslint": "^
|
|
59
|
-
"typescript-eslint": "^8.
|
|
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"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
62
|
"node": ">=20.18.1"
|
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
|
|
|
@@ -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"]
|
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(
|
|
31
|
-
expect(fs.existsSync(path.join(
|
|
32
|
-
expect(fs.existsSync(path.join(
|
|
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(
|
|
43
|
-
expect(fs.existsSync(path.join(
|
|
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(
|
|
54
|
-
expect(fs.existsSync(path.join(
|
|
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
|
-
//
|
|
59
|
-
|
|
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,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,
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
"sourceMap": true,
|
|
15
15
|
"rootDir": "src",
|
|
16
16
|
"outDir": "dist",
|
|
17
|
-
"baseUrl": ".",
|
|
18
17
|
"types": ["node"]
|
|
19
18
|
},
|
|
20
19
|
"include": ["src/**/*.ts"],
|
package/dist/cli.cjs
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
var commander = require('commander');
|
|
6
|
-
var inquirer = require('inquirer');
|
|
7
|
-
var ora = require('ora');
|
|
8
|
-
var fs = require('fs-extra');
|
|
9
|
-
var path = require('path');
|
|
10
|
-
var url = require('url');
|
|
11
|
-
var child_process = require('child_process');
|
|
12
|
-
|
|
13
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
14
|
-
|
|
15
|
-
var inquirer__default = /*#__PURE__*/_interopDefaultLegacy(inquirer);
|
|
16
|
-
var ora__default = /*#__PURE__*/_interopDefaultLegacy(ora);
|
|
17
|
-
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
18
|
-
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
19
|
-
|
|
20
|
-
const __dirname$1 = path__default["default"].dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('cli.cjs', document.baseURI).href))));
|
|
21
|
-
async function createProject(options) {
|
|
22
|
-
const { name, description = 'A SKWeb project', template, dest } = options;
|
|
23
|
-
const auth = options.auth ?? 'static';
|
|
24
|
-
const templateDir = path__default["default"].join(__dirname$1, '..', 'templates', template);
|
|
25
|
-
const baseDir = path__default["default"].resolve(dest);
|
|
26
|
-
const targetDir = path__default["default"].join(baseDir, name);
|
|
27
|
-
if (await fs__default["default"].pathExists(targetDir)) {
|
|
28
|
-
if ((await fs__default["default"].readdir(targetDir)).length > 0) {
|
|
29
|
-
throw new Error(`目标目录 ${targetDir} 不为空`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
await fs__default["default"].mkdirs(targetDir);
|
|
34
|
-
}
|
|
35
|
-
const files = await fs__default["default"].readdir(templateDir, { recursive: true });
|
|
36
|
-
for (const file of files) {
|
|
37
|
-
const fileName = String(file);
|
|
38
|
-
const srcPath = path__default["default"].join(templateDir, fileName);
|
|
39
|
-
const stats = await fs__default["default"].stat(srcPath);
|
|
40
|
-
if (stats.isDirectory()) {
|
|
41
|
-
await fs__default["default"].mkdirs(path__default["default"].join(targetDir, fileName));
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
let content = await fs__default["default"].readFile(srcPath, 'utf-8');
|
|
45
|
-
content = content
|
|
46
|
-
.replace(/\{\{name\}\}/g, name)
|
|
47
|
-
.replace(/\{\{description\}\}/g, description)
|
|
48
|
-
.replace(/\{\{auth\}\}/g, auth);
|
|
49
|
-
const destPath = path__default["default"].join(targetDir, fileName);
|
|
50
|
-
await fs__default["default"].writeFile(destPath, content);
|
|
51
|
-
}
|
|
52
|
-
await fs__default["default"].mkdirs(path__default["default"].join(targetDir, 'logs'));
|
|
53
|
-
}
|
|
54
|
-
async function installDependencies(dest, name) {
|
|
55
|
-
const targetDir = name ? path__default["default"].join(path__default["default"].resolve(dest), name) : path__default["default"].resolve(dest);
|
|
56
|
-
child_process.execSync('npm install', {
|
|
57
|
-
cwd: targetDir,
|
|
58
|
-
stdio: 'inherit'
|
|
59
|
-
});
|
|
60
|
-
child_process.execSync('npm install pm2 -g', {
|
|
61
|
-
cwd: targetDir,
|
|
62
|
-
stdio: 'inherit'
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
async function initGit(dest, name) {
|
|
66
|
-
const targetDir = name ? path__default["default"].join(path__default["default"].resolve(dest), name) : path__default["default"].resolve(dest);
|
|
67
|
-
child_process.execSync('git init', {
|
|
68
|
-
cwd: targetDir,
|
|
69
|
-
stdio: 'inherit'
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
function printBanner() {
|
|
73
|
-
console.log(`
|
|
74
|
-
╔════════════════════════════════════════════════════════════════════╗
|
|
75
|
-
║ Create SKWeb Project ║
|
|
76
|
-
╚════════════════════════════════════════════════════════════════════╝
|
|
77
|
-
`);
|
|
78
|
-
}
|
|
79
|
-
function printSuccess(name, dest) {
|
|
80
|
-
const projectPath = path__default["default"].join(path__default["default"].resolve(dest), name);
|
|
81
|
-
console.log(`
|
|
82
|
-
╔════════════════════════════════════════════════════════════════════╗
|
|
83
|
-
║ 项目创建成功! ║
|
|
84
|
-
╚════════════════════════════════════════════════════════════════════╝
|
|
85
|
-
|
|
86
|
-
项目名称: ${name}
|
|
87
|
-
项目路径: ${projectPath}
|
|
88
|
-
|
|
89
|
-
接下来可以执行:
|
|
90
|
-
|
|
91
|
-
cd ${name}
|
|
92
|
-
|
|
93
|
-
# 复制环境变量模板并按需修改
|
|
94
|
-
cp .env.example .env
|
|
95
|
-
|
|
96
|
-
# 开发模式运行
|
|
97
|
-
npm run start:dev
|
|
98
|
-
|
|
99
|
-
# 生产模式运行 (需要先构建)
|
|
100
|
-
npm run build
|
|
101
|
-
npm run start
|
|
102
|
-
|
|
103
|
-
# 查看日志
|
|
104
|
-
npm run logs
|
|
105
|
-
|
|
106
|
-
# 停止服务
|
|
107
|
-
npm run stop
|
|
108
|
-
|
|
109
|
-
# 重启服务
|
|
110
|
-
npm run restart
|
|
111
|
-
|
|
112
|
-
项目特性:
|
|
113
|
-
✅ TypeScript/MJS/CJS 支持
|
|
114
|
-
✅ @dotenvx/dotenvx 环境变量管理
|
|
115
|
-
✅ PM2 进程管理
|
|
116
|
-
✅ SKWeb Express 集成
|
|
117
|
-
✅ Keycloak/JWKS 鉴权 (可选)
|
|
118
|
-
✅ ESLint 代码检查
|
|
119
|
-
✅ Mocha 测试框架
|
|
120
|
-
`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function main() {
|
|
124
|
-
const program = new commander.Command();
|
|
125
|
-
program
|
|
126
|
-
.name('create-skweb')
|
|
127
|
-
.description('Create a new SKWeb project with TypeScript, CJS, or MJS support')
|
|
128
|
-
.argument('[name]', 'Project name')
|
|
129
|
-
.option('--template <type>', 'Template type: ts, mjs, cjs', 'ts')
|
|
130
|
-
.option('--dest <path>', 'Destination directory', '.')
|
|
131
|
-
.option('--auth <type>', 'Auth provider: static (HS256) or jwks (Keycloak/Auth0)', 'static')
|
|
132
|
-
.option('--skip-install', 'Skip dependency installation')
|
|
133
|
-
.option('--skip-git', 'Skip git initialization')
|
|
134
|
-
.action(async (name, options) => {
|
|
135
|
-
printBanner();
|
|
136
|
-
// 只要没有传 --no-interactive 且任何一个关键参数缺失,就走交互式 prompt
|
|
137
|
-
const providedTemplate = process.argv.includes('--template');
|
|
138
|
-
const providedDest = process.argv.includes('--dest');
|
|
139
|
-
const providedAuth = process.argv.includes('--auth');
|
|
140
|
-
const providedSkipInstall = process.argv.includes('--skip-install');
|
|
141
|
-
const providedSkipGit = process.argv.includes('--skip-git');
|
|
142
|
-
// 如果有任何一个关键参数未通过命令行提供,就走交互式 prompt
|
|
143
|
-
const needsPrompt = !name
|
|
144
|
-
|| !providedTemplate
|
|
145
|
-
|| !providedDest
|
|
146
|
-
|| !providedAuth
|
|
147
|
-
|| !providedSkipInstall
|
|
148
|
-
|| !providedSkipGit;
|
|
149
|
-
if (needsPrompt) {
|
|
150
|
-
const questions = [];
|
|
151
|
-
if (!name) {
|
|
152
|
-
questions.push({
|
|
153
|
-
type: 'input',
|
|
154
|
-
name: 'name',
|
|
155
|
-
message: 'Enter project name:',
|
|
156
|
-
default: 'my-skweb-app',
|
|
157
|
-
validate: (input) => {
|
|
158
|
-
if (!input || input.trim() === '') {
|
|
159
|
-
return 'Please enter a project name';
|
|
160
|
-
}
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
if (!providedTemplate) {
|
|
166
|
-
questions.push({
|
|
167
|
-
type: 'list',
|
|
168
|
-
name: 'template',
|
|
169
|
-
message: 'Select project template:',
|
|
170
|
-
choices: [
|
|
171
|
-
{ name: 'TypeScript', value: 'ts' },
|
|
172
|
-
{ name: 'ES Modules (MJS)', value: 'mjs' },
|
|
173
|
-
{ name: 'CommonJS (CJS)', value: 'cjs' }
|
|
174
|
-
],
|
|
175
|
-
default: 'ts'
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
if (!providedAuth) {
|
|
179
|
-
questions.push({
|
|
180
|
-
type: 'list',
|
|
181
|
-
name: 'auth',
|
|
182
|
-
message: 'Select auth provider:',
|
|
183
|
-
choices: [
|
|
184
|
-
{ name: 'Static (HS256 shared secret)', value: 'static' },
|
|
185
|
-
{ name: 'JWKS (Keycloak / Auth0 / OIDC)', value: 'jwks' }
|
|
186
|
-
],
|
|
187
|
-
default: 'static'
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
if (!providedDest) {
|
|
191
|
-
questions.push({
|
|
192
|
-
type: 'input',
|
|
193
|
-
name: 'dest',
|
|
194
|
-
message: 'Enter destination directory:',
|
|
195
|
-
default: '.'
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
if (!providedSkipInstall) {
|
|
199
|
-
questions.push({
|
|
200
|
-
type: 'confirm',
|
|
201
|
-
name: 'install',
|
|
202
|
-
message: 'Install dependencies?',
|
|
203
|
-
default: true
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
if (!providedSkipGit) {
|
|
207
|
-
questions.push({
|
|
208
|
-
type: 'confirm',
|
|
209
|
-
name: 'git',
|
|
210
|
-
message: 'Initialize git repository?',
|
|
211
|
-
default: true
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
const answers = await inquirer__default["default"].prompt(questions);
|
|
215
|
-
if (answers.name)
|
|
216
|
-
name = answers.name;
|
|
217
|
-
if (answers.template)
|
|
218
|
-
options.template = answers.template;
|
|
219
|
-
if (answers.auth)
|
|
220
|
-
options.auth = answers.auth;
|
|
221
|
-
if (answers.dest)
|
|
222
|
-
options.dest = answers.dest;
|
|
223
|
-
if (typeof answers.install === 'boolean')
|
|
224
|
-
options.skipInstall = !answers.install;
|
|
225
|
-
if (typeof answers.git === 'boolean')
|
|
226
|
-
options.skipGit = !answers.git;
|
|
227
|
-
}
|
|
228
|
-
const spinner = ora__default["default"]('Creating project...').start();
|
|
229
|
-
try {
|
|
230
|
-
await createProject({
|
|
231
|
-
name,
|
|
232
|
-
template: options.template,
|
|
233
|
-
dest: options.dest,
|
|
234
|
-
auth: (options.auth || 'static')
|
|
235
|
-
});
|
|
236
|
-
spinner.succeed('Project files created');
|
|
237
|
-
if (!options.skipInstall) {
|
|
238
|
-
spinner.text = 'Installing dependencies...';
|
|
239
|
-
spinner.start();
|
|
240
|
-
await installDependencies(options.dest, name);
|
|
241
|
-
spinner.succeed('Dependencies installed');
|
|
242
|
-
}
|
|
243
|
-
if (!options.skipGit) {
|
|
244
|
-
spinner.text = 'Initializing git repository...';
|
|
245
|
-
spinner.start();
|
|
246
|
-
await initGit(options.dest, name);
|
|
247
|
-
spinner.succeed('Git repository initialized');
|
|
248
|
-
}
|
|
249
|
-
spinner.stop();
|
|
250
|
-
printSuccess(name, options.dest);
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
spinner.fail('Failed to create project');
|
|
254
|
-
console.error(error);
|
|
255
|
-
process.exit(1);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
await program.parseAsync();
|
|
259
|
-
}
|
|
260
|
-
main().catch(console.error);
|
|
261
|
-
//# sourceMappingURL=cli.cjs.map
|