create-vite-vue 1.8.0 → 2.0.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.
@@ -0,0 +1,22 @@
1
+ {
2
+ "workbench.colorCustomizations": {
3
+ "activityBar.activeBackground": "#65c89b",
4
+ "activityBar.background": "#65c89b",
5
+ "activityBar.foreground": "#15202b",
6
+ "activityBar.inactiveForeground": "#15202b99",
7
+ "activityBarBadge.background": "#945bc4",
8
+ "activityBarBadge.foreground": "#e7e7e7",
9
+ "commandCenter.border": "#15202b99",
10
+ "sash.hoverBorder": "#65c89b",
11
+ "statusBar.background": "#42b883",
12
+ "statusBar.foreground": "#15202b",
13
+ "statusBarItem.hoverBackground": "#359268",
14
+ "statusBarItem.remoteBackground": "#42b883",
15
+ "statusBarItem.remoteForeground": "#15202b",
16
+ "titleBar.activeBackground": "#42b883",
17
+ "titleBar.activeForeground": "#15202b",
18
+ "titleBar.inactiveBackground": "#42b88399",
19
+ "titleBar.inactiveForeground": "#15202b99"
20
+ },
21
+ "peacock.color": "#42b883"
22
+ }
package/bin/index.js CHANGED
@@ -1,69 +1,83 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
+ // bin/index.js
2
3
  import fs from 'fs'
3
4
  import path from 'path'
4
5
  import { fileURLToPath } from 'url'
5
6
 
6
7
  // === 导入模块 ===
8
+ import { cleanMainFile } from '../lib/cleanMain.js'
7
9
  import { parseExtraPlugins, parseFeatures } from '../lib/features.js'
8
- import { generateMainFile } from '../lib/mainFile.js'
9
10
  import { generatePackageJson } from '../lib/package.js'
10
11
  import { setupPlugins } from '../lib/plugins/index.js'
11
12
  import { askAutoRoute, askRunDev, chooseFeatures, chooseLanguage, getProjectName } from '../lib/prompts.js'
12
- import { copyBaseTemplate, copyOptionalTemplates, updateIndexHtml } from '../lib/template.js'
13
+ import { copyBaseTemplate, updateIndexHtml } from '../lib/template.js'
13
14
  import { checkNodeVersion, detectPackageManager, runCmd } from '../lib/utils.js'
14
15
 
15
- // ===================== 常量 =====================
16
- const requiredVersion = '22.19.0'
17
16
  const __filename = fileURLToPath(import.meta.url)
18
17
  const __dirname = path.dirname(__filename)
19
- const pkgManager = detectPackageManager()
20
-
21
- const pkgCommands = {
22
- npm: { install: 'npm install', dev: 'npm run dev' },
23
- pnpm: { install: 'pnpm install', dev: 'pnpm dev' }
24
- }
18
+ const requiredVersion = '22.19.0'
25
19
 
26
20
  // ===================== 主流程 =====================
27
21
  ; (async () => {
22
+ // 1. 检查 Node 版本
28
23
  checkNodeVersion(requiredVersion)
29
24
 
25
+ // 2. 获取项目名称
30
26
  const projectName = await getProjectName(fs, path)
31
27
  const targetDir = path.resolve(process.cwd(), projectName)
32
28
 
29
+ // 3. 选择语言
33
30
  const language = await chooseLanguage()
31
+
32
+ // 4. 选择功能
34
33
  const featureList = await chooseFeatures()
34
+
35
+ // 5. 解析功能
35
36
  const features = parseFeatures(featureList)
36
37
  const extraPlugins = parseExtraPlugins(featureList)
38
+ const enableHttps = featureList.includes('https') || false
37
39
 
40
+ // 6. 询问自动路由
38
41
  const autoRoute = await askAutoRoute(features.router)
39
- const enableHttps = featureList.includes('https') || false
42
+
43
+ // 7. 询问是否运行 dev
44
+ const pkgManager = detectPackageManager()
45
+ const pkgCommands = {
46
+ npm: { install: 'npm install', dev: 'npm run dev' },
47
+ pnpm: { install: 'pnpm install', dev: 'pnpm dev' }
48
+ }
40
49
  const runDev = await askRunDev(pkgCommands[pkgManager].dev)
41
50
 
42
- // 模板文件处理
43
- copyBaseTemplate(language, targetDir, __dirname)
44
- updateIndexHtml(projectName, targetDir)
45
- copyOptionalTemplates(features, extraPlugins, language, targetDir, __dirname)
51
+ // 8. 复制基础模板
52
+ await copyBaseTemplate(language, targetDir, __dirname)
53
+
54
+ // 9. 更新 index.html
55
+ await updateIndexHtml(projectName, targetDir)
46
56
 
47
- // 配置插件
48
- setupPlugins(features, extraPlugins, {
57
+ // 10. 配置插件,并获取已使用的占位符
58
+ const unusedPlaceholders = await setupPlugins(features, extraPlugins, {
49
59
  language,
50
60
  targetDir,
51
61
  autoRoute,
52
- enableHttps
62
+ enableHttps,
63
+ __dirname
53
64
  })
54
65
 
55
- // 生成 main 文件
56
- await generateMainFile(features, extraPlugins, language, targetDir)
66
+ // 11. 清理 main 文件中未使用的占位符
67
+ await cleanMainFile(language, targetDir, unusedPlaceholders)
57
68
 
58
- // package.json
59
- generatePackageJson(projectName, features, extraPlugins, autoRoute, enableHttps, language, targetDir, pkgManager)
69
+ // 12. 生成 package.json
70
+ await generatePackageJson(projectName, features, extraPlugins, autoRoute, enableHttps, language, targetDir, pkgManager)
60
71
 
61
- // 安装依赖
72
+ // 13. 安装依赖
73
+ console.log('\n📦 正在安装依赖...')
62
74
  runCmd(pkgCommands[pkgManager].install, targetDir)
63
75
 
64
- // 启动 dev 或提示完成
65
- if(runDev) runCmd(pkgCommands[pkgManager].dev, targetDir)
66
- else {
76
+ // 14. 启动 dev 或提示完成
77
+ if(runDev) {
78
+ console.log('\n🚀 启动开发服务器...')
79
+ runCmd(pkgCommands[pkgManager].dev, targetDir)
80
+ } else {
67
81
  console.log(`\n✅ 项目创建完成`)
68
82
  console.log(`👉 cd ${projectName}`)
69
83
  console.log(`👉 ${pkgCommands[pkgManager].dev}`)
@@ -0,0 +1,36 @@
1
+ // lib/cleanMain.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function cleanMainFile (language, targetDir, unusedPlaceholders) {
7
+ const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
8
+ const mainPath = path.join(targetDir, `src/${mainFile}`)
9
+
10
+ if(!existsSync(mainPath)) {
11
+ console.log(' ⚠️ main 文件不存在,跳过清理')
12
+ return
13
+ }
14
+
15
+ let content = await fs.readFile(mainPath, 'utf-8')
16
+
17
+ // 直接删除所有未使用的占位符
18
+ const allUnused = [...unusedPlaceholders.import, ...unusedPlaceholders.use]
19
+
20
+ for(const placeholder of allUnused) {
21
+ const regex = new RegExp(`^\\s*${escapeRegExp(placeholder)}\\s*$\\n?`, 'gm')
22
+ content = content.replace(regex, '')
23
+ }
24
+
25
+ // 清理多余空行
26
+ content = content.replace(/\n{3,}/g, '\n\n')
27
+ content = content.replace(/^\n+/, '')
28
+ content = content.replace(/\n+$/, '\n')
29
+
30
+ await fs.writeFile(mainPath, content)
31
+ console.log(' 🧹 已清理未使用的占位符')
32
+ }
33
+
34
+ function escapeRegExp (str) {
35
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
36
+ }
package/lib/package.js CHANGED
@@ -1,13 +1,20 @@
1
1
  // lib/package.js
2
- import fs from 'fs'
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
3
4
  import path from 'path'
4
5
 
5
- export function generatePackageJson (projectName, features, extraPlugins, autoRoute, enableHttps, language, targetDir, pkgManager) {
6
- const pkgTpl = path.join(targetDir, 'package.json.tpl')
7
- if(!fs.existsSync(pkgTpl)) return
8
- let pkg = fs.readFileSync(pkgTpl, 'utf-8')
6
+ export async function generatePackageJson (projectName, features, extraPlugins, autoRoute, enableHttps, language, targetDir, pkgManager) {
7
+ const pkgPath = path.join(targetDir, 'package.json')
9
8
 
9
+ if(!existsSync(pkgPath)) {
10
+ throw new Error(`package.json 不存在: ${pkgPath}`)
11
+ }
12
+
13
+ let pkgContent = await fs.readFile(pkgPath, 'utf-8')
14
+
15
+ // 收集可选依赖
10
16
  const optionalDeps = {}
17
+
11
18
  if(features.router) optionalDeps['vue-router'] = '^5.0.3'
12
19
  if(features.pinia) {
13
20
  optionalDeps['pinia'] = '^3.0.4'
@@ -31,15 +38,26 @@ export function generatePackageJson (projectName, features, extraPlugins, autoRo
31
38
  if(enableHttps) optionalDeps['vite-plugin-mkcert'] = '^1.17.10'
32
39
  if(autoRoute) optionalDeps['vite-plugin-pages'] = '^0.33.3'
33
40
 
34
- const keys = Object.keys(optionalDeps)
35
- const depsStr = keys.length ? ',\n' + keys.map(k => ` "${k}": "${optionalDeps[k]}"`).join(',\n') : ''
36
- pkg = pkg.replace('__PROJECT_NAME__', projectName).replace('__OPTIONAL_DEP__', depsStr)
41
+ // 构建依赖字符串
42
+ const depsKeys = Object.keys(optionalDeps)
43
+ let depsStr = ''
44
+
45
+ if(depsKeys.length > 0) {
46
+ depsStr = ',\n' + depsKeys.map(k => ` "${k}": "${optionalDeps[k]}"`).join(',\n')
47
+ }
48
+
49
+ // 替换占位符
50
+ pkgContent = pkgContent.replace('__PROJECT_NAME__', projectName)
51
+ pkgContent = pkgContent.replace('__OPTIONAL_DEP__', depsStr)
52
+
53
+ // 解析并重新格式化 JSON
54
+ const pkgObj = JSON.parse(pkgContent)
37
55
 
38
- const pkgObj = JSON.parse(pkg)
56
+ // pnpm 特殊配置
39
57
  if(pkgManager === 'pnpm' && features.ui.includes('vant')) {
40
58
  pkgObj.pnpm = { overrides: { "@vant/use": "^1.0.0", "@vant/popperjs": "^1.0.0" } }
41
59
  }
42
60
 
43
- fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkgObj, null, 2))
44
- fs.unlinkSync(pkgTpl)
61
+ await fs.writeFile(pkgPath, JSON.stringify(pkgObj, null, 2))
62
+ console.log(' ✅ package.json 生成完成')
45
63
  }
@@ -1,18 +1,18 @@
1
1
  // lib/plugins/autoRoute.js
2
- import fs from 'fs'
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
3
4
  import path from 'path'
4
5
 
5
- export function setupAutoRoute (language, targetDir) {
6
- setupRouter(language, targetDir)
7
- setupVite(language, targetDir)
8
- }
6
+ export async function setupAutoRoute (language, targetDir) {
7
+ console.log(' 🚀 配置自动路由...')
9
8
 
10
- // ================= router =================
11
- function setupRouter (language, targetDir) {
12
- const routerIndexPath = path.join(
13
- targetDir,
14
- `src/router/index.${language === 'ts' ? 'ts' : 'js'}`
15
- )
9
+ // 修改 router/index 文件
10
+ const routerIndexPath = path.join(targetDir, `src/router/index.${language === 'ts' ? 'ts' : 'js'}`)
11
+
12
+ // 确保文件存在
13
+ if(!existsSync(routerIndexPath)) {
14
+ throw new Error(`router/index.js 不存在,请确保已选择 Router 插件且模板复制成功`)
15
+ }
16
16
 
17
17
  const content = `import { createRouter, createWebHistory } from 'vue-router'
18
18
  import routes from '~pages'
@@ -21,34 +21,28 @@ routes.unshift({ path: '/', redirect: '/home' })
21
21
 
22
22
  export default createRouter({ history: createWebHistory(), routes })`
23
23
 
24
- fs.writeFileSync(routerIndexPath, content)
25
- }
24
+ await fs.writeFile(routerIndexPath, content)
25
+ console.log(' ✅ router/index.js 已更新')
26
26
 
27
- // ================= vite =================
28
- function setupVite (language, targetDir) {
29
- const viteConfigPath = path.join(
30
- targetDir,
31
- `vite.config.${language === 'ts' ? 'ts' : 'js'}`
32
- )
27
+ // 修改 vite.config
28
+ const viteConfigPathTs = path.join(targetDir, 'vite.config.ts')
29
+ const viteConfigPathJs = path.join(targetDir, 'vite.config.js')
30
+ const viteConfigPath = existsSync(viteConfigPathTs) ? viteConfigPathTs : viteConfigPathJs
33
31
 
34
- if(!fs.existsSync(viteConfigPath)) return
32
+ if(!viteConfigPath || !existsSync(viteConfigPath)) {
33
+ throw new Error(`vite.config 文件不存在`)
34
+ }
35
35
 
36
- let viteConfig = fs.readFileSync(viteConfigPath, 'utf-8')
36
+ let viteConfig = await fs.readFile(viteConfigPath, 'utf-8')
37
37
 
38
- // import fs
39
38
  if(!viteConfig.includes("import fs from 'fs'")) {
40
39
  viteConfig = `import fs from 'fs'\n${viteConfig}`
41
40
  }
42
41
 
43
- // import Pages
44
42
  if(!viteConfig.includes("vite-plugin-pages")) {
45
- viteConfig = viteConfig.replace(
46
- /(import .*?from .*?\n)/,
47
- `$1import Pages from 'vite-plugin-pages'\n`
48
- )
43
+ viteConfig = viteConfig.replace(/(import .*?from .*?\n)/, `$1import Pages from 'vite-plugin-pages'\n`)
49
44
  }
50
45
 
51
- // 插件注入
52
46
  if(!viteConfig.includes("Pages({")) {
53
47
  viteConfig = viteConfig.replace(
54
48
  /plugins:\s*\[/,
@@ -75,5 +69,6 @@ function setupVite (language, targetDir) {
75
69
  )
76
70
  }
77
71
 
78
- fs.writeFileSync(viteConfigPath, viteConfig)
72
+ await fs.writeFile(viteConfigPath, viteConfig)
73
+ console.log(' ✅ vite.config 已更新')
79
74
  }
@@ -0,0 +1,19 @@
1
+ // lib/plugins/axios.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function setupAxios (language, targetDir, __dirname) {
7
+ console.log(' 🌐 配置 Axios...')
8
+
9
+ // 复制模板
10
+ const axiosTemplate = language === 'ts' ? 'axios-ts' : 'axios-js'
11
+ const templatePath = path.resolve(__dirname, `../template/${axiosTemplate}`)
12
+
13
+ if(!existsSync(templatePath)) {
14
+ throw new Error(`模板不存在: ${templatePath}`)
15
+ }
16
+
17
+ await fs.cp(templatePath, targetDir, { recursive: true })
18
+ console.log(' ✅ Axios 模板复制完成')
19
+ }
@@ -0,0 +1,28 @@
1
+ // lib/plugins/elementPlus.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function setupElementPlus (language, targetDir, __dirname) {
7
+ console.log(' 🎨 配置 Element Plus...')
8
+
9
+ // 修改 main 文件
10
+ const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
11
+ const mainPath = path.join(targetDir, `src/${mainFile}`)
12
+
13
+ if(!existsSync(mainPath)) {
14
+ throw new Error(`main 文件不存在: ${mainPath}`)
15
+ }
16
+
17
+ let content = await fs.readFile(mainPath, 'utf-8')
18
+ content = content.replace(
19
+ '/* __ELEMENT_IMPORT__ */',
20
+ `import ElementPlus from 'element-plus'\nimport zhCn from 'element-plus/es/locale/lang/zh-cn'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'`
21
+ )
22
+ content = content.replace(
23
+ '/* __ELEMENT_USE__ */',
24
+ `app.use(ElementPlus, { locale: zhCn })\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n app.component(key, component)\n}`
25
+ )
26
+ await fs.writeFile(mainPath, content)
27
+ console.log(' ✅ main 文件已更新')
28
+ }
@@ -1,38 +1,33 @@
1
1
  // lib/plugins/https.js
2
- import fs from 'fs'
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
3
4
  import path from 'path'
4
5
 
5
- export function setupHttps (targetDir) {
6
+ export async function setupHttps (targetDir) {
7
+ console.log(' 🔐 配置 HTTPS...')
8
+
6
9
  const viteConfigPathTs = path.join(targetDir, 'vite.config.ts')
7
10
  const viteConfigPathJs = path.join(targetDir, 'vite.config.js')
11
+ const viteConfigPath = existsSync(viteConfigPathTs) ? viteConfigPathTs : viteConfigPathJs
8
12
 
9
- const viteConfigPath = fs.existsSync(viteConfigPathTs)
10
- ? viteConfigPathTs
11
- : viteConfigPathJs
12
-
13
- if(!viteConfigPath || !fs.existsSync(viteConfigPath)) return
13
+ if(!viteConfigPath || !existsSync(viteConfigPath)) {
14
+ throw new Error(`vite.config 文件不存在`)
15
+ }
14
16
 
15
- let viteConfig = fs.readFileSync(viteConfigPath, 'utf-8')
17
+ let viteConfig = await fs.readFile(viteConfigPath, 'utf-8')
16
18
 
17
- // ================== 1. 注入 import ==================
19
+ // 注入 import
18
20
  if(!viteConfig.includes("vite-plugin-mkcert")) {
19
- viteConfig = viteConfig.replace(
20
- /(import .*?from .*?\n)/,
21
- `$1import mkcert from 'vite-plugin-mkcert'\n`
22
- )
21
+ viteConfig = viteConfig.replace(/(import .*?from .*?\n)/, `$1import mkcert from 'vite-plugin-mkcert'\n`)
23
22
  }
24
23
 
25
- // ================== 2. 注入插件 ==================
24
+ // 注入插件
26
25
  if(!viteConfig.includes('mkcert()')) {
27
- viteConfig = viteConfig.replace(
28
- /plugins:\s*\[/,
29
- `plugins: [\n mkcert(),`
30
- )
26
+ viteConfig = viteConfig.replace(/plugins:\s*\[/, `plugins: [\n mkcert(),`)
31
27
  }
32
28
 
33
- fs.writeFileSync(viteConfigPath, viteConfig)
34
-
35
- // ================== 3. 提示 ==================
29
+ await fs.writeFile(viteConfigPath, viteConfig)
30
+ console.log(' ✅ vite.config 已更新')
36
31
  console.log('🔐 已启用 HTTPS(mkcert)')
37
32
  console.log('👉 首次运行会自动生成本地证书,请稍等...')
38
33
  }
@@ -1,23 +1,71 @@
1
1
  // lib/plugins/index.js
2
2
  import { setupAutoRoute } from './autoRoute.js'
3
+ import { setupAxios } from './axios.js'
4
+ import { setupElementPlus } from './elementPlus.js'
3
5
  import { setupHttps } from './https.js'
6
+ import { setupPinia } from './pinia.js'
7
+ import { setupRouter } from './router.js'
4
8
  import { setupTailwind } from './tailwind.js'
9
+ import { setupVant } from './vant.js'
5
10
 
6
- export function setupPlugins (features, extraPlugins, context) {
7
- const { language, targetDir, autoRoute, enableHttps } = context
11
+ export async function setupPlugins (features, extraPlugins, context) {
12
+ const { language, targetDir, autoRoute, enableHttps, __dirname } = context
13
+
14
+ // 收集未使用的占位符
15
+ const unusedPlaceholders = {
16
+ import: [],
17
+ use: []
18
+ }
19
+
20
+ console.log('\n🔌 配置插件...')
21
+
22
+ // Router(先复制模板,再配置自动路由)
23
+ if(features.router) {
24
+ await setupRouter(language, targetDir, __dirname)
25
+ if(autoRoute) await setupAutoRoute(language, targetDir)
26
+ } else {
27
+ unusedPlaceholders.import.push('/* __ROUTER_IMPORT__ */')
28
+ unusedPlaceholders.use.push('/* __ROUTER_USE__ */')
29
+ }
30
+
31
+ // Pinia
32
+ if(features.pinia) {
33
+ await setupPinia(language, targetDir, __dirname)
34
+ } else {
35
+ unusedPlaceholders.import.push('/* __PINIA_IMPORT__ */')
36
+ unusedPlaceholders.use.push('/* __PINIA_USE__ */')
37
+ }
38
+
39
+ // Element Plus
40
+ if(features.ui.includes('element')) {
41
+ await setupElementPlus(language, targetDir, __dirname)
42
+ } else {
43
+ unusedPlaceholders.import.push('/* __ELEMENT_IMPORT__ */')
44
+ unusedPlaceholders.use.push('/* __ELEMENT_USE__ */')
45
+ }
46
+
47
+ // Vant
48
+ if(features.ui.includes('vant')) {
49
+ await setupVant(language, targetDir, __dirname)
50
+ } else {
51
+ unusedPlaceholders.import.push('/* __VANT_IMPORT__ */')
52
+ unusedPlaceholders.use.push('/* __VANT_USE__ */')
53
+ }
54
+
55
+ // Axios
56
+ if(features.axios) {
57
+ await setupAxios(language, targetDir, __dirname)
58
+ }
8
59
 
9
60
  // Tailwind
10
61
  if(extraPlugins.includes('tailwind')) {
11
- setupTailwind(targetDir)
62
+ await setupTailwind(language, targetDir, __dirname)
12
63
  }
13
64
 
14
65
  // HTTPS
15
66
  if(enableHttps) {
16
- setupHttps(targetDir)
67
+ await setupHttps(targetDir)
17
68
  }
18
69
 
19
- // 自动路由
20
- if(features.router && autoRoute) {
21
- setupAutoRoute(language, targetDir)
22
- }
70
+ return unusedPlaceholders
23
71
  }
@@ -0,0 +1,36 @@
1
+ // lib/plugins/pinia.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function setupPinia (language, targetDir, __dirname) {
7
+ console.log(' 📦 配置 Pinia...')
8
+
9
+ // 复制模板
10
+ const piniaTemplate = language === 'ts' ? 'pinia-ts' : 'pinia-js'
11
+ const templatePath = path.resolve(__dirname, `../template/${piniaTemplate}`)
12
+
13
+ if(!existsSync(templatePath)) {
14
+ throw new Error(`模板不存在: ${templatePath}`)
15
+ }
16
+
17
+ await fs.cp(templatePath, targetDir, { recursive: true })
18
+ console.log(' ✅ Pinia 模板复制完成')
19
+
20
+ // 修改 main 文件
21
+ const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
22
+ const mainPath = path.join(targetDir, `src/${mainFile}`)
23
+
24
+ if(!existsSync(mainPath)) {
25
+ throw new Error(`main 文件不存在: ${mainPath}`)
26
+ }
27
+
28
+ let content = await fs.readFile(mainPath, 'utf-8')
29
+ content = content.replace(
30
+ '/* __PINIA_IMPORT__ */',
31
+ `import { createPinia } from 'pinia'\nimport persistedstate from 'pinia-plugin-persistedstate'`
32
+ )
33
+ content = content.replace('/* __PINIA_USE__ */', 'app.use(createPinia().use(persistedstate))')
34
+ await fs.writeFile(mainPath, content)
35
+ console.log(' ✅ main 文件已更新')
36
+ }
@@ -0,0 +1,32 @@
1
+ // lib/plugins/router.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function setupRouter (language, targetDir, __dirname) {
7
+ console.log(' 🛤️ 配置 Router...')
8
+
9
+ // 复制模板
10
+ const routerTemplate = language === 'ts' ? 'router-ts' : 'router-js'
11
+ const templatePath = path.resolve(__dirname, `../template/${routerTemplate}`)
12
+ if(!existsSync(templatePath)) {
13
+ throw new Error(`模板不存在: ${templatePath}`)
14
+ }
15
+
16
+ await fs.cp(templatePath, targetDir, { recursive: true })
17
+ console.log(' ✅ Router 模板复制完成')
18
+
19
+ // 修改 main 文件
20
+ const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
21
+ const mainPath = path.join(targetDir, `src/${mainFile}`)
22
+
23
+ if(!existsSync(mainPath)) {
24
+ throw new Error(`main 文件不存在: ${mainPath}`)
25
+ }
26
+
27
+ let content = await fs.readFile(mainPath, 'utf-8')
28
+ content = content.replace('/* __ROUTER_IMPORT__ */', "import router from './router'")
29
+ content = content.replace('/* __ROUTER_USE__ */', 'app.use(router)')
30
+ await fs.writeFile(mainPath, content)
31
+ console.log(' ✅ main 文件已更新')
32
+ }
@@ -1,14 +1,31 @@
1
1
  // lib/plugins/tailwind.js
2
- import fs from 'fs'
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
3
4
  import path from 'path'
4
5
 
5
- export function setupTailwind (targetDir) {
6
- const stylePath = path.join(targetDir, 'src/style.css')
7
- if(!fs.existsSync(stylePath)) return
6
+ export async function setupTailwind (language, targetDir, __dirname) {
7
+ console.log(' 🎨 配置 Tailwind CSS...')
8
+
9
+ // 复制模板
10
+ const tailwindTemplate = language === 'ts' ? 'tailwind-ts' : 'tailwind-js'
11
+ const templatePath = path.resolve(__dirname, `../template/${tailwindTemplate}`)
12
+
13
+ if(!existsSync(templatePath)) {
14
+ throw new Error(`模板不存在: ${templatePath}`)
15
+ }
8
16
 
9
- const original = fs.readFileSync(stylePath, 'utf-8')
17
+ await fs.cp(templatePath, targetDir, { recursive: true })
18
+ console.log(' ✅ Tailwind 模板复制完成')
19
+
20
+ // 更新 style.css
21
+ const stylePath = path.join(targetDir, 'src/style.css')
22
+ if(!existsSync(stylePath)) {
23
+ throw new Error(`style.css 不存在: ${stylePath}`)
24
+ }
10
25
 
26
+ const original = await fs.readFile(stylePath, 'utf-8')
11
27
  if(!original.includes('@import "tailwindcss";')) {
12
- fs.writeFileSync(stylePath, `@import "tailwindcss";\n${original}`)
28
+ await fs.writeFile(stylePath, `@import "tailwindcss";\n${original}`)
29
+ console.log(' ✅ style.css 已更新')
13
30
  }
14
31
  }
@@ -0,0 +1,25 @@
1
+ // lib/plugins/vant.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function setupVant (language, targetDir, __dirname) {
7
+ console.log(' 📱 配置 Vant...')
8
+
9
+ // 修改 main 文件
10
+ const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
11
+ const mainPath = path.join(targetDir, `src/${mainFile}`)
12
+
13
+ if(!existsSync(mainPath)) {
14
+ throw new Error(`main 文件不存在: ${mainPath}`)
15
+ }
16
+
17
+ let content = await fs.readFile(mainPath, 'utf-8')
18
+ content = content.replace(
19
+ '/* __VANT_IMPORT__ */',
20
+ `import Vant from 'vant'\nimport 'vant/lib/index.css'`
21
+ )
22
+ content = content.replace('/* __VANT_USE__ */', 'app.use(Vant)')
23
+ await fs.writeFile(mainPath, content)
24
+ console.log(' ✅ main 文件已更新')
25
+ }
package/lib/prompts.js CHANGED
@@ -37,7 +37,7 @@ export async function chooseFeatures () {
37
37
  const { featureList } = await prompts({
38
38
  type: 'multiselect',
39
39
  name: 'featureList',
40
- message: '请选择基础功能(↑↓选择,空格确认,回车完成)',
40
+ message: '请选择功能(↑↓选择,空格确认,回车完成)',
41
41
  instructions: false,
42
42
  choices: [
43
43
  { title: 'Vue Router', value: 'router' },
package/lib/template.js CHANGED
@@ -1,29 +1,43 @@
1
1
  // lib/template.js
2
- import fs from 'fs'
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
3
4
  import path from 'path'
4
5
 
5
- export function copyBaseTemplate (language, targetDir, __dirname) {
6
+ export async function copyBaseTemplate (language, targetDir, __dirname) {
6
7
  const baseTemplate = language === 'ts' ? 'base-ts' : 'base-js'
7
- fs.cpSync(path.resolve(__dirname, `../template/${baseTemplate}`), targetDir, { recursive: true })
8
- }
8
+ const templatePath = path.resolve(__dirname, '../template', baseTemplate)
9
9
 
10
- export function updateIndexHtml (projectName, targetDir) {
11
- const indexPath = path.join(targetDir, 'index.html')
12
- if(!fs.existsSync(indexPath)) return
13
- const indexContent = fs.readFileSync(indexPath, 'utf-8')
14
- fs.writeFileSync(indexPath, indexContent.replace(/<title>.*<\/title>/, `<title>${projectName}</title>`))
15
- }
10
+ if(!existsSync(templatePath)) {
11
+ throw new Error(`基础模板不存在: ${templatePath}`)
12
+ }
16
13
 
17
- export function copyOptionalTemplates (features, extraPlugins, language, targetDir, __dirname) {
18
- const copy = name => {
19
- fs.cpSync(path.resolve(__dirname, `../template/${name}`), targetDir, { recursive: true })
14
+ // 复制整个模板目录
15
+ await fs.cp(templatePath, targetDir, { recursive: true })
16
+
17
+ // 重命名 main 文件(去掉 .tpl 后缀)
18
+ const mainTplPath = path.join(targetDir, `src/main.${language === 'ts' ? 'ts' : 'js'}.tpl`)
19
+ const mainPath = path.join(targetDir, `src/main.${language === 'ts' ? 'ts' : 'js'}`)
20
+
21
+ if(existsSync(mainTplPath)) {
22
+ await fs.rename(mainTplPath, mainPath)
20
23
  }
21
- features.router && copy(language === 'ts' ? 'router-ts' : 'router-js')
22
- features.pinia && copy(language === 'ts' ? 'pinia-ts' : 'pinia-js')
23
- features.axios && copy(language === 'ts' ? 'axios-ts' : 'axios-js')
24
- for(const plugin of extraPlugins) {
25
- const templateName = `${plugin}-${language === 'ts' ? 'ts' : 'js'}`
26
- const templatePath = path.resolve(__dirname, `../template/${templateName}`)
27
- if(fs.existsSync(templatePath)) copy(templateName)
24
+
25
+ // 重命名 package.json(去掉 .tpl 后缀)
26
+ const pkgTplPath = path.join(targetDir, 'package.json.tpl')
27
+ const pkgPath = path.join(targetDir, 'package.json')
28
+
29
+ if(existsSync(pkgTplPath)) {
30
+ await fs.rename(pkgTplPath, pkgPath)
28
31
  }
32
+
33
+ console.log(' ✅ 基础模板复制完成')
34
+ }
35
+
36
+ export async function updateIndexHtml (projectName, targetDir) {
37
+ const indexPath = path.join(targetDir, 'index.html')
38
+ if(!existsSync(indexPath)) return
39
+
40
+ const indexContent = await fs.readFile(indexPath, 'utf-8')
41
+ await fs.writeFile(indexPath, indexContent.replace(/<title>.*<\/title>/, `<title>${projectName}</title>`))
42
+ console.log(' ✅ index.html 更新完成')
29
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vite-vue",
3
- "version": "1.8.0",
3
+ "version": "2.0.0",
4
4
  "description": "基于Vite+Vue3创建基础项目模板",
5
5
  "main": "index.js",
6
6
  "author": "YwaiX",
package/lib/mainFile.js DELETED
@@ -1,48 +0,0 @@
1
- // lib/mainFile.js
2
- import fs from 'fs'
3
- import path from 'path'
4
-
5
- export async function generateMainFile (features, extraPlugins, language, targetDir) {
6
- const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
7
- const mainTplPath = path.join(targetDir, `src/${mainFile}.tpl`)
8
- if(!fs.existsSync(mainTplPath)) return
9
- let main = fs.readFileSync(mainTplPath, 'utf-8')
10
-
11
- const replacements = {
12
- '/* __ROUTER_IMPORT__ */': features.router ? "import router from './router'" : '',
13
- '/* __PINIA_IMPORT__ */': features.pinia ? "import { createPinia } from 'pinia'\nimport persistedstate from 'pinia-plugin-persistedstate'" : '',
14
- '/* __ELEMENT_IMPORT__ */': features.ui.includes('element')
15
- ? `import ElementPlus from 'element-plus'
16
- import zhCn from 'element-plus/es/locale/lang/zh-cn'
17
- import 'element-plus/dist/index.css'
18
- import * as ElementPlusIconsVue from '@element-plus/icons-vue'`
19
- : '',
20
- '/* __VANT_IMPORT__ */': features.ui.includes('vant')
21
- ? `import Vant from 'vant'
22
- import 'vant/lib/index.css'`
23
- : '',
24
- '/* __ROUTER_USE__ */': features.router ? 'app.use(router)' : '',
25
- '/* __PINIA_USE__ */': features.pinia ? 'app.use(createPinia().use(persistedstate))' : '',
26
- '/* __ELEMENT_USE__ */': features.ui.includes('element')
27
- ? `app.use(ElementPlus, { locale: zhCn })
28
- for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
29
- app.component(key, component)
30
- }` : '',
31
- '/* __VANT_USE__ */': features.ui.includes('vant') ? 'app.use(Vant)' : ''
32
- }
33
-
34
- function escapeRegExp (str) {
35
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
36
- }
37
-
38
- for(const [placeholder, content] of Object.entries(replacements)) {
39
- if(content) main = main.replace(placeholder, content)
40
- else main = main.replace(new RegExp(`^\\s*${escapeRegExp(placeholder)}\\s*$\\n?`, 'gm'), '')
41
- }
42
-
43
- main = main.replace(/(\s*)const app = createApp\(App\)/, '\n\n$1const app = createApp(App)')
44
- main = main.replace(/\n{3,}/g, '\n\n')
45
-
46
- fs.writeFileSync(path.join(targetDir, `src/${mainFile}`), main)
47
- fs.unlinkSync(mainTplPath)
48
- }