create-vite-vue 1.6.1 → 1.7.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 -426
- package/lib/features.js +13 -0
- package/lib/mainFile.js +48 -0
- package/lib/package.js +45 -0
- package/lib/prompts.js +80 -0
- package/lib/router.js +21 -0
- package/lib/template.js +38 -0
- package/lib/utils.js +38 -0
- package/lib/viteConfig.js +24 -0
- package/package.json +1 -1
- package/template/axios-ts/src/utils/requestCache.ts +7 -0
- package/template/base-js/jsconfig.json +1 -0
- package/template/base-ts/tsconfig.json +1 -0
package/bin/index.js
CHANGED
|
@@ -1,454 +1,72 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { execSync } from 'child_process'
|
|
1
|
+
#!/usr/bin/env node
|
|
3
2
|
import fs from 'fs'
|
|
4
3
|
import path from 'path'
|
|
5
|
-
import prompts from 'prompts'
|
|
6
4
|
import { fileURLToPath } from 'url'
|
|
7
5
|
|
|
8
|
-
//
|
|
6
|
+
// === 导入模块 ===
|
|
7
|
+
import { parseExtraPlugins, parseFeatures } from '../lib/features.js'
|
|
8
|
+
import { generateMainFile } from '../lib/mainFile.js'
|
|
9
|
+
import { generatePackageJson } from '../lib/package.js'
|
|
10
|
+
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 { checkNodeVersion, detectPackageManager, runCmd } from '../lib/utils.js'
|
|
14
|
+
import { configureVite } from '../lib/viteConfig.js'
|
|
15
|
+
|
|
16
|
+
// ===================== 常量 =====================
|
|
9
17
|
const requiredVersion = '22.19.0'
|
|
10
|
-
|
|
11
|
-
function compareVersion (v1, v2) {
|
|
12
|
-
const a = v1.split('.').map(Number)
|
|
13
|
-
const b = v2.split('.').map(Number)
|
|
14
|
-
|
|
15
|
-
for(let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
16
|
-
const n1 = a[i] || 0
|
|
17
|
-
const n2 = b[i] || 0
|
|
18
|
-
if(n1 > n2) return 1
|
|
19
|
-
if(n1 < n2) return -1
|
|
20
|
-
}
|
|
21
|
-
return 0
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const currentVersion = process.version.replace('v', '')
|
|
25
|
-
|
|
26
|
-
if(compareVersion(currentVersion, requiredVersion) < 0) {
|
|
27
|
-
console.error(`❌ Node.js 版本过低`)
|
|
28
|
-
console.error(`当前版本: ${currentVersion}`)
|
|
29
|
-
console.error(`最低要求: ${requiredVersion}`)
|
|
30
|
-
console.error(`请升级 Node.js 后再运行`)
|
|
31
|
-
process.exit(1)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
18
|
const __filename = fileURLToPath(import.meta.url)
|
|
35
19
|
const __dirname = path.dirname(__filename)
|
|
36
|
-
|
|
37
20
|
const pkgManager = detectPackageManager()
|
|
38
21
|
|
|
39
22
|
const pkgCommands = {
|
|
40
|
-
npm: {
|
|
41
|
-
|
|
42
|
-
dev: 'npm run dev'
|
|
43
|
-
},
|
|
44
|
-
pnpm: {
|
|
45
|
-
install: 'pnpm install',
|
|
46
|
-
dev: 'pnpm dev'
|
|
47
|
-
},
|
|
48
|
-
bun: {
|
|
49
|
-
install: 'bun install',
|
|
50
|
-
dev: 'bun run dev'
|
|
51
|
-
}
|
|
23
|
+
npm: { install: 'npm install', dev: 'npm run dev' },
|
|
24
|
+
pnpm: { install: 'pnpm install', dev: 'pnpm dev' }
|
|
52
25
|
}
|
|
53
26
|
|
|
27
|
+
// ===================== 主流程 =====================
|
|
54
28
|
; (async () => {
|
|
55
|
-
|
|
56
|
-
let projectName
|
|
57
|
-
while(true) {
|
|
58
|
-
const res = await prompts({
|
|
59
|
-
type: 'text',
|
|
60
|
-
name: 'projectName',
|
|
61
|
-
message: '📦 项目名称',
|
|
62
|
-
validate: v => v ? true : '项目名不能为空'
|
|
63
|
-
})
|
|
64
|
-
projectName = res.projectName
|
|
65
|
-
if(!projectName) process.exit(1)
|
|
66
|
-
|
|
67
|
-
const targetDir = path.resolve(process.cwd(), projectName)
|
|
68
|
-
if(fs.existsSync(targetDir)) {
|
|
69
|
-
console.log('❌ 目录已存在,请重新输入')
|
|
70
|
-
continue
|
|
71
|
-
}
|
|
72
|
-
break
|
|
73
|
-
}
|
|
29
|
+
checkNodeVersion(requiredVersion)
|
|
74
30
|
|
|
31
|
+
const projectName = await getProjectName(fs, path)
|
|
75
32
|
const targetDir = path.resolve(process.cwd(), projectName)
|
|
76
33
|
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
message: '请选择项目语言',
|
|
82
|
-
choices: [
|
|
83
|
-
{ title: 'JavaScript', value: 'js' },
|
|
84
|
-
{ title: 'TypeScript', value: 'ts' }
|
|
85
|
-
]
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
// 2️⃣ 功能选择(多选)
|
|
89
|
-
const { featureList } = await prompts({
|
|
90
|
-
type: 'multiselect',
|
|
91
|
-
name: 'featureList',
|
|
92
|
-
message: '请选择基础功能(↑↓选择,空格确认,回车完成)',
|
|
93
|
-
instructions: false,
|
|
94
|
-
choices: [
|
|
95
|
-
{ title: 'Vue Router', value: 'router' },
|
|
96
|
-
{ title: 'Pinia(含持久化)', value: 'pinia' },
|
|
97
|
-
{ title: 'Axios', value: 'axios' },
|
|
98
|
-
{ title: 'Element Plus(PC UI)', value: 'element' },
|
|
99
|
-
{ title: 'Vant(Mobile UI)', value: 'vant' },
|
|
100
|
-
{ title: 'VueUse(实用 Composition API)', value: 'vueuse' },
|
|
101
|
-
{ title: 'Lodash(工具库)', value: 'lodash' },
|
|
102
|
-
{ title: 'Day.js(日期处理)', value: 'dayjs' },
|
|
103
|
-
{ title: 'Tailwind CSS(原子化 CSS)', value: 'tailwind' },
|
|
104
|
-
{ title: 'mitt(事件总线)', value: 'mitt' },
|
|
105
|
-
{ title: 'HTTPS(mkcert)', value: 'https' }
|
|
106
|
-
]
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
// 转换成原来的结构(保证后面代码基本不用动)
|
|
110
|
-
const features = {
|
|
111
|
-
router: featureList?.includes('router') || false,
|
|
112
|
-
pinia: featureList?.includes('pinia') || false,
|
|
113
|
-
axios: featureList?.includes('axios') || false,
|
|
114
|
-
ui: featureList?.filter(v => ['element', 'vant'].includes(v)) || []
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const extraPlugins = featureList?.filter(v =>
|
|
118
|
-
['vueuse', 'lodash', 'dayjs', 'tailwind', 'mitt', 'https'].includes(v)
|
|
119
|
-
) || []
|
|
120
|
-
|
|
121
|
-
// 询问是否开启自动路由
|
|
122
|
-
let autoRoute = false
|
|
123
|
-
if(features.router) {
|
|
124
|
-
const { enableAutoRoute } = await prompts({
|
|
125
|
-
type: 'toggle',
|
|
126
|
-
name: 'enableAutoRoute',
|
|
127
|
-
message: '是否开启自动配置路由(vite-plugin-pages)?',
|
|
128
|
-
initial: false,
|
|
129
|
-
active: '是',
|
|
130
|
-
inactive: '否'
|
|
131
|
-
})
|
|
132
|
-
autoRoute = enableAutoRoute
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const enableHttps = featureList?.includes('https') || false
|
|
136
|
-
|
|
137
|
-
// 3️⃣ 是否立即运行 dev
|
|
138
|
-
const { runDev } = await prompts({
|
|
139
|
-
type: 'select',
|
|
140
|
-
name: 'runDev',
|
|
141
|
-
message: `是否立即运行 ${pkgCommands[pkgManager].dev}?`,
|
|
142
|
-
choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
// 4️⃣ 拷贝 base 模板
|
|
146
|
-
const baseTemplate = language === 'ts' ? 'base-ts' : 'base-js'
|
|
147
|
-
fs.cpSync(
|
|
148
|
-
path.resolve(__dirname, `../template/${baseTemplate}`),
|
|
149
|
-
targetDir,
|
|
150
|
-
{ recursive: true }
|
|
151
|
-
)
|
|
34
|
+
const language = await chooseLanguage()
|
|
35
|
+
const featureList = await chooseFeatures()
|
|
36
|
+
const features = parseFeatures(featureList)
|
|
37
|
+
const extraPlugins = parseExtraPlugins(featureList)
|
|
152
38
|
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
const indexContent = fs.readFileSync(indexPath, 'utf-8')
|
|
157
|
-
fs.writeFileSync(
|
|
158
|
-
indexPath,
|
|
159
|
-
indexContent.replace(/<title>.*<\/title>/, `<title>${projectName}</title>`)
|
|
160
|
-
)
|
|
161
|
-
}
|
|
39
|
+
const autoRoute = await askAutoRoute(features.router)
|
|
40
|
+
const enableHttps = featureList.includes('https') || false
|
|
41
|
+
const runDev = await askRunDev(pkgCommands[pkgManager].dev)
|
|
162
42
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
fs.writeFileSync(stylePath, `@import "tailwindcss";\n${original}`)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
43
|
+
// 模板文件处理
|
|
44
|
+
copyBaseTemplate(language, targetDir, __dirname)
|
|
45
|
+
updateIndexHtml(projectName, targetDir)
|
|
46
|
+
appendTailwind(extraPlugins, targetDir)
|
|
47
|
+
copyOptionalTemplates(features, extraPlugins, language, targetDir, __dirname)
|
|
171
48
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
fs.cpSync(path.resolve(__dirname, `../template/${name}`), targetDir, { recursive: true })
|
|
175
|
-
}
|
|
176
|
-
features.router && copy(language === 'ts' ? 'router-ts' : 'router-js')
|
|
177
|
-
features.pinia && copy(language === 'ts' ? 'pinia-ts' : 'pinia-js')
|
|
178
|
-
features.axios && copy(language === 'ts' ? 'axios-ts' : 'axios-js')
|
|
49
|
+
// 生成 main 文件
|
|
50
|
+
await generateMainFile(features, extraPlugins, language, targetDir)
|
|
179
51
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
const templateName = `${plugin}-${language === 'ts' ? 'ts' : 'js'}`
|
|
183
|
-
const templatePath = path.resolve(__dirname, `../template/${templateName}`)
|
|
184
|
-
if(fs.existsSync(templatePath)) {
|
|
185
|
-
fs.cpSync(templatePath, targetDir, { recursive: true })
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// 6️⃣ 生成 main.js / main.ts
|
|
190
|
-
const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
|
|
191
|
-
const mainTplPath = path.join(targetDir, `src/${mainFile}.tpl`)
|
|
192
|
-
let main = fs.readFileSync(mainTplPath, 'utf-8')
|
|
193
|
-
|
|
194
|
-
const replacements = {
|
|
195
|
-
'/* __ROUTER_IMPORT__ */': features.router ? "import router from './router'" : '',
|
|
196
|
-
'/* __PINIA_IMPORT__ */': features.pinia
|
|
197
|
-
? "import { createPinia } from 'pinia'\nimport persistedstate from 'pinia-plugin-persistedstate'"
|
|
198
|
-
: '',
|
|
199
|
-
'/* __ELEMENT_IMPORT__ */': features.ui.includes('element')
|
|
200
|
-
? `import ElementPlus from 'element-plus'
|
|
201
|
-
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
|
202
|
-
import 'element-plus/dist/index.css'
|
|
203
|
-
import * as ElementPlusIconsVue from '@element-plus/icons-vue'`
|
|
204
|
-
: '',
|
|
205
|
-
'/* __VANT_IMPORT__ */': features.ui.includes('vant')
|
|
206
|
-
? `import Vant from 'vant'
|
|
207
|
-
import 'vant/lib/index.css'`
|
|
208
|
-
: '',
|
|
209
|
-
'/* __ROUTER_USE__ */': features.router ? 'app.use(router)' : '',
|
|
210
|
-
'/* __PINIA_USE__ */': features.pinia
|
|
211
|
-
? 'app.use(createPinia().use(persistedstate))'
|
|
212
|
-
: '',
|
|
213
|
-
'/* __ELEMENT_USE__ */': features.ui.includes('element')
|
|
214
|
-
? `app.use(ElementPlus, { locale: zhCn })
|
|
215
|
-
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
|
216
|
-
app.component(key, component)
|
|
217
|
-
}`
|
|
218
|
-
: '',
|
|
219
|
-
'/* __VANT_USE__ */': features.ui.includes('vant')
|
|
220
|
-
? 'app.use(Vant)'
|
|
221
|
-
: ''
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function escapeRegExp (str) {
|
|
225
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
for(const [placeholder, content] of Object.entries(replacements)) {
|
|
229
|
-
if(content) {
|
|
230
|
-
main = main.replace(placeholder, content)
|
|
231
|
-
} else {
|
|
232
|
-
const re = new RegExp(`^\\s*${escapeRegExp(placeholder)}\\s*$\\n?`, 'gm')
|
|
233
|
-
main = main.replace(re, '')
|
|
234
|
-
}
|
|
235
|
-
}
|
|
52
|
+
// package.json
|
|
53
|
+
generatePackageJson(projectName, features, extraPlugins, autoRoute, enableHttps, language, targetDir, pkgManager)
|
|
236
54
|
|
|
237
|
-
|
|
238
|
-
|
|
55
|
+
// 配置 vite
|
|
56
|
+
configureVite(language, autoRoute, enableHttps, targetDir)
|
|
239
57
|
|
|
240
|
-
|
|
241
|
-
|
|
58
|
+
// 配置 router
|
|
59
|
+
configureRouter(features.router, autoRoute, language, targetDir)
|
|
242
60
|
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
if(fs.existsSync(pkgTpl)) {
|
|
246
|
-
let pkg = fs.readFileSync(pkgTpl, 'utf-8')
|
|
61
|
+
// 安装依赖
|
|
62
|
+
runCmd(pkgCommands[pkgManager].install, targetDir)
|
|
247
63
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
optionalDeps['pinia'] = '^3.0.4'
|
|
252
|
-
optionalDeps['pinia-plugin-persistedstate'] = '^4.7.1'
|
|
253
|
-
}
|
|
254
|
-
if(features.axios) optionalDeps['axios'] = '^1.13.6'
|
|
255
|
-
if(features.ui.includes('element')) {
|
|
256
|
-
optionalDeps['element-plus'] = '^2.13.5'
|
|
257
|
-
optionalDeps['@element-plus/icons-vue'] = '^2.3.2'
|
|
258
|
-
}
|
|
259
|
-
if(features.ui.includes('vant')) {
|
|
260
|
-
optionalDeps['vant'] = '^4.9.22'
|
|
261
|
-
}
|
|
262
|
-
// 增强插件依赖
|
|
263
|
-
if(extraPlugins.includes('vueuse')) optionalDeps['@vueuse/core'] = '^14.2.1'
|
|
264
|
-
if(extraPlugins.includes('dayjs')) optionalDeps['dayjs'] = '^1.11.20'
|
|
265
|
-
if(extraPlugins.includes('lodash')) optionalDeps['lodash'] = '^4.17.23'
|
|
266
|
-
if(extraPlugins.includes('tailwind')) {
|
|
267
|
-
optionalDeps['tailwindcss'] = '^4.2.2'
|
|
268
|
-
optionalDeps['@tailwindcss/postcss'] = '^4.2.2'
|
|
269
|
-
optionalDeps['postcss'] = '^8.5.8'
|
|
270
|
-
}
|
|
271
|
-
if(extraPlugins.includes('mitt')) optionalDeps['mitt'] = '^3.0.1'
|
|
272
|
-
if(enableHttps) optionalDeps['vite-plugin-mkcert'] = '^1.17.10'
|
|
273
|
-
if(autoRoute) optionalDeps['vite-plugin-pages'] = '^0.33.3'
|
|
274
|
-
|
|
275
|
-
let depsStr = ''
|
|
276
|
-
const keys = Object.keys(optionalDeps)
|
|
277
|
-
if(keys.length > 0) {
|
|
278
|
-
depsStr = ',\n' + keys.map(k => ` "${k}": "${optionalDeps[k]}"`).join(',\n')
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
pkg = pkg
|
|
282
|
-
.replace('__PROJECT_NAME__', projectName)
|
|
283
|
-
.replace('__OPTIONAL_DEP__', depsStr)
|
|
284
|
-
|
|
285
|
-
// ⭐ 新增逻辑
|
|
286
|
-
let pkgObj = JSON.parse(pkg)
|
|
287
|
-
|
|
288
|
-
if(pkgManager === 'pnpm' && features.ui.includes('vant')) {
|
|
289
|
-
pkgObj.pnpm = {
|
|
290
|
-
overrides: {
|
|
291
|
-
"@vant/use": "^1.0.0",
|
|
292
|
-
"@vant/popperjs": "^1.0.0"
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
pkg = JSON.stringify(pkgObj, null, 2)
|
|
298
|
-
|
|
299
|
-
fs.writeFileSync(path.join(targetDir, 'package.json'), pkg)
|
|
300
|
-
fs.unlinkSync(pkgTpl)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// 8️⃣ 配置 vite.config.js / vite.config.ts(自动路由 + HTTPS)
|
|
304
|
-
const viteConfigPath = path.join(
|
|
305
|
-
targetDir,
|
|
306
|
-
`vite.config.${language === 'ts' ? 'ts' : 'js'}`
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
if(fs.existsSync(viteConfigPath)) {
|
|
310
|
-
let viteConfig = fs.readFileSync(viteConfigPath, 'utf-8')
|
|
311
|
-
|
|
312
|
-
// ===== mkcert 插件 =====
|
|
313
|
-
if(enableHttps) {
|
|
314
|
-
if(!viteConfig.includes("vite-plugin-mkcert")) {
|
|
315
|
-
viteConfig = viteConfig.replace(
|
|
316
|
-
/(import .*?from .*?\n)/,
|
|
317
|
-
`$1import mkcert from 'vite-plugin-mkcert'\n`
|
|
318
|
-
)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if(!viteConfig.includes("mkcert(")) {
|
|
322
|
-
viteConfig = viteConfig.replace(
|
|
323
|
-
/plugins:\s*\[/,
|
|
324
|
-
`plugins: [
|
|
325
|
-
mkcert(),`
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ===== 自动路由 =====
|
|
331
|
-
if(autoRoute) {
|
|
332
|
-
if(!viteConfig.includes("import fs from 'fs'")) {
|
|
333
|
-
viteConfig = `import fs from 'fs'\n${viteConfig}`
|
|
334
|
-
}
|
|
335
|
-
if(!viteConfig.includes("import Pages from 'vite-plugin-pages'")) {
|
|
336
|
-
viteConfig = viteConfig.replace(
|
|
337
|
-
/(import .*?from .*?\n)/,
|
|
338
|
-
`$1import Pages from 'vite-plugin-pages'\n`
|
|
339
|
-
)
|
|
340
|
-
}
|
|
341
|
-
if(!viteConfig.includes("Pages({")) {
|
|
342
|
-
viteConfig = viteConfig.replace(
|
|
343
|
-
/plugins:\s*\[/,
|
|
344
|
-
`plugins: [
|
|
345
|
-
Pages({
|
|
346
|
-
dirs: 'src/views',
|
|
347
|
-
extensions: ['vue'],
|
|
348
|
-
exclude: ['**/_*.vue'],
|
|
349
|
-
async extendRoute(route) {
|
|
350
|
-
const componentPath = path.resolve(process.cwd(), route.component.slice(1))
|
|
351
|
-
const dirPath = path.dirname(componentPath)
|
|
352
|
-
const metaFile = path.resolve(dirPath, 'meta.json')
|
|
353
|
-
if(fs.existsSync(metaFile)) {
|
|
354
|
-
try {
|
|
355
|
-
const metaContent = fs.readFileSync(metaFile, 'utf-8')
|
|
356
|
-
const meta = JSON.parse(metaContent)
|
|
357
|
-
route.meta = { ...(route.meta || {}), ...meta }
|
|
358
|
-
} catch(err) {
|
|
359
|
-
console.warn(\`加载 \${metaFile} 失败:\`, err)
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
return { ...route }
|
|
363
|
-
}
|
|
364
|
-
}),`
|
|
365
|
-
)
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
fs.writeFileSync(viteConfigPath, viteConfig)
|
|
370
|
-
}
|
|
371
|
-
// 9️⃣ 替换 router/index.js
|
|
372
|
-
if(features.router) {
|
|
373
|
-
const routerIndexPath = path.join(targetDir, `src/router/index.${language === 'ts' ? 'ts' : 'js'}`)
|
|
374
|
-
if(autoRoute) {
|
|
375
|
-
fs.writeFileSync(
|
|
376
|
-
routerIndexPath,
|
|
377
|
-
`import { createRouter, createWebHistory } from 'vue-router'
|
|
378
|
-
import routes from '~pages'
|
|
379
|
-
|
|
380
|
-
routes.unshift({
|
|
381
|
-
path: '/',
|
|
382
|
-
redirect: '/home'
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
export default createRouter({
|
|
386
|
-
history: createWebHistory(),
|
|
387
|
-
routes
|
|
388
|
-
})
|
|
389
|
-
`
|
|
390
|
-
)
|
|
391
|
-
} else {
|
|
392
|
-
fs.writeFileSync(
|
|
393
|
-
routerIndexPath,
|
|
394
|
-
`import { createRouter, createWebHistory } from 'vue-router'
|
|
395
|
-
|
|
396
|
-
const routes = [
|
|
397
|
-
{
|
|
398
|
-
path: '/',
|
|
399
|
-
component: () => import('@/views/home/index.vue')
|
|
400
|
-
}
|
|
401
|
-
]
|
|
402
|
-
|
|
403
|
-
export default createRouter({
|
|
404
|
-
history: createWebHistory(),
|
|
405
|
-
routes
|
|
406
|
-
})
|
|
407
|
-
`
|
|
408
|
-
)
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// 1️⃣0️⃣ 安装依赖
|
|
413
|
-
console.log('📦 安装依赖中...')
|
|
414
|
-
|
|
415
|
-
let installCmd = ''
|
|
416
|
-
|
|
417
|
-
if(pkgManager === 'pnpm') {
|
|
418
|
-
installCmd = 'pnpm install'
|
|
419
|
-
} else {
|
|
420
|
-
installCmd = 'npm install'
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
execSync(installCmd, { cwd: targetDir, stdio: 'inherit' })
|
|
424
|
-
|
|
425
|
-
// 1️⃣1️⃣ 运行 dev
|
|
426
|
-
if(runDev) {
|
|
427
|
-
console.log('🚀 启动开发服务器...')
|
|
428
|
-
if(enableHttps) {
|
|
429
|
-
console.log('🔐 首次启用 HTTPS 会自动生成证书,请稍等...')
|
|
430
|
-
}
|
|
431
|
-
execSync(pkgCommands[pkgManager].dev, {
|
|
432
|
-
cwd: targetDir,
|
|
433
|
-
stdio: 'inherit'
|
|
434
|
-
})
|
|
435
|
-
} else {
|
|
64
|
+
// 启动 dev 或提示完成
|
|
65
|
+
if(runDev) runCmd(pkgCommands[pkgManager].dev, targetDir)
|
|
66
|
+
else {
|
|
436
67
|
console.log(`\n✅ 项目创建完成`)
|
|
437
68
|
console.log(`👉 cd ${projectName}`)
|
|
438
69
|
console.log(`👉 ${pkgCommands[pkgManager].dev}`)
|
|
439
|
-
if(enableHttps)
|
|
440
|
-
console.log('🔐 首次启用 HTTPS 会自动生成证书,请稍等...\n')
|
|
441
|
-
}
|
|
70
|
+
if(enableHttps) console.log('🔐 首次启用 HTTPS 会自动生成证书,请稍等...')
|
|
442
71
|
}
|
|
443
|
-
})()
|
|
444
|
-
|
|
445
|
-
function detectPackageManager () {
|
|
446
|
-
const userAgent = process.env.npm_config_user_agent || ''
|
|
447
|
-
|
|
448
|
-
if(userAgent.startsWith('pnpm')) return 'pnpm'
|
|
449
|
-
if(userAgent.startsWith('npm')) return 'npm'
|
|
450
|
-
|
|
451
|
-
if(fs.existsSync('pnpm-lock.yaml')) return 'pnpm'
|
|
452
|
-
|
|
453
|
-
return 'npm'
|
|
454
|
-
}
|
|
72
|
+
})()
|
package/lib/features.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// lib/features.js
|
|
2
|
+
export function parseFeatures (featureList) {
|
|
3
|
+
return {
|
|
4
|
+
router: featureList.includes('router'),
|
|
5
|
+
pinia: featureList.includes('pinia'),
|
|
6
|
+
axios: featureList.includes('axios'),
|
|
7
|
+
ui: featureList.filter(v => ['element', 'vant'].includes(v))
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function parseExtraPlugins (featureList) {
|
|
12
|
+
return featureList.filter(v => ['vueuse', 'lodash', 'dayjs', 'tailwind', 'mitt', 'https'].includes(v))
|
|
13
|
+
}
|
package/lib/mainFile.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
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/package.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// lib/package.js
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
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')
|
|
9
|
+
|
|
10
|
+
const optionalDeps = {}
|
|
11
|
+
if(features.router) optionalDeps['vue-router'] = '^5.0.3'
|
|
12
|
+
if(features.pinia) {
|
|
13
|
+
optionalDeps['pinia'] = '^3.0.4'
|
|
14
|
+
optionalDeps['pinia-plugin-persistedstate'] = '^4.7.1'
|
|
15
|
+
}
|
|
16
|
+
if(features.axios) optionalDeps['axios'] = '^1.13.6'
|
|
17
|
+
if(features.ui.includes('element')) {
|
|
18
|
+
optionalDeps['element-plus'] = '^2.13.5'
|
|
19
|
+
optionalDeps['@element-plus/icons-vue'] = '^2.3.2'
|
|
20
|
+
}
|
|
21
|
+
if(features.ui.includes('vant')) optionalDeps['vant'] = '^4.9.22'
|
|
22
|
+
if(extraPlugins.includes('vueuse')) optionalDeps['@vueuse/core'] = '^14.2.1'
|
|
23
|
+
if(extraPlugins.includes('dayjs')) optionalDeps['dayjs'] = '^1.11.20'
|
|
24
|
+
if(extraPlugins.includes('lodash')) optionalDeps['lodash'] = '^4.17.23'
|
|
25
|
+
if(extraPlugins.includes('tailwind')) {
|
|
26
|
+
optionalDeps['tailwindcss'] = '^4.2.2'
|
|
27
|
+
optionalDeps['@tailwindcss/postcss'] = '^4.2.2'
|
|
28
|
+
optionalDeps['postcss'] = '^8.5.8'
|
|
29
|
+
}
|
|
30
|
+
if(extraPlugins.includes('mitt')) optionalDeps['mitt'] = '^3.0.1'
|
|
31
|
+
if(enableHttps) optionalDeps['vite-plugin-mkcert'] = '^1.17.10'
|
|
32
|
+
if(autoRoute) optionalDeps['vite-plugin-pages'] = '^0.33.3'
|
|
33
|
+
|
|
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)
|
|
37
|
+
|
|
38
|
+
const pkgObj = JSON.parse(pkg)
|
|
39
|
+
if(pkgManager === 'pnpm' && features.ui.includes('vant')) {
|
|
40
|
+
pkgObj.pnpm = { overrides: { "@vant/use": "^1.0.0", "@vant/popperjs": "^1.0.0" } }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkgObj, null, 2))
|
|
44
|
+
fs.unlinkSync(pkgTpl)
|
|
45
|
+
}
|
package/lib/prompts.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// lib/prompts.js
|
|
2
|
+
import prompts from 'prompts'
|
|
3
|
+
|
|
4
|
+
export async function getProjectName (fs, path) {
|
|
5
|
+
while(true) {
|
|
6
|
+
const res = await prompts({
|
|
7
|
+
type: 'text',
|
|
8
|
+
name: 'projectName',
|
|
9
|
+
message: '📦 项目名称',
|
|
10
|
+
validate: v => v ? true : '项目名不能为空'
|
|
11
|
+
})
|
|
12
|
+
const name = res.projectName
|
|
13
|
+
if(!name) process.exit(1)
|
|
14
|
+
const targetDir = path.resolve(process.cwd(), name)
|
|
15
|
+
if(fs.existsSync(targetDir)) {
|
|
16
|
+
console.log('❌ 目录已存在,请重新输入')
|
|
17
|
+
continue
|
|
18
|
+
}
|
|
19
|
+
return name
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function chooseLanguage () {
|
|
24
|
+
const { language } = await prompts({
|
|
25
|
+
type: 'select',
|
|
26
|
+
name: 'language',
|
|
27
|
+
message: '请选择项目语言',
|
|
28
|
+
choices: [
|
|
29
|
+
{ title: 'JavaScript', value: 'js' },
|
|
30
|
+
{ title: 'TypeScript', value: 'ts' }
|
|
31
|
+
]
|
|
32
|
+
})
|
|
33
|
+
return language
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function chooseFeatures () {
|
|
37
|
+
const { featureList } = await prompts({
|
|
38
|
+
type: 'multiselect',
|
|
39
|
+
name: 'featureList',
|
|
40
|
+
message: '请选择基础功能(↑↓选择,空格确认,回车完成)',
|
|
41
|
+
instructions: false,
|
|
42
|
+
choices: [
|
|
43
|
+
{ title: 'Vue Router', value: 'router' },
|
|
44
|
+
{ title: 'Pinia(含持久化)', value: 'pinia' },
|
|
45
|
+
{ title: 'Axios', value: 'axios' },
|
|
46
|
+
{ title: 'Element Plus(PC UI)', value: 'element' },
|
|
47
|
+
{ title: 'Vant(Mobile UI)', value: 'vant' },
|
|
48
|
+
{ title: 'VueUse(实用 Composition API)', value: 'vueuse' },
|
|
49
|
+
{ title: 'Lodash(工具库)', value: 'lodash' },
|
|
50
|
+
{ title: 'Day.js(日期处理)', value: 'dayjs' },
|
|
51
|
+
{ title: 'Tailwind CSS(原子化 CSS)', value: 'tailwind' },
|
|
52
|
+
{ title: 'mitt(事件总线)', value: 'mitt' },
|
|
53
|
+
{ title: 'HTTPS(mkcert)', value: 'https' }
|
|
54
|
+
]
|
|
55
|
+
})
|
|
56
|
+
return featureList || []
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function askAutoRoute (routerEnabled) {
|
|
60
|
+
if(!routerEnabled) return false
|
|
61
|
+
const { enableAutoRoute } = await prompts({
|
|
62
|
+
type: 'toggle',
|
|
63
|
+
name: 'enableAutoRoute',
|
|
64
|
+
message: '是否开启自动配置路由(vite-plugin-pages)?',
|
|
65
|
+
initial: false,
|
|
66
|
+
active: '是',
|
|
67
|
+
inactive: '否'
|
|
68
|
+
})
|
|
69
|
+
return enableAutoRoute
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function askRunDev (devCommand) {
|
|
73
|
+
const { runDev } = await prompts({
|
|
74
|
+
type: 'select',
|
|
75
|
+
name: 'runDev',
|
|
76
|
+
message: `是否立即运行 ${devCommand}?`,
|
|
77
|
+
choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
|
|
78
|
+
})
|
|
79
|
+
return runDev
|
|
80
|
+
}
|
package/lib/router.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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/template.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// lib/template.js
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
export function copyBaseTemplate (language, targetDir, __dirname) {
|
|
6
|
+
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
|
+
}
|
|
16
|
+
|
|
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}`)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function copyOptionalTemplates (features, extraPlugins, language, targetDir, __dirname) {
|
|
27
|
+
const copy = name => {
|
|
28
|
+
fs.cpSync(path.resolve(__dirname, `../template/${name}`), targetDir, { recursive: true })
|
|
29
|
+
}
|
|
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)
|
|
37
|
+
}
|
|
38
|
+
}
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// lib/utils.js
|
|
2
|
+
import { execSync } from 'child_process'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
|
|
5
|
+
export function checkNodeVersion (requiredVersion) {
|
|
6
|
+
const currentVersion = process.version.replace('v', '')
|
|
7
|
+
if(compareVersion(currentVersion, requiredVersion) < 0) {
|
|
8
|
+
console.error(`❌ Node.js 版本过低`)
|
|
9
|
+
console.error(`当前版本: ${currentVersion}`)
|
|
10
|
+
console.error(`最低要求: ${requiredVersion}`)
|
|
11
|
+
console.error(`请升级 Node.js 后再运行`)
|
|
12
|
+
process.exit(1)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function compareVersion (v1, v2) {
|
|
17
|
+
const a = v1.split('.').map(Number)
|
|
18
|
+
const b = v2.split('.').map(Number)
|
|
19
|
+
for(let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
20
|
+
const n1 = a[i] || 0
|
|
21
|
+
const n2 = b[i] || 0
|
|
22
|
+
if(n1 > n2) return 1
|
|
23
|
+
if(n1 < n2) return -1
|
|
24
|
+
}
|
|
25
|
+
return 0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function detectPackageManager () {
|
|
29
|
+
const userAgent = process.env.npm_config_user_agent || ''
|
|
30
|
+
if(userAgent.startsWith('pnpm')) return 'pnpm'
|
|
31
|
+
if(userAgent.startsWith('npm')) return 'npm'
|
|
32
|
+
if(fs.existsSync('pnpm-lock.yaml')) return 'pnpm'
|
|
33
|
+
return 'npm'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function runCmd (cmd, cwd = process.cwd()) {
|
|
37
|
+
execSync(cmd, { cwd, stdio: 'inherit' })
|
|
38
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { InternalAxiosRequestConfig } from "axios"
|
|
2
2
|
|
|
3
|
+
// 扩展 InternalAxiosRequestConfig 类型,添加自定义属性 __requestKey
|
|
4
|
+
declare module "axios" {
|
|
5
|
+
interface InternalAxiosRequestConfig {
|
|
6
|
+
__requestKey?: string
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
const pendingRequests = new Map()
|
|
4
11
|
|
|
5
12
|
/**
|