create-vite-vue 1.8.0 → 2.1.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/index.js +44 -31
- package/lib/cleanMain.js +36 -0
- package/lib/features.js +18 -6
- package/lib/package.js +34 -22
- package/lib/plugins/autoRoute.js +24 -29
- package/lib/plugins/axios.js +19 -0
- package/lib/plugins/elementPlus.js +28 -0
- package/lib/plugins/https.js +16 -21
- package/lib/plugins/index.js +58 -10
- package/lib/plugins/pinia.js +36 -0
- package/lib/plugins/router.js +32 -0
- package/lib/plugins/tailwind.js +23 -6
- package/lib/plugins/vant.js +25 -0
- package/lib/prompts.js +1 -1
- package/lib/template.js +34 -20
- package/package.json +8 -1
- package/lib/mainFile.js +0 -48
package/bin/index.js
CHANGED
|
@@ -1,69 +1,82 @@
|
|
|
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
|
// === 导入模块 ===
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { cleanMainFile } from '../lib/cleanMain.js'
|
|
9
|
+
import { parsePlugins } from '../lib/features.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,
|
|
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
|
|
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()
|
|
35
|
-
const features = parseFeatures(featureList)
|
|
36
|
-
const extraPlugins = parseExtraPlugins(featureList)
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
const
|
|
35
|
+
// 5. 解析插件
|
|
36
|
+
const plugins = parsePlugins(featureList)
|
|
37
|
+
const enableHttps = plugins.https
|
|
38
|
+
|
|
39
|
+
// 6. 询问自动路由
|
|
40
|
+
const autoRoute = await askAutoRoute(plugins.router)
|
|
41
|
+
|
|
42
|
+
// 7. 询问是否运行 dev
|
|
43
|
+
const pkgManager = detectPackageManager()
|
|
44
|
+
const pkgCommands = {
|
|
45
|
+
npm: { install: 'npm install', dev: 'npm run dev' },
|
|
46
|
+
pnpm: { install: 'pnpm install', dev: 'pnpm dev' }
|
|
47
|
+
}
|
|
40
48
|
const runDev = await askRunDev(pkgCommands[pkgManager].dev)
|
|
41
49
|
|
|
42
|
-
//
|
|
43
|
-
copyBaseTemplate(language, targetDir, __dirname)
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
// 8. 复制基础模板
|
|
51
|
+
await copyBaseTemplate(language, targetDir, __dirname)
|
|
52
|
+
|
|
53
|
+
// 9. 更新 index.html
|
|
54
|
+
await updateIndexHtml(projectName, targetDir)
|
|
46
55
|
|
|
47
|
-
//
|
|
48
|
-
setupPlugins(
|
|
56
|
+
// 10. 配置插件,并获取已使用的占位符
|
|
57
|
+
const unusedPlaceholders = await setupPlugins(plugins, {
|
|
49
58
|
language,
|
|
50
59
|
targetDir,
|
|
51
60
|
autoRoute,
|
|
52
|
-
enableHttps
|
|
61
|
+
enableHttps,
|
|
62
|
+
__dirname
|
|
53
63
|
})
|
|
54
64
|
|
|
55
|
-
//
|
|
56
|
-
await
|
|
65
|
+
// 11. 清理 main 文件中未使用的占位符
|
|
66
|
+
await cleanMainFile(language, targetDir, unusedPlaceholders)
|
|
57
67
|
|
|
58
|
-
// package.json
|
|
59
|
-
generatePackageJson(projectName,
|
|
68
|
+
// 12. 生成 package.json
|
|
69
|
+
await generatePackageJson(projectName, plugins, autoRoute, enableHttps, language, targetDir, pkgManager)
|
|
60
70
|
|
|
61
|
-
// 安装依赖
|
|
71
|
+
// 13. 安装依赖
|
|
72
|
+
console.log('\n📦 正在安装依赖...')
|
|
62
73
|
runCmd(pkgCommands[pkgManager].install, targetDir)
|
|
63
74
|
|
|
64
|
-
// 启动 dev 或提示完成
|
|
65
|
-
if(runDev)
|
|
66
|
-
|
|
75
|
+
// 14. 启动 dev 或提示完成
|
|
76
|
+
if(runDev) {
|
|
77
|
+
console.log('\n🚀 启动开发服务器...')
|
|
78
|
+
runCmd(pkgCommands[pkgManager].dev, targetDir)
|
|
79
|
+
} else {
|
|
67
80
|
console.log(`\n✅ 项目创建完成`)
|
|
68
81
|
console.log(`👉 cd ${projectName}`)
|
|
69
82
|
console.log(`👉 ${pkgCommands[pkgManager].dev}`)
|
package/lib/cleanMain.js
ADDED
|
@@ -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/features.js
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
// lib/features.js
|
|
2
|
-
export function
|
|
2
|
+
export function parsePlugins (featureList) {
|
|
3
3
|
return {
|
|
4
|
+
// 核心插件
|
|
4
5
|
router: featureList.includes('router'),
|
|
5
6
|
pinia: featureList.includes('pinia'),
|
|
6
7
|
axios: featureList.includes('axios'),
|
|
7
|
-
ui: featureList.filter(v => ['element', 'vant'].includes(v))
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// UI 插件
|
|
10
|
+
elementPlus: featureList.includes('element'),
|
|
11
|
+
vant: featureList.includes('vant'),
|
|
12
|
+
|
|
13
|
+
// 工具插件
|
|
14
|
+
vueuse: featureList.includes('vueuse'),
|
|
15
|
+
lodash: featureList.includes('lodash'),
|
|
16
|
+
dayjs: featureList.includes('dayjs'),
|
|
17
|
+
mitt: featureList.includes('mitt'),
|
|
18
|
+
|
|
19
|
+
// 样式插件
|
|
20
|
+
tailwind: featureList.includes('tailwind'),
|
|
21
|
+
|
|
22
|
+
// 开发工具
|
|
23
|
+
https: featureList.includes('https')
|
|
24
|
+
}
|
|
13
25
|
}
|
package/lib/package.js
CHANGED
|
@@ -1,45 +1,57 @@
|
|
|
1
1
|
// lib/package.js
|
|
2
|
-
import
|
|
2
|
+
import { existsSync } from 'fs'
|
|
3
|
+
import fs from 'fs/promises'
|
|
3
4
|
import path from 'path'
|
|
5
|
+
export async function generatePackageJson (projectName, plugins, autoRoute, enableHttps, language, targetDir, pkgManager) {
|
|
6
|
+
const pkgPath = path.join(targetDir, 'package.json')
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
if(!existsSync(pkgPath)) {
|
|
9
|
+
throw new Error(`package.json 不存在: ${pkgPath}`)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let pkgContent = await fs.readFile(pkgPath, 'utf-8')
|
|
9
13
|
|
|
10
14
|
const optionalDeps = {}
|
|
11
|
-
|
|
12
|
-
if(
|
|
15
|
+
|
|
16
|
+
if(plugins.router) optionalDeps['vue-router'] = '^5.0.3'
|
|
17
|
+
if(plugins.pinia) {
|
|
13
18
|
optionalDeps['pinia'] = '^3.0.4'
|
|
14
19
|
optionalDeps['pinia-plugin-persistedstate'] = '^4.7.1'
|
|
15
20
|
}
|
|
16
|
-
if(
|
|
17
|
-
if(
|
|
21
|
+
if(plugins.axios) optionalDeps['axios'] = '^1.13.6'
|
|
22
|
+
if(plugins.elementPlus) {
|
|
18
23
|
optionalDeps['element-plus'] = '^2.13.5'
|
|
19
24
|
optionalDeps['@element-plus/icons-vue'] = '^2.3.2'
|
|
20
25
|
}
|
|
21
|
-
if(
|
|
22
|
-
if(
|
|
23
|
-
if(
|
|
24
|
-
if(
|
|
25
|
-
if(
|
|
26
|
+
if(plugins.vant) optionalDeps['vant'] = '^4.9.22'
|
|
27
|
+
if(plugins.vueuse) optionalDeps['@vueuse/core'] = '^14.2.1'
|
|
28
|
+
if(plugins.dayjs) optionalDeps['dayjs'] = '^1.11.20'
|
|
29
|
+
if(plugins.lodash) optionalDeps['lodash'] = '^4.17.23'
|
|
30
|
+
if(plugins.tailwind) {
|
|
26
31
|
optionalDeps['tailwindcss'] = '^4.2.2'
|
|
27
32
|
optionalDeps['@tailwindcss/postcss'] = '^4.2.2'
|
|
28
33
|
optionalDeps['postcss'] = '^8.5.8'
|
|
29
34
|
}
|
|
30
|
-
if(
|
|
35
|
+
if(plugins.mitt) optionalDeps['mitt'] = '^3.0.1'
|
|
31
36
|
if(enableHttps) optionalDeps['vite-plugin-mkcert'] = '^1.17.10'
|
|
32
37
|
if(autoRoute) optionalDeps['vite-plugin-pages'] = '^0.33.3'
|
|
33
38
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
const depsKeys = Object.keys(optionalDeps)
|
|
40
|
+
let depsStr = ''
|
|
41
|
+
|
|
42
|
+
if(depsKeys.length > 0) {
|
|
43
|
+
depsStr = ',\n' + depsKeys.map(k => ` "${k}": "${optionalDeps[k]}"`).join(',\n')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pkgContent = pkgContent.replace('__PROJECT_NAME__', projectName)
|
|
47
|
+
pkgContent = pkgContent.replace('__OPTIONAL_DEP__', depsStr)
|
|
48
|
+
|
|
49
|
+
const pkgObj = JSON.parse(pkgContent)
|
|
37
50
|
|
|
38
|
-
|
|
39
|
-
if(pkgManager === 'pnpm' && features.ui.includes('vant')) {
|
|
51
|
+
if(pkgManager === 'pnpm' && plugins.vant) {
|
|
40
52
|
pkgObj.pnpm = { overrides: { "@vant/use": "^1.0.0", "@vant/popperjs": "^1.0.0" } }
|
|
41
53
|
}
|
|
42
54
|
|
|
43
|
-
fs.
|
|
44
|
-
|
|
55
|
+
await fs.writeFile(pkgPath, JSON.stringify(pkgObj, null, 2))
|
|
56
|
+
console.log(' ✅ package.json 生成完成')
|
|
45
57
|
}
|
package/lib/plugins/autoRoute.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
// lib/plugins/autoRoute.js
|
|
2
|
-
import
|
|
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
|
-
|
|
7
|
-
setupVite(language, targetDir)
|
|
8
|
-
}
|
|
6
|
+
export async function setupAutoRoute (language, targetDir) {
|
|
7
|
+
console.log(' 🚀 配置自动路由...')
|
|
9
8
|
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.
|
|
25
|
-
|
|
24
|
+
await fs.writeFile(routerIndexPath, content)
|
|
25
|
+
console.log(' ✅ router/index.js 已更新')
|
|
26
26
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
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(!
|
|
32
|
+
if(!viteConfigPath || !existsSync(viteConfigPath)) {
|
|
33
|
+
throw new Error(`vite.config 文件不存在`)
|
|
34
|
+
}
|
|
35
35
|
|
|
36
|
-
let viteConfig = fs.
|
|
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.
|
|
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
|
+
}
|
package/lib/plugins/https.js
CHANGED
|
@@ -1,38 +1,33 @@
|
|
|
1
1
|
// lib/plugins/https.js
|
|
2
|
-
import
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
17
|
+
let viteConfig = await fs.readFile(viteConfigPath, 'utf-8')
|
|
16
18
|
|
|
17
|
-
//
|
|
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
|
-
//
|
|
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.
|
|
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
|
}
|
package/lib/plugins/index.js
CHANGED
|
@@ -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 (
|
|
7
|
-
const { language, targetDir, autoRoute, enableHttps } = context
|
|
11
|
+
export async function setupPlugins (plugins, 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(plugins.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(plugins.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(plugins.elementPlus) {
|
|
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(plugins.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(plugins.axios) {
|
|
57
|
+
await setupAxios(language, targetDir, __dirname)
|
|
58
|
+
}
|
|
8
59
|
|
|
9
60
|
// Tailwind
|
|
10
|
-
if(
|
|
11
|
-
setupTailwind(targetDir)
|
|
61
|
+
if(plugins.tailwind) {
|
|
62
|
+
await setupTailwind(language, targetDir, __dirname)
|
|
12
63
|
}
|
|
13
64
|
|
|
14
65
|
// HTTPS
|
|
15
|
-
if(
|
|
16
|
-
setupHttps(targetDir)
|
|
66
|
+
if(plugins.https) {
|
|
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
|
+
}
|
package/lib/plugins/tailwind.js
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
// lib/plugins/tailwind.js
|
|
2
|
-
import
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
8
|
-
}
|
|
8
|
+
const templatePath = path.resolve(__dirname, '../template', baseTemplate)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "基于Vite+Vue3创建基础项目模板",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "YwaiX",
|
|
@@ -22,6 +22,13 @@
|
|
|
22
22
|
"pinia",
|
|
23
23
|
"axios"
|
|
24
24
|
],
|
|
25
|
+
"files": [
|
|
26
|
+
"bin/",
|
|
27
|
+
"lib/",
|
|
28
|
+
"template/",
|
|
29
|
+
"package.json",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
25
32
|
"dependencies": {
|
|
26
33
|
"prompts": "^2.4.2"
|
|
27
34
|
}
|
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
|
-
}
|