create-vite-vue 1.7.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'
11
+ import { setupPlugins } from '../lib/plugins/index.js'
10
12
  import { askAutoRoute, askRunDev, chooseFeatures, chooseLanguage, getProjectName } from '../lib/prompts.js'
11
- import { configureRouter } from '../lib/router.js'
12
- import { appendTailwind, 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
- import { configureVite } from '../lib/viteConfig.js'
15
15
 
16
- // ===================== 常量 =====================
17
- const requiredVersion = '22.19.0'
18
16
  const __filename = fileURLToPath(import.meta.url)
19
17
  const __dirname = path.dirname(__filename)
20
- const pkgManager = detectPackageManager()
21
-
22
- const pkgCommands = {
23
- npm: { install: 'npm install', dev: 'npm run dev' },
24
- pnpm: { install: 'pnpm install', dev: 'pnpm dev' }
25
- }
18
+ const requiredVersion = '22.19.0'
26
19
 
27
20
  // ===================== 主流程 =====================
28
21
  ; (async () => {
22
+ // 1. 检查 Node 版本
29
23
  checkNodeVersion(requiredVersion)
30
24
 
25
+ // 2. 获取项目名称
31
26
  const projectName = await getProjectName(fs, path)
32
27
  const targetDir = path.resolve(process.cwd(), projectName)
33
28
 
29
+ // 3. 选择语言
34
30
  const language = await chooseLanguage()
31
+
32
+ // 4. 选择功能
35
33
  const featureList = await chooseFeatures()
34
+
35
+ // 5. 解析功能
36
36
  const features = parseFeatures(featureList)
37
37
  const extraPlugins = parseExtraPlugins(featureList)
38
+ const enableHttps = featureList.includes('https') || false
38
39
 
40
+ // 6. 询问自动路由
39
41
  const autoRoute = await askAutoRoute(features.router)
40
- 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
+ }
41
49
  const runDev = await askRunDev(pkgCommands[pkgManager].dev)
42
50
 
43
- // 模板文件处理
44
- copyBaseTemplate(language, targetDir, __dirname)
45
- updateIndexHtml(projectName, targetDir)
46
- appendTailwind(extraPlugins, targetDir)
47
- copyOptionalTemplates(features, extraPlugins, language, targetDir, __dirname)
51
+ // 8. 复制基础模板
52
+ await copyBaseTemplate(language, targetDir, __dirname)
48
53
 
49
- // 生成 main 文件
50
- await generateMainFile(features, extraPlugins, language, targetDir)
54
+ // 9. 更新 index.html
55
+ await updateIndexHtml(projectName, targetDir)
51
56
 
52
- // package.json
53
- generatePackageJson(projectName, features, extraPlugins, autoRoute, enableHttps, language, targetDir, pkgManager)
57
+ // 10. 配置插件,并获取已使用的占位符
58
+ const unusedPlaceholders = await setupPlugins(features, extraPlugins, {
59
+ language,
60
+ targetDir,
61
+ autoRoute,
62
+ enableHttps,
63
+ __dirname
64
+ })
54
65
 
55
- // 配置 vite
56
- configureVite(language, autoRoute, enableHttps, targetDir)
66
+ // 11. 清理 main 文件中未使用的占位符
67
+ await cleanMainFile(language, targetDir, unusedPlaceholders)
57
68
 
58
- // 配置 router
59
- configureRouter(features.router, autoRoute, language, targetDir)
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
  }
@@ -0,0 +1,74 @@
1
+ // lib/plugins/autoRoute.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function setupAutoRoute (language, targetDir) {
7
+ console.log(' 🚀 配置自动路由...')
8
+
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
+
17
+ const content = `import { createRouter, createWebHistory } from 'vue-router'
18
+ import routes from '~pages'
19
+
20
+ routes.unshift({ path: '/', redirect: '/home' })
21
+
22
+ export default createRouter({ history: createWebHistory(), routes })`
23
+
24
+ await fs.writeFile(routerIndexPath, content)
25
+ console.log(' ✅ router/index.js 已更新')
26
+
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
31
+
32
+ if(!viteConfigPath || !existsSync(viteConfigPath)) {
33
+ throw new Error(`vite.config 文件不存在`)
34
+ }
35
+
36
+ let viteConfig = await fs.readFile(viteConfigPath, 'utf-8')
37
+
38
+ if(!viteConfig.includes("import fs from 'fs'")) {
39
+ viteConfig = `import fs from 'fs'\n${viteConfig}`
40
+ }
41
+
42
+ if(!viteConfig.includes("vite-plugin-pages")) {
43
+ viteConfig = viteConfig.replace(/(import .*?from .*?\n)/, `$1import Pages from 'vite-plugin-pages'\n`)
44
+ }
45
+
46
+ if(!viteConfig.includes("Pages({")) {
47
+ viteConfig = viteConfig.replace(
48
+ /plugins:\s*\[/,
49
+ `plugins: [\n Pages({
50
+ dirs: 'src/views',
51
+ extensions: ['vue'],
52
+ exclude: ['**/_*.vue'],
53
+ async extendRoute(route) {
54
+ const componentPath = path.resolve(process.cwd(), route.component.slice(1))
55
+ const dirPath = path.dirname(componentPath)
56
+ const metaFile = path.resolve(dirPath, 'meta.json')
57
+ if(fs.existsSync(metaFile)) {
58
+ try {
59
+ const metaContent = fs.readFileSync(metaFile, 'utf-8')
60
+ const meta = JSON.parse(metaContent)
61
+ route.meta = { ...(route.meta || {}), ...meta }
62
+ } catch(err) {
63
+ console.warn(\`加载 \${metaFile} 失败:\`, err)
64
+ }
65
+ }
66
+ return { ...route }
67
+ }
68
+ }),`
69
+ )
70
+ }
71
+
72
+ await fs.writeFile(viteConfigPath, viteConfig)
73
+ console.log(' ✅ vite.config 已更新')
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
+ }
@@ -0,0 +1,33 @@
1
+ // lib/plugins/https.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
6
+ export async function setupHttps (targetDir) {
7
+ console.log(' 🔐 配置 HTTPS...')
8
+
9
+ const viteConfigPathTs = path.join(targetDir, 'vite.config.ts')
10
+ const viteConfigPathJs = path.join(targetDir, 'vite.config.js')
11
+ const viteConfigPath = existsSync(viteConfigPathTs) ? viteConfigPathTs : viteConfigPathJs
12
+
13
+ if(!viteConfigPath || !existsSync(viteConfigPath)) {
14
+ throw new Error(`vite.config 文件不存在`)
15
+ }
16
+
17
+ let viteConfig = await fs.readFile(viteConfigPath, 'utf-8')
18
+
19
+ // 注入 import
20
+ if(!viteConfig.includes("vite-plugin-mkcert")) {
21
+ viteConfig = viteConfig.replace(/(import .*?from .*?\n)/, `$1import mkcert from 'vite-plugin-mkcert'\n`)
22
+ }
23
+
24
+ // 注入插件
25
+ if(!viteConfig.includes('mkcert()')) {
26
+ viteConfig = viteConfig.replace(/plugins:\s*\[/, `plugins: [\n mkcert(),`)
27
+ }
28
+
29
+ await fs.writeFile(viteConfigPath, viteConfig)
30
+ console.log(' ✅ vite.config 已更新')
31
+ console.log('🔐 已启用 HTTPS(mkcert)')
32
+ console.log('👉 首次运行会自动生成本地证书,请稍等...')
33
+ }
@@ -0,0 +1,71 @@
1
+ // lib/plugins/index.js
2
+ import { setupAutoRoute } from './autoRoute.js'
3
+ import { setupAxios } from './axios.js'
4
+ import { setupElementPlus } from './elementPlus.js'
5
+ import { setupHttps } from './https.js'
6
+ import { setupPinia } from './pinia.js'
7
+ import { setupRouter } from './router.js'
8
+ import { setupTailwind } from './tailwind.js'
9
+ import { setupVant } from './vant.js'
10
+
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
+ }
59
+
60
+ // Tailwind
61
+ if(extraPlugins.includes('tailwind')) {
62
+ await setupTailwind(language, targetDir, __dirname)
63
+ }
64
+
65
+ // HTTPS
66
+ if(enableHttps) {
67
+ await setupHttps(targetDir)
68
+ }
69
+
70
+ return unusedPlaceholders
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
+ }
@@ -0,0 +1,31 @@
1
+ // lib/plugins/tailwind.js
2
+ import { existsSync } from 'fs'
3
+ import fs from 'fs/promises'
4
+ import path from 'path'
5
+
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
+ }
16
+
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
+ }
25
+
26
+ const original = await fs.readFile(stylePath, 'utf-8')
27
+ if(!original.includes('@import "tailwindcss";')) {
28
+ await fs.writeFile(stylePath, `@import "tailwindcss";\n${original}`)
29
+ console.log(' ✅ style.css 已更新')
30
+ }
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,38 +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
- }
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
- }
8
+ const templatePath = path.resolve(__dirname, '../template', baseTemplate)
16
9
 
17
- export function appendTailwind (extraPlugins, targetDir) {
18
- if(!extraPlugins.includes('tailwind')) return
19
- const stylePath = path.join(targetDir, 'src/style.css')
20
- const original = fs.readFileSync(stylePath, 'utf-8')
21
- if(!original.startsWith('@import "tailwindcss";')) {
22
- fs.writeFileSync(stylePath, `@import "tailwindcss";\n${original}`)
10
+ if(!existsSync(templatePath)) {
11
+ throw new Error(`基础模板不存在: ${templatePath}`)
23
12
  }
24
- }
25
13
 
26
- export function copyOptionalTemplates (features, extraPlugins, language, targetDir, __dirname) {
27
- const copy = name => {
28
- 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)
29
23
  }
30
- features.router && copy(language === 'ts' ? 'router-ts' : 'router-js')
31
- features.pinia && copy(language === 'ts' ? 'pinia-ts' : 'pinia-js')
32
- features.axios && copy(language === 'ts' ? 'axios-ts' : 'axios-js')
33
- for(const plugin of extraPlugins) {
34
- const templateName = `${plugin}-${language === 'ts' ? 'ts' : 'js'}`
35
- const templatePath = path.resolve(__dirname, `../template/${templateName}`)
36
- 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)
37
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 更新完成')
38
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vite-vue",
3
- "version": "1.7.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
- }
package/lib/router.js DELETED
@@ -1,21 +0,0 @@
1
- // lib/router.js
2
- import fs from 'fs'
3
- import path from 'path'
4
-
5
- export function configureRouter (routerEnabled, autoRoute, language, targetDir) {
6
- if(!routerEnabled) return
7
- const routerIndexPath = path.join(targetDir, `src/router/index.${language === 'ts' ? 'ts' : 'js'}`)
8
- const content = autoRoute
9
- ? `import { createRouter, createWebHistory } from 'vue-router'
10
- import routes from '~pages'
11
-
12
- routes.unshift({ path: '/', redirect: '/home' })
13
-
14
- export default createRouter({ history: createWebHistory(), routes })`
15
- : `import { createRouter, createWebHistory } from 'vue-router'
16
-
17
- const routes = [ { path: '/', component: () => import('@/views/home/index.vue') } ]
18
-
19
- export default createRouter({ history: createWebHistory(), routes })`
20
- fs.writeFileSync(routerIndexPath, content)
21
- }
package/lib/viteConfig.js DELETED
@@ -1,24 +0,0 @@
1
- // lib/viteConfig.js
2
- import fs from 'fs'
3
- import path from 'path'
4
-
5
- export function configureVite (language, autoRoute, enableHttps, targetDir) {
6
- const viteConfigPath = path.join(targetDir, `vite.config.${language === 'ts' ? 'ts' : 'js'}`)
7
- if(!fs.existsSync(viteConfigPath)) return
8
- let viteConfig = fs.readFileSync(viteConfigPath, 'utf-8')
9
-
10
- // mkcert
11
- if(enableHttps && !viteConfig.includes("vite-plugin-mkcert")) {
12
- viteConfig = viteConfig.replace(/(import .*?from .*?\n)/, `$1import mkcert from 'vite-plugin-mkcert'\n`)
13
- viteConfig = viteConfig.replace(/plugins:\s*\[/, `plugins: [\n mkcert(),`)
14
- }
15
-
16
- // 自动路由
17
- if(autoRoute) {
18
- if(!viteConfig.includes("import fs from 'fs'")) viteConfig = `import fs from 'fs'\n${viteConfig}`
19
- if(!viteConfig.includes("import Pages from 'vite-plugin-pages'")) viteConfig = viteConfig.replace(/(import .*?from .*?\n)/, `$1import Pages from 'vite-plugin-pages'\n`)
20
- if(!viteConfig.includes("Pages({")) viteConfig = viteConfig.replace(/plugins:\s*\[/, `plugins: [\n Pages({\n dirs: 'src/views',\n extensions: ['vue'],\n exclude: ['**/_*.vue'],\n async extendRoute(route) {\n const componentPath = path.resolve(process.cwd(), route.component.slice(1))\n const dirPath = path.dirname(componentPath)\n const metaFile = path.resolve(dirPath, 'meta.json')\n if(fs.existsSync(metaFile)) {\n try {\n const metaContent = fs.readFileSync(metaFile, 'utf-8')\n const meta = JSON.parse(metaContent)\n route.meta = { ...(route.meta || {}), ...meta }\n } catch(err) {\n console.warn(\`加载 \${metaFile} 失败:\`, err)\n }\n }\n return { ...route }\n }\n }),`)
21
- }
22
-
23
- fs.writeFileSync(viteConfigPath, viteConfig)
24
- }