softleader-nuxt-core 1.0.9 → 1.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.
Files changed (79) hide show
  1. package/README.md +93 -64
  2. package/assets/css/layout.css +56 -20
  3. package/bin/init.mjs +244 -155
  4. package/commitlint.config.cjs +1 -0
  5. package/components/templates/Welcome.vue +715 -0
  6. package/components/uiBusiness/BDataTable.vue +977 -888
  7. package/components/uiBusiness/CitySelect.vue +2 -2
  8. package/components/uiBusiness/PolicyForm.vue +9 -3
  9. package/components/uiInterface/IAvatar.vue +1 -1
  10. package/components/uiInterface/IBreadcrumbs.vue +1 -1
  11. package/components/uiInterface/IButton.vue +77 -20
  12. package/components/uiInterface/ICard.vue +9 -4
  13. package/components/uiInterface/ICheckbox.vue +12 -6
  14. package/components/uiInterface/IDataTable.vue +534 -515
  15. package/components/uiInterface/IRadio.vue +8 -2
  16. package/components/uiInterface/ISelect.vue +44 -19
  17. package/components/uiInterface/IStack.vue +9 -2
  18. package/components/uiInterface/ISwitch.vue +14 -3
  19. package/components/uiInterface/ITextField.vue +51 -24
  20. package/components/uiInterface/ITextarea.vue +54 -33
  21. package/composables/useApiRegistry.ts +1 -1
  22. package/composables/useErrorHandler.ts +12 -0
  23. package/composables/useFeatureFlag.ts +1 -1
  24. package/composables/useFileUpload.ts +2 -1
  25. package/composables/useFormatter.ts +2 -2
  26. package/composables/useModules.ts +18 -0
  27. package/composables/useOptions.ts +41 -22
  28. package/composables/useRepo.ts +49 -0
  29. package/configs/default.json +36 -9
  30. package/core/config/app.ts +10 -21
  31. package/core/config/components.ts +13 -25
  32. package/core/config/css.ts +4 -1
  33. package/core/config/eslint.config.mjs +180 -0
  34. package/core/config/git/commit-types.cjs +30 -0
  35. package/core/config/git/commitlint.config.cjs +17 -0
  36. package/core/config/git/cz-config.cjs +21 -0
  37. package/core/config/git/lint-staged.config.js +6 -0
  38. package/core/config/gitignore +30 -0
  39. package/core/config/i18n.ts +9 -7
  40. package/core/config/layout.ts +22 -16
  41. package/core/config/modules.ts +10 -6
  42. package/core/config/nitro.ts +13 -15
  43. package/core/config/prettier.json +14 -0
  44. package/core/config/runtime.ts +5 -29
  45. package/core/config/security/README.md +3 -1
  46. package/core/config/theme-tokens.ts +36 -270
  47. package/core/config/vite.ts +3 -4
  48. package/core/options/registry.ts +13 -2
  49. package/core/options/types.ts +1 -1
  50. package/core/repositories/index.ts +1 -1
  51. package/core/templates/gitignore +38 -0
  52. package/core/templates/husky/commit-msg +6 -0
  53. package/core/templates/husky/pre-commit +6 -0
  54. package/core/templates/vscode/extensions.json +8 -0
  55. package/core/templates/vscode/settings.json +69 -0
  56. package/core/templates/vscode/snippets.code-snippets +16 -0
  57. package/core/templates/vscode/vue3Template.code-snippets +165 -0
  58. package/cz.config.cjs +1 -0
  59. package/eslint.config.mjs +3 -0
  60. package/i18n/locales/en-US.json +1 -1
  61. package/i18n/locales/zh-TW.json +1 -1
  62. package/layouts/default.vue +118 -35
  63. package/lint-staged.config.js +1 -0
  64. package/modules/options-scanner.ts +113 -0
  65. package/modules/repositories-scanner.ts +95 -0
  66. package/nuxt.config.ts +66 -13
  67. package/package.json +32 -6
  68. package/plugins/README.md +2 -0
  69. package/plugins/api.ts +7 -4
  70. package/plugins/vuetify.ts +10 -10
  71. package/prettier.config.cjs +1 -0
  72. package/schemas/config.schema.json +128 -0
  73. package/scripts/product-loader.ts +95 -19
  74. package/scripts/release.mjs +24 -0
  75. package/scripts/sync-configs.mjs +53 -0
  76. package/stores/common.ts +3 -3
  77. package/tsconfig.json +16 -1
  78. package/types/repo.ts +13 -0
  79. package/components/uiInterface/layout/IBreadcrumbs.vue +0 -120
package/bin/init.mjs CHANGED
@@ -1,155 +1,244 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'node:fs'
4
- import path from 'node:path'
5
- import { fileURLToPath } from 'node:url'
6
- import { execSync } from 'node:child_process'
7
- import { parseArgs } from 'node:util'
8
-
9
- const __filename = fileURLToPath(import.meta.url)
10
- const __dirname = path.dirname(__filename)
11
-
12
- const options = {
13
- help: {
14
- type: 'boolean',
15
- short: 'h'
16
- }
17
- }
18
-
19
- const { values, positionals } = parseArgs({ options, allowPositionals: true })
20
-
21
- if (values.help) {
22
- console.log(`
23
- 用法 (Usage): npx softleader-nuxt-core init [project-name]
24
-
25
- 參數 (Arguments):
26
- [project-name] 要建立的專案資料夾名稱 (預設: my-nuxt-app)
27
-
28
- 選項 (Options):
29
- -h, --help 顯示此幫助訊息
30
- `)
31
- process.exit(0)
32
- }
33
-
34
- // 取得專案名稱,預設為 my-nuxt-app
35
- const projectName = positionals[0] || 'my-nuxt-app'
36
- // 解析出目標資料夾的絕對路徑
37
- const targetDir = path.resolve(process.cwd(), projectName)
38
- // 產生合法的 npm package name (轉小寫並移除非英數字元)
39
- const packageName = path.basename(targetDir).toLowerCase().replace(/[^a-z0-9-]/g, '') || 'my-nuxt-app'
40
-
41
- // 檢查資料夾是否已存在
42
- if (fs.existsSync(targetDir)) {
43
- console.error(`錯誤 (Error): 目錄 "${projectName}" 已經存在。`)
44
- process.exit(1)
45
- }
46
-
47
- console.log(`開始在 ${targetDir} 建立 Nuxt 3 專案...`)
48
-
49
- // 1. 建立目標資料夾
50
- fs.mkdirSync(targetDir, { recursive: true })
51
-
52
- // 2. 建立 package.json
53
- const packageJson = {
54
- name: packageName,
55
- version: "1.0.0",
56
- private: true,
57
- type: "module",
58
- scripts: {
59
- "build": "nuxt build",
60
- "dev": "nuxt dev",
61
- "generate": "nuxt generate",
62
- "preview": "nuxt preview",
63
- "postinstall": "nuxt prepare"
64
- },
65
- dependencies: {
66
- "softleader-nuxt-core": "latest"
67
- },
68
- devDependencies: {
69
- "nuxt": "^3.15.4",
70
- "vue": "latest",
71
- "vue-router": "latest"
72
- }
73
- }
74
-
75
- fs.writeFileSync(
76
- path.join(targetDir, 'package.json'),
77
- JSON.stringify(packageJson, null, 2)
78
- )
79
-
80
- // 3. 建立 nuxt.config.ts (預設繼承 softleader-nuxt-core Layer)
81
- const nuxtConfigTs = `// https://nuxt.com/docs/api/configuration/nuxt-config
82
- export default defineNuxtConfig({
83
- extends: ['softleader-nuxt-core'],
84
- compatibilityDate: '2024-04-03',
85
- devtools: { enabled: true }
86
- })
87
- `
88
- fs.writeFileSync(path.join(targetDir, 'nuxt.config.ts'), nuxtConfigTs)
89
-
90
- // 4. 建立基礎的 app.vue
91
- const appVue = `<template>
92
- <NuxtLayout>
93
- <NuxtPage />
94
- </NuxtLayout>
95
- </template>
96
- `
97
- fs.writeFileSync(path.join(targetDir, 'app.vue'), appVue)
98
-
99
- // 5. 建立範例頁面 pages/index.vue
100
- const pagesDir = path.join(targetDir, 'pages')
101
- fs.mkdirSync(pagesDir)
102
- const indexVue = `<template>
103
- <IStack vertical justify="center" align="center" class="min-h-screen bg-gray-50 p-6">
104
- <ICard class="w-full max-w-2xl px-8 py-10 text-center shadow-lg rounded-2xl">
105
- <IIcon name="heroicons:rocket-launch" class="text-6xl text-primary-500 mb-6 mx-auto" />
106
- <h1 class="text-3xl font-bold text-gray-800 mb-4">歡迎使用 Softleader Nuxt Core!</h1>
107
- <p class="text-gray-600 mb-8 leading-relaxed">
108
- 您的專案已經成功建立。現在你可以直接使用 Layer 內建的設計系統元件,加速你的開發流程。
109
- </p>
110
-
111
- <IAlert type="success" show-icon class="mb-8 text-left">
112
- <strong>準備就緒!</strong> 嘗試編輯 <code>pages/index.vue</code> 來查看熱更新效果。
113
- </IAlert>
114
-
115
- <IStack gap="4" justify="center">
116
- <IButton type="primary" size="large" href="https://nuxt.com/docs" target="_blank">
117
- <IIcon name="heroicons:book-open" class="mr-2" />
118
- Nuxt 官方文件
119
- </IButton>
120
- <IButton variant="outlined" size="large" href="https://github.com" target="_blank">
121
- <IIcon name="bx:bxl-github" class="mr-2" />
122
- 元件庫指南
123
- </IButton>
124
- </IStack>
125
- </ICard>
126
- </IStack>
127
- </template>
128
- `
129
- fs.writeFileSync(path.join(pagesDir, 'index.vue'), indexVue)
130
-
131
- // 6. 建立 tsconfig.json 以支援 TypeScript 型別檢查
132
- const tsconfig = `{
133
- "extends": "./.nuxt/tsconfig.json"
134
- }
135
- `
136
- fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), tsconfig)
137
-
138
- // 7. 初始化 Git 儲存庫並安裝 npm 依賴
139
- try {
140
- console.log('正在安裝依賴,這可能需要一點時間 (Installing dependencies)...')
141
- execSync('npm install', { cwd: targetDir, stdio: 'inherit' })
142
-
143
- console.log('正在初始化 Git 儲存庫 (Initializing git repository)...')
144
- execSync('git init', { cwd: targetDir, stdio: 'ignore' })
145
- } catch (error) {
146
- console.log('警告 (Warning): 自動安裝指令執行失敗。您可能需要手動進入資料夾執行 npm install。')
147
- }
148
-
149
- console.log(`
150
- 專案 "${projectName}" 已準備就緒!
151
-
152
- 下一步 (Next steps):
153
- cd ${projectName}
154
- npm run dev
155
- `)
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)) {
92
+ log.error(`錯誤: 目錄 "${targetPath}" 已經存在。初始化必須使用新目錄。`)
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 = `<script setup lang="ts">
154
+ const { formatDateTime } = useDateTime()
155
+ const notify = useNotify()
156
+ const now = ref(formatDateTime(new Date()))
157
+ const handleClick = () => notify.success('連動成功!')
158
+ </script>
159
+
160
+ <template>
161
+ <div class="pa-10">
162
+ <IAlert type="info" title="專案已就緒" text="這是繼承自 softleader-nuxt-core 的新專案。" class="mb-6" />
163
+ <ICard title="功能示範">
164
+ <div class="d-flex align-center gap-4">
165
+ <div>時間: {{ now }}</div>
166
+ <IButton variant="primary" @click="handleClick">測試通知</IButton>
167
+ </div>
168
+ </ICard>
169
+ </div>
170
+ </template>
171
+ `
172
+ writeFile('pages/index.vue', indexVue, false)
173
+ }
174
+
175
+ // 5. 範例檔案 (若不存在則補齊)
176
+ const useAppInfoTs = `export const useAppInfo = () => {
177
+ const config = useAppConfig()
178
+ return { getProjectTitle: () => config.title || '未命名專案' }
179
+ }
180
+ `
181
+ writeFile('composables/useAppInfo.ts', useAppInfoTs, false)
182
+
183
+ const exampleRepoTs = `export const exampleRepository = () => {
184
+ const api = useApi()
185
+ return {
186
+ getUsers: () => api.get('/users'),
187
+ createUser: (data: any) => api.post('/users', data)
188
+ }
189
+ }
190
+ `
191
+ writeFile('repositories/exampleRepository.ts', exampleRepoTs, false)
192
+
193
+ // 6. Config Blueprint
194
+ const defaultConfig = `{
195
+ "$schema": "./schema.json",
196
+ "//": "Softleader Nuxt Core 專案設定檔",
197
+ "branding": { "name": "${projectName}", "shortName": "${packageName.toUpperCase()}" },
198
+ "meta": { "title": "${projectName}", "description": "企業級 Nuxt 3 核心架構", "lang": "zh-TW" },
199
+ "layout": { "menuStyle": "sidebar", "sidebar": { "width": 260, "title": "管理系統" } },
200
+ "theme": { "primaryColor": "#2563eb", "borderRadius": 12 },
201
+ "network": { "apiBaseUrl": "/api/v1" },
202
+ "features": { "enableAuth": true, "enableLog": true }
203
+ }`
204
+ writeFile('configs/default.json', defaultConfig, false)
205
+
206
+ // 7. VS Code & Schema
207
+ writeFile('.vscode/settings.json', JSON.stringify({ 'json.schemas': [{ fileMatch: ['/configs/*.json'], url: './configs/schema.json' }] }, null, 2), true)
208
+ const schemaPath = path.resolve(__dirname, '../schemas/config.schema.json')
209
+ if (fs.existsSync(schemaPath)) {
210
+ fs.copyFileSync(schemaPath, path.join(targetDir, 'configs/schema.json'))
211
+ log.info(' [更新] Config Schema 指引')
212
+ }
213
+
214
+ // 8. README (僅在 Init 或檔案不存在時產生)
215
+ const readmeMd = `# ${projectName}\n\n基於 \`softleader-nuxt-core\` 核心架構。\n\n## 🚀 快速開始\n1. \`npm install\`\n2. \`npm run dev\`\n`
216
+ writeFile('README.md', readmeMd, false)
217
+
218
+ // 9. Git & Husky (Init 模式或缺件時補齊)
219
+ if (isInit) {
220
+ try {
221
+ execSync('git init', { cwd: targetDir, stdio: 'ignore' })
222
+ log.info(' [Git] 初始化儲存庫')
223
+ } catch (e) {}
224
+ }
225
+
226
+ const huskyDir = path.join(targetDir, '.husky')
227
+ if (!fs.existsSync(huskyDir)) {
228
+ fs.mkdirSync(huskyDir, { recursive: true })
229
+ const templateHuskyDir = path.resolve(__dirname, '../core/templates/husky')
230
+ const hooks = ['pre-commit', 'commit-msg']
231
+ hooks.forEach(hook => {
232
+ const templatePath = path.join(templateHuskyDir, hook)
233
+ if (fs.existsSync(templatePath)) {
234
+ fs.copyFileSync(templatePath, path.join(huskyDir, hook))
235
+ try { fs.chmodSync(path.join(huskyDir, hook), '755') } catch (e) {}
236
+ }
237
+ })
238
+ log.info(' [Husky] 補齊 Git Hooks 設定')
239
+ }
240
+
241
+ log.success(`\n[完成] ${isInit ? '專案建立成功' : '架構同步成功'}!`)
242
+ if (isInit) {
243
+ console.log(`\n請執行以下指令開始開發:\n cd ${targetPath}\n npm install\n npm run dev\n`)
244
+ }
@@ -0,0 +1 @@
1
+ module.exports = require('./core/config/git/commitlint.config.cjs')