softleader-nuxt-core 2.0.0 → 2.2.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/bin/init.mjs +266 -266
- package/components/uiInterface/IButton.vue +466 -466
- package/composables/useModules.ts +18 -18
- package/package.json +2 -1
- package/repositories/index.ts +18 -0
- package/repositories/modules/auth.ts +29 -0
- package/repositories/modules/user.ts +100 -0
- package/scripts/release.mjs +24 -24
package/bin/init.mjs
CHANGED
|
@@ -1,266 +1,266 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* softleader-nuxt-core CLI
|
|
5
|
-
* 支援指令:
|
|
6
|
-
* init <project-name> : 建立全新專案
|
|
7
|
-
* sync [path] : 同步現有專案架構與配置
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import fs from 'node:fs'
|
|
11
|
-
import path from 'node:path'
|
|
12
|
-
import { fileURLToPath } from 'node:url'
|
|
13
|
-
import { execSync } from 'node:child_process'
|
|
14
|
-
import { parseArgs } from 'node:util'
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
17
|
-
const __dirname = path.dirname(__filename)
|
|
18
|
-
|
|
19
|
-
// 解析參數
|
|
20
|
-
const options = {
|
|
21
|
-
help: { type: 'boolean', short: 'h' }
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const { values, positionals } = parseArgs({ options, allowPositionals: true })
|
|
25
|
-
|
|
26
|
-
if (values.help || positionals.length === 0) {
|
|
27
|
-
console.log(`
|
|
28
|
-
用法 (Usage):
|
|
29
|
-
npx softleader-nuxt-core init [project-name] 建立全新專案
|
|
30
|
-
npx softleader-nuxt-core sync [path] 同步現有專案架構 (不覆寫業務代碼)
|
|
31
|
-
|
|
32
|
-
參數 (Arguments):
|
|
33
|
-
[project-name] / [path] 目標資料夾路徑 (預設: 當前目錄)
|
|
34
|
-
|
|
35
|
-
選項 (Options):
|
|
36
|
-
-h, --help 顯示指令幫助訊息
|
|
37
|
-
`)
|
|
38
|
-
process.exit(0)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const command = positionals[0]
|
|
42
|
-
const isInit = command === 'init'
|
|
43
|
-
const isSync = command === 'sync'
|
|
44
|
-
|
|
45
|
-
if (!isInit && !isSync) {
|
|
46
|
-
console.error(`錯誤: 不支援的指令 "${command}"。請使用 init 或 sync。`)
|
|
47
|
-
process.exit(1)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const targetPath = positionals[1] || (isInit ? 'my-nuxt-app' : '.')
|
|
51
|
-
const targetDir = path.resolve(process.cwd(), targetPath)
|
|
52
|
-
const projectName = path.basename(targetDir)
|
|
53
|
-
const packageName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '') || 'my-nuxt-app'
|
|
54
|
-
|
|
55
|
-
// --- Helpers ---
|
|
56
|
-
const log = {
|
|
57
|
-
info: (msg) => console.log(`\x1b[36m${msg}\x1b[0m`),
|
|
58
|
-
success: (msg) => console.log(`\x1b[32m${msg}\x1b[0m`),
|
|
59
|
-
warn: (msg) => console.log(`\x1b[33m${msg}\x1b[0m`),
|
|
60
|
-
error: (msg) => console.error(`\x1b[31m${msg}\x1b[0m`),
|
|
61
|
-
step: (msg) => console.log(`\n\x1b[35m[${msg}]\x1b[0m`)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const ensureDir = (dirPath) => {
|
|
65
|
-
const fullPath = path.join(targetDir, dirPath)
|
|
66
|
-
if (!fs.existsSync(fullPath)) {
|
|
67
|
-
fs.mkdirSync(fullPath, { recursive: true })
|
|
68
|
-
log.info(` [建立] 目錄: ${dirPath}`)
|
|
69
|
-
} else {
|
|
70
|
-
log.info(` [略過] 目錄已存在: ${dirPath}`)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const writeFile = (filePath, content, overwrite = true) => {
|
|
75
|
-
const fullPath = path.join(targetDir, filePath)
|
|
76
|
-
const dir = path.dirname(fullPath)
|
|
77
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
78
|
-
|
|
79
|
-
if (fs.existsSync(fullPath) && !overwrite) {
|
|
80
|
-
log.info(` [略過] 檔案已存在: ${filePath}`)
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
fs.writeFileSync(fullPath, content)
|
|
85
|
-
log.info(` [產生] 檔案: ${filePath}`)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// --- Main Execution ---
|
|
89
|
-
|
|
90
|
-
if (isInit) {
|
|
91
|
-
if (fs.existsSync(targetDir) && fs.existsSync(path.join(targetDir, 'package.json'))) {
|
|
92
|
-
log.error(`錯誤: 目錄 "${targetPath}" 已經存在且包含 package.json。初始化失敗。`)
|
|
93
|
-
process.exit(1)
|
|
94
|
-
}
|
|
95
|
-
log.step('Initializing New Project...')
|
|
96
|
-
} else {
|
|
97
|
-
log.step('Syncing Project Architecture...')
|
|
98
|
-
if (!fs.existsSync(path.join(targetDir, 'package.json'))) {
|
|
99
|
-
log.error('錯誤: 找不到 package.json。請在專案根目錄執行 sync 指令。')
|
|
100
|
-
process.exit(1)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// 1. 基本目錄
|
|
105
|
-
const standardDirs = ['pages', 'composables', 'repositories', 'utils', 'assets/images', 'plugins', 'constants', 'configs']
|
|
106
|
-
standardDirs.forEach(ensureDir)
|
|
107
|
-
|
|
108
|
-
// 2. 基礎設定檔 (sync 時若已存在且內容包含繼承語法,則略過或僅更新 schema)
|
|
109
|
-
const extConfigs = [
|
|
110
|
-
{
|
|
111
|
-
name: 'eslint.config.mjs',
|
|
112
|
-
content: "import coreConfig from 'softleader-nuxt-core/core/config/eslint.config.mjs'\n\nexport default [\n ...coreConfig\n]\n"
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: '.prettierrc.cjs',
|
|
116
|
-
content: "module.exports = {\n ...require('softleader-nuxt-core/core/config/prettier.json')\n}\n"
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
name: '.commitlintrc.cjs',
|
|
120
|
-
content: "module.exports = require('softleader-nuxt-core/core/config/git/commitlint.config.cjs')\n"
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: '.lintstagedrc.js',
|
|
124
|
-
content: "export { default } from 'softleader-nuxt-core/core/config/git/lint-staged.config.js'\n"
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
name: '.cz-config.cjs',
|
|
128
|
-
content: "module.exports = require('softleader-nuxt-core/core/config/git/cz-config.cjs')\n"
|
|
129
|
-
}
|
|
130
|
-
]
|
|
131
|
-
extConfigs.forEach(conf => writeFile(conf.name, conf.content, isInit)) // Init 模式覆寫, Sync 模式保留
|
|
132
|
-
|
|
133
|
-
// 3. Nuxt 配置 (僅在 Init 或檔案不存在時產生)
|
|
134
|
-
const nuxtConfigTs = `// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
135
|
-
export default defineNuxtConfig({
|
|
136
|
-
extends: ['softleader-nuxt-core'],
|
|
137
|
-
compatibilityDate: '2024-04-03',
|
|
138
|
-
devtools: { enabled: true }
|
|
139
|
-
})
|
|
140
|
-
`
|
|
141
|
-
writeFile('nuxt.config.ts', nuxtConfigTs, false)
|
|
142
|
-
|
|
143
|
-
const appVue = `<template>
|
|
144
|
-
<NuxtLayout>
|
|
145
|
-
<NuxtPage />
|
|
146
|
-
</NuxtLayout>
|
|
147
|
-
</template>
|
|
148
|
-
`
|
|
149
|
-
writeFile('app.vue', appVue, false)
|
|
150
|
-
|
|
151
|
-
// 4. 首頁 (Init 模式才產生)
|
|
152
|
-
if (isInit) {
|
|
153
|
-
const indexVue = `<template>
|
|
154
|
-
<Welcome />
|
|
155
|
-
</template>
|
|
156
|
-
`
|
|
157
|
-
writeFile('pages/index.vue', indexVue, false)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 5. 範例檔案 (若不存在則補齊)
|
|
161
|
-
const useAppInfoTs = `export const useAppInfo = () => {
|
|
162
|
-
const config = useAppConfig()
|
|
163
|
-
return { getProjectTitle: () => config.title || '未命名專案' }
|
|
164
|
-
}
|
|
165
|
-
`
|
|
166
|
-
writeFile('composables/useAppInfo.ts', useAppInfoTs, false)
|
|
167
|
-
|
|
168
|
-
const exampleRepoTs = `/**
|
|
169
|
-
* 示範:Repository 模式與 useApi 的整合
|
|
170
|
-
*/
|
|
171
|
-
const exampleRepository = {
|
|
172
|
-
/** 獲取使用者列表 */
|
|
173
|
-
getUsers: () => useApi().get('/users'),
|
|
174
|
-
/** 建立使用者 */
|
|
175
|
-
createUser: (data: any) => useApi().post('/users', data)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export default exampleRepository
|
|
179
|
-
`
|
|
180
|
-
writeFile('repositories/exampleRepository.ts', exampleRepoTs, true)
|
|
181
|
-
|
|
182
|
-
// 6. Config Blueprint
|
|
183
|
-
const defaultConfig = `{
|
|
184
|
-
"$schema": "./schema.json",
|
|
185
|
-
"//": "Softleader Nuxt Core 專案設定檔",
|
|
186
|
-
"branding": { "name": "${projectName}", "shortName": "${packageName.toUpperCase()}" },
|
|
187
|
-
"meta": { "title": "${projectName}", "description": "企業級 Nuxt 3 核心架構", "lang": "zh-TW" },
|
|
188
|
-
"layout": { "menuStyle": "sidebar", "sidebar": { "width": 260, "title": "管理系統" } },
|
|
189
|
-
"theme": { "primaryColor": "#2563eb", "borderRadius": 12 },
|
|
190
|
-
"network": { "apiBaseUrl": "/api/v1" },
|
|
191
|
-
"features": { "enableAuth": true, "enableLog": true }
|
|
192
|
-
}`
|
|
193
|
-
writeFile('configs/default.json', defaultConfig, false)
|
|
194
|
-
|
|
195
|
-
// 7. VS Code & Schema
|
|
196
|
-
writeFile('.vscode/settings.json', JSON.stringify({ 'json.schemas': [{ fileMatch: ['/configs/*.json'], url: './configs/schema.json' }] }, null, 2), true)
|
|
197
|
-
const schemaPath = path.resolve(__dirname, '../schemas/config.schema.json')
|
|
198
|
-
if (fs.existsSync(schemaPath)) {
|
|
199
|
-
fs.copyFileSync(schemaPath, path.join(targetDir, 'configs/schema.json'))
|
|
200
|
-
log.info(' [更新] Config Schema 指引')
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// 8. package.json (僅在 Init 或檔案不存在時產生)
|
|
204
|
-
if (isInit) {
|
|
205
|
-
// 讀取當前 core 的版本
|
|
206
|
-
let coreVersion = 'latest'
|
|
207
|
-
try {
|
|
208
|
-
const corePkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'))
|
|
209
|
-
coreVersion = corePkg.version
|
|
210
|
-
} catch (e) {}
|
|
211
|
-
|
|
212
|
-
const packageJson = {
|
|
213
|
-
name: packageName,
|
|
214
|
-
private: true,
|
|
215
|
-
type: 'module',
|
|
216
|
-
scripts: {
|
|
217
|
-
dev: 'nuxt dev',
|
|
218
|
-
build: 'nuxt build',
|
|
219
|
-
generate: 'nuxt generate',
|
|
220
|
-
preview: 'nuxt preview',
|
|
221
|
-
postinstall: 'nuxt prepare',
|
|
222
|
-
typecheck: 'nuxi typecheck'
|
|
223
|
-
},
|
|
224
|
-
dependencies: {
|
|
225
|
-
'softleader-nuxt-core': `^${coreVersion}`,
|
|
226
|
-
'nuxt': '^3.15.4'
|
|
227
|
-
},
|
|
228
|
-
devDependencies: {
|
|
229
|
-
'vue-tsc': '^2.0.0',
|
|
230
|
-
'typescript': '^5.0.0'
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
writeFile('package.json', JSON.stringify(packageJson, null, 2), false)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// 9. README (僅在 Init 或檔案不存在時產生)
|
|
237
|
-
const readmeMd = `# ${projectName}\n\n基於 \`softleader-nuxt-core\` 核心架構。\n\n## 🚀 快速開始\n1. \`npm install\`\n2. \`npm run dev\`\n`
|
|
238
|
-
writeFile('README.md', readmeMd, false)
|
|
239
|
-
|
|
240
|
-
// 9. Git & Husky (Init 模式或缺件時補齊)
|
|
241
|
-
if (isInit) {
|
|
242
|
-
try {
|
|
243
|
-
execSync('git init', { cwd: targetDir, stdio: 'ignore' })
|
|
244
|
-
log.info(' [Git] 初始化儲存庫')
|
|
245
|
-
} catch (e) {}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const huskyDir = path.join(targetDir, '.husky')
|
|
249
|
-
if (!fs.existsSync(huskyDir)) {
|
|
250
|
-
fs.mkdirSync(huskyDir, { recursive: true })
|
|
251
|
-
const templateHuskyDir = path.resolve(__dirname, '../core/templates/husky')
|
|
252
|
-
const hooks = ['pre-commit', 'commit-msg']
|
|
253
|
-
hooks.forEach(hook => {
|
|
254
|
-
const templatePath = path.join(templateHuskyDir, hook)
|
|
255
|
-
if (fs.existsSync(templatePath)) {
|
|
256
|
-
fs.copyFileSync(templatePath, path.join(huskyDir, hook))
|
|
257
|
-
try { fs.chmodSync(path.join(huskyDir, hook), '755') } catch (e) {}
|
|
258
|
-
}
|
|
259
|
-
})
|
|
260
|
-
log.info(' [Husky] 補齊 Git Hooks 設定')
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
log.success(`\n[完成] ${isInit ? '專案建立成功' : '架構同步成功'}!`)
|
|
264
|
-
if (isInit) {
|
|
265
|
-
console.log(`\n請執行以下指令開始開發:\n cd ${targetPath}\n npm install\n npm run dev\n`)
|
|
266
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* softleader-nuxt-core CLI
|
|
5
|
+
* 支援指令:
|
|
6
|
+
* init <project-name> : 建立全新專案
|
|
7
|
+
* sync [path] : 同步現有專案架構與配置
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
import { execSync } from 'node:child_process'
|
|
14
|
+
import { parseArgs } from 'node:util'
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
17
|
+
const __dirname = path.dirname(__filename)
|
|
18
|
+
|
|
19
|
+
// 解析參數
|
|
20
|
+
const options = {
|
|
21
|
+
help: { type: 'boolean', short: 'h' }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { values, positionals } = parseArgs({ options, allowPositionals: true })
|
|
25
|
+
|
|
26
|
+
if (values.help || positionals.length === 0) {
|
|
27
|
+
console.log(`
|
|
28
|
+
用法 (Usage):
|
|
29
|
+
npx softleader-nuxt-core init [project-name] 建立全新專案
|
|
30
|
+
npx softleader-nuxt-core sync [path] 同步現有專案架構 (不覆寫業務代碼)
|
|
31
|
+
|
|
32
|
+
參數 (Arguments):
|
|
33
|
+
[project-name] / [path] 目標資料夾路徑 (預設: 當前目錄)
|
|
34
|
+
|
|
35
|
+
選項 (Options):
|
|
36
|
+
-h, --help 顯示指令幫助訊息
|
|
37
|
+
`)
|
|
38
|
+
process.exit(0)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const command = positionals[0]
|
|
42
|
+
const isInit = command === 'init'
|
|
43
|
+
const isSync = command === 'sync'
|
|
44
|
+
|
|
45
|
+
if (!isInit && !isSync) {
|
|
46
|
+
console.error(`錯誤: 不支援的指令 "${command}"。請使用 init 或 sync。`)
|
|
47
|
+
process.exit(1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const targetPath = positionals[1] || (isInit ? 'my-nuxt-app' : '.')
|
|
51
|
+
const targetDir = path.resolve(process.cwd(), targetPath)
|
|
52
|
+
const projectName = path.basename(targetDir)
|
|
53
|
+
const packageName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '') || 'my-nuxt-app'
|
|
54
|
+
|
|
55
|
+
// --- Helpers ---
|
|
56
|
+
const log = {
|
|
57
|
+
info: (msg) => console.log(`\x1b[36m${msg}\x1b[0m`),
|
|
58
|
+
success: (msg) => console.log(`\x1b[32m${msg}\x1b[0m`),
|
|
59
|
+
warn: (msg) => console.log(`\x1b[33m${msg}\x1b[0m`),
|
|
60
|
+
error: (msg) => console.error(`\x1b[31m${msg}\x1b[0m`),
|
|
61
|
+
step: (msg) => console.log(`\n\x1b[35m[${msg}]\x1b[0m`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const ensureDir = (dirPath) => {
|
|
65
|
+
const fullPath = path.join(targetDir, dirPath)
|
|
66
|
+
if (!fs.existsSync(fullPath)) {
|
|
67
|
+
fs.mkdirSync(fullPath, { recursive: true })
|
|
68
|
+
log.info(` [建立] 目錄: ${dirPath}`)
|
|
69
|
+
} else {
|
|
70
|
+
log.info(` [略過] 目錄已存在: ${dirPath}`)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const writeFile = (filePath, content, overwrite = true) => {
|
|
75
|
+
const fullPath = path.join(targetDir, filePath)
|
|
76
|
+
const dir = path.dirname(fullPath)
|
|
77
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
78
|
+
|
|
79
|
+
if (fs.existsSync(fullPath) && !overwrite) {
|
|
80
|
+
log.info(` [略過] 檔案已存在: ${filePath}`)
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fs.writeFileSync(fullPath, content)
|
|
85
|
+
log.info(` [產生] 檔案: ${filePath}`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// --- Main Execution ---
|
|
89
|
+
|
|
90
|
+
if (isInit) {
|
|
91
|
+
if (fs.existsSync(targetDir) && fs.existsSync(path.join(targetDir, 'package.json'))) {
|
|
92
|
+
log.error(`錯誤: 目錄 "${targetPath}" 已經存在且包含 package.json。初始化失敗。`)
|
|
93
|
+
process.exit(1)
|
|
94
|
+
}
|
|
95
|
+
log.step('Initializing New Project...')
|
|
96
|
+
} else {
|
|
97
|
+
log.step('Syncing Project Architecture...')
|
|
98
|
+
if (!fs.existsSync(path.join(targetDir, 'package.json'))) {
|
|
99
|
+
log.error('錯誤: 找不到 package.json。請在專案根目錄執行 sync 指令。')
|
|
100
|
+
process.exit(1)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 1. 基本目錄
|
|
105
|
+
const standardDirs = ['pages', 'composables', 'repositories', 'utils', 'assets/images', 'plugins', 'constants', 'configs']
|
|
106
|
+
standardDirs.forEach(ensureDir)
|
|
107
|
+
|
|
108
|
+
// 2. 基礎設定檔 (sync 時若已存在且內容包含繼承語法,則略過或僅更新 schema)
|
|
109
|
+
const extConfigs = [
|
|
110
|
+
{
|
|
111
|
+
name: 'eslint.config.mjs',
|
|
112
|
+
content: "import coreConfig from 'softleader-nuxt-core/core/config/eslint.config.mjs'\n\nexport default [\n ...coreConfig\n]\n"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: '.prettierrc.cjs',
|
|
116
|
+
content: "module.exports = {\n ...require('softleader-nuxt-core/core/config/prettier.json')\n}\n"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: '.commitlintrc.cjs',
|
|
120
|
+
content: "module.exports = require('softleader-nuxt-core/core/config/git/commitlint.config.cjs')\n"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: '.lintstagedrc.js',
|
|
124
|
+
content: "export { default } from 'softleader-nuxt-core/core/config/git/lint-staged.config.js'\n"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: '.cz-config.cjs',
|
|
128
|
+
content: "module.exports = require('softleader-nuxt-core/core/config/git/cz-config.cjs')\n"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
extConfigs.forEach(conf => writeFile(conf.name, conf.content, isInit)) // Init 模式覆寫, Sync 模式保留
|
|
132
|
+
|
|
133
|
+
// 3. Nuxt 配置 (僅在 Init 或檔案不存在時產生)
|
|
134
|
+
const nuxtConfigTs = `// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
135
|
+
export default defineNuxtConfig({
|
|
136
|
+
extends: ['softleader-nuxt-core'],
|
|
137
|
+
compatibilityDate: '2024-04-03',
|
|
138
|
+
devtools: { enabled: true }
|
|
139
|
+
})
|
|
140
|
+
`
|
|
141
|
+
writeFile('nuxt.config.ts', nuxtConfigTs, false)
|
|
142
|
+
|
|
143
|
+
const appVue = `<template>
|
|
144
|
+
<NuxtLayout>
|
|
145
|
+
<NuxtPage />
|
|
146
|
+
</NuxtLayout>
|
|
147
|
+
</template>
|
|
148
|
+
`
|
|
149
|
+
writeFile('app.vue', appVue, false)
|
|
150
|
+
|
|
151
|
+
// 4. 首頁 (Init 模式才產生)
|
|
152
|
+
if (isInit) {
|
|
153
|
+
const indexVue = `<template>
|
|
154
|
+
<Welcome />
|
|
155
|
+
</template>
|
|
156
|
+
`
|
|
157
|
+
writeFile('pages/index.vue', indexVue, false)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 5. 範例檔案 (若不存在則補齊)
|
|
161
|
+
const useAppInfoTs = `export const useAppInfo = () => {
|
|
162
|
+
const config = useAppConfig()
|
|
163
|
+
return { getProjectTitle: () => config.title || '未命名專案' }
|
|
164
|
+
}
|
|
165
|
+
`
|
|
166
|
+
writeFile('composables/useAppInfo.ts', useAppInfoTs, false)
|
|
167
|
+
|
|
168
|
+
const exampleRepoTs = `/**
|
|
169
|
+
* 示範:Repository 模式與 useApi 的整合
|
|
170
|
+
*/
|
|
171
|
+
const exampleRepository = {
|
|
172
|
+
/** 獲取使用者列表 */
|
|
173
|
+
getUsers: () => useApi().get('/users'),
|
|
174
|
+
/** 建立使用者 */
|
|
175
|
+
createUser: (data: any) => useApi().post('/users', data)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export default exampleRepository
|
|
179
|
+
`
|
|
180
|
+
writeFile('repositories/exampleRepository.ts', exampleRepoTs, true)
|
|
181
|
+
|
|
182
|
+
// 6. Config Blueprint
|
|
183
|
+
const defaultConfig = `{
|
|
184
|
+
"$schema": "./schema.json",
|
|
185
|
+
"//": "Softleader Nuxt Core 專案設定檔",
|
|
186
|
+
"branding": { "name": "${projectName}", "shortName": "${packageName.toUpperCase()}" },
|
|
187
|
+
"meta": { "title": "${projectName}", "description": "企業級 Nuxt 3 核心架構", "lang": "zh-TW" },
|
|
188
|
+
"layout": { "menuStyle": "sidebar", "sidebar": { "width": 260, "title": "管理系統" } },
|
|
189
|
+
"theme": { "primaryColor": "#2563eb", "borderRadius": 12 },
|
|
190
|
+
"network": { "apiBaseUrl": "/api/v1" },
|
|
191
|
+
"features": { "enableAuth": true, "enableLog": true }
|
|
192
|
+
}`
|
|
193
|
+
writeFile('configs/default.json', defaultConfig, false)
|
|
194
|
+
|
|
195
|
+
// 7. VS Code & Schema
|
|
196
|
+
writeFile('.vscode/settings.json', JSON.stringify({ 'json.schemas': [{ fileMatch: ['/configs/*.json'], url: './configs/schema.json' }] }, null, 2), true)
|
|
197
|
+
const schemaPath = path.resolve(__dirname, '../schemas/config.schema.json')
|
|
198
|
+
if (fs.existsSync(schemaPath)) {
|
|
199
|
+
fs.copyFileSync(schemaPath, path.join(targetDir, 'configs/schema.json'))
|
|
200
|
+
log.info(' [更新] Config Schema 指引')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 8. package.json (僅在 Init 或檔案不存在時產生)
|
|
204
|
+
if (isInit) {
|
|
205
|
+
// 讀取當前 core 的版本
|
|
206
|
+
let coreVersion = 'latest'
|
|
207
|
+
try {
|
|
208
|
+
const corePkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'))
|
|
209
|
+
coreVersion = corePkg.version
|
|
210
|
+
} catch (e) {}
|
|
211
|
+
|
|
212
|
+
const packageJson = {
|
|
213
|
+
name: packageName,
|
|
214
|
+
private: true,
|
|
215
|
+
type: 'module',
|
|
216
|
+
scripts: {
|
|
217
|
+
dev: 'nuxt dev',
|
|
218
|
+
build: 'nuxt build',
|
|
219
|
+
generate: 'nuxt generate',
|
|
220
|
+
preview: 'nuxt preview',
|
|
221
|
+
postinstall: 'nuxt prepare',
|
|
222
|
+
typecheck: 'nuxi typecheck'
|
|
223
|
+
},
|
|
224
|
+
dependencies: {
|
|
225
|
+
'softleader-nuxt-core': `^${coreVersion}`,
|
|
226
|
+
'nuxt': '^3.15.4'
|
|
227
|
+
},
|
|
228
|
+
devDependencies: {
|
|
229
|
+
'vue-tsc': '^2.0.0',
|
|
230
|
+
'typescript': '^5.0.0'
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
writeFile('package.json', JSON.stringify(packageJson, null, 2), false)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 9. README (僅在 Init 或檔案不存在時產生)
|
|
237
|
+
const readmeMd = `# ${projectName}\n\n基於 \`softleader-nuxt-core\` 核心架構。\n\n## 🚀 快速開始\n1. \`npm install\`\n2. \`npm run dev\`\n`
|
|
238
|
+
writeFile('README.md', readmeMd, false)
|
|
239
|
+
|
|
240
|
+
// 9. Git & Husky (Init 模式或缺件時補齊)
|
|
241
|
+
if (isInit) {
|
|
242
|
+
try {
|
|
243
|
+
execSync('git init', { cwd: targetDir, stdio: 'ignore' })
|
|
244
|
+
log.info(' [Git] 初始化儲存庫')
|
|
245
|
+
} catch (e) {}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const huskyDir = path.join(targetDir, '.husky')
|
|
249
|
+
if (!fs.existsSync(huskyDir)) {
|
|
250
|
+
fs.mkdirSync(huskyDir, { recursive: true })
|
|
251
|
+
const templateHuskyDir = path.resolve(__dirname, '../core/templates/husky')
|
|
252
|
+
const hooks = ['pre-commit', 'commit-msg']
|
|
253
|
+
hooks.forEach(hook => {
|
|
254
|
+
const templatePath = path.join(templateHuskyDir, hook)
|
|
255
|
+
if (fs.existsSync(templatePath)) {
|
|
256
|
+
fs.copyFileSync(templatePath, path.join(huskyDir, hook))
|
|
257
|
+
try { fs.chmodSync(path.join(huskyDir, hook), '755') } catch (e) {}
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
log.info(' [Husky] 補齊 Git Hooks 設定')
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
log.success(`\n[完成] ${isInit ? '專案建立成功' : '架構同步成功'}!`)
|
|
264
|
+
if (isInit) {
|
|
265
|
+
console.log(`\n請執行以下指令開始開發:\n cd ${targetPath}\n npm install\n npm run dev\n`)
|
|
266
|
+
}
|