@jackie_qian/create-vue-app 1.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.
package/bin/index.mjs ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/index.js').catch((err) => {
3
+ console.error(err)
4
+ process.exit(1)
5
+ })
package/dist/cli.js ADDED
@@ -0,0 +1,348 @@
1
+ import {
2
+ cancel,
3
+ confirm,
4
+ intro,
5
+ isCancel,
6
+ multiselect,
7
+ outro,
8
+ select,
9
+ spinner,
10
+ text,
11
+ } from "@clack/prompts";
12
+ import pc from "picocolors";
13
+ import path from "node:path";
14
+ import {
15
+ FEATURES_OPTIONS,
16
+ PACKAGE_MANAGER_OPTIONS,
17
+ UI_LIBRARY_OPTIONS,
18
+ } from "./utils/constants.js";
19
+ import {
20
+ devCommand,
21
+ ensureEmptyDir,
22
+ installCommand,
23
+ pathExists,
24
+ run,
25
+ writeFileEnsureDir,
26
+ } from "./utils/util.js";
27
+ import {
28
+ patchMain,
29
+ renderAppWithRouter,
30
+ renderEditorConfig,
31
+ renderEnv,
32
+ renderHomeView,
33
+ renderPagesIndex,
34
+ renderRouter,
35
+ renderStore,
36
+ renderUnoConfig,
37
+ renderVsCodeExtensions,
38
+ renderVsCodeSettings,
39
+ renderViteConfig,
40
+ } from "./templates/index.js";
41
+ import {
42
+ addDep,
43
+ applyExampleTemplateJs,
44
+ ensureCounterStoreInModules,
45
+ finalizePackageJson,
46
+ loadPackageJson,
47
+ patchTsconfigApp,
48
+ readTextFile,
49
+ updateTsconfigTypes,
50
+ writeEslintConfig,
51
+ writePackageJson,
52
+ writeStylelintConfig,
53
+ writeTextFile,
54
+ } from "./scaffold.js";
55
+ function exitWithCancel() {
56
+ cancel("已取消");
57
+ process.exit(1);
58
+ }
59
+ function normalizeProjectName(input) {
60
+ return input.trim().replaceAll(" ", "-");
61
+ }
62
+ function toFeatureSet(values) {
63
+ return new Set(values);
64
+ }
65
+ export async function main() {
66
+ intro(pc.cyan("自定义 Vue3 模板生成器"));
67
+ const useDefaults = process.argv.includes("--defaults");
68
+ const preferJs = process.argv.includes("--js");
69
+ const argName = process.argv.slice(2).find((a) => !a.startsWith("-"));
70
+ let projectName;
71
+ let language;
72
+ let features;
73
+ let installDeps;
74
+ let packageManager;
75
+ let uiLibrary;
76
+ if (useDefaults) {
77
+ projectName = normalizeProjectName(argName ?? "my-app");
78
+ language = preferJs ? "js" : "ts";
79
+ features = toFeatureSet([
80
+ "router",
81
+ "pinia",
82
+ "piniaPersist",
83
+ "sassEmbedded",
84
+ "eslintAntfu",
85
+ "stylelint",
86
+ "gitHooks",
87
+ "pages",
88
+ "autoImport",
89
+ "components",
90
+ "unocss",
91
+ ]);
92
+ installDeps = false;
93
+ packageManager = "pnpm";
94
+ uiLibrary = "none";
95
+ } else {
96
+ const projectNameRaw = await text({
97
+ message: "项目目录名: ",
98
+ initialValue: argName ?? "",
99
+ validate: (v) => {
100
+ const name = normalizeProjectName(String(v ?? ""));
101
+ if (!name) return "请输入项目名";
102
+ if (name === ".") return "不支持使用 . 作为项目名";
103
+ return undefined;
104
+ },
105
+ });
106
+ if (isCancel(projectNameRaw)) exitWithCancel();
107
+ projectName = normalizeProjectName(String(projectNameRaw));
108
+ const useTsRaw = await confirm({
109
+ message: "使用 TypeScript?",
110
+ initialValue: true,
111
+ });
112
+ if (isCancel(useTsRaw)) exitWithCancel();
113
+ language = Boolean(useTsRaw) ? "ts" : "js";
114
+ const featuresSelected = await multiselect({
115
+ message:
116
+ "选择要包含在项目中的功能:(↑/↓为导航,空格为选择,a为切换全部,输入为确认)",
117
+ options: [...FEATURES_OPTIONS],
118
+ initialValues: ["router", "pinia"],
119
+ });
120
+ if (isCancel(featuresSelected)) exitWithCancel();
121
+ features = toFeatureSet(featuresSelected);
122
+ if (features.has("piniaPersist")) features.add("pinia");
123
+ uiLibrary = await select({
124
+ message: "选择 UI 库",
125
+ options: [...UI_LIBRARY_OPTIONS],
126
+ initialValue: "",
127
+ });
128
+ if (isCancel(uiLibrary)) exitWithCancel();
129
+ if (uiLibrary === "vant") {
130
+ features.add("vant");
131
+ features.add("autoImport");
132
+ features.add("components");
133
+ } else if (uiLibrary === "elementPlus") {
134
+ features.add("elementPlus");
135
+ features.add("autoImport");
136
+ features.add("components");
137
+ }
138
+ if (features.has("router")) features.add("pages");
139
+ features.add("autoImport");
140
+ features.add("components");
141
+ features.add("unocss");
142
+ packageManager = await select({
143
+ message:
144
+ "Which package manager will you use? (Vue 3.6 beta requires version overrides that differ per package manager)",
145
+ options: [...PACKAGE_MANAGER_OPTIONS],
146
+ initialValue: "pnpm",
147
+ });
148
+ if (isCancel(packageManager)) exitWithCancel();
149
+ const installDepsRaw = await confirm({
150
+ message: `生成后是否自动安装依赖(${packageManager} install)?`,
151
+ initialValue: true,
152
+ });
153
+ if (isCancel(installDepsRaw)) exitWithCancel();
154
+ installDeps = Boolean(installDepsRaw);
155
+ }
156
+ const cwd = process.cwd();
157
+ const targetDir = path.resolve(cwd, projectName);
158
+ const s = spinner();
159
+ s.start("生成基础项目");
160
+ try {
161
+ await ensureEmptyDir(targetDir);
162
+ const flags = ["--bare", "--force"];
163
+ if (language === "ts") flags.push("--ts");
164
+ if (features.has("router")) flags.push("--router");
165
+ if (features.has("pinia")) flags.push("--pinia");
166
+ if (flags.length === 2) flags.push("--default");
167
+ run("pnpm", ["dlx", "create-vue@latest", ...flags, projectName], cwd);
168
+ s.stop("基础项目已生成");
169
+ } catch (e) {
170
+ s.stop("生成失败");
171
+ throw e;
172
+ }
173
+ if (language === "js") {
174
+ await applyExampleTemplateJs(targetDir, projectName, features);
175
+ await writeFileEnsureDir(
176
+ path.join(targetDir, ".vscode/settings.json"),
177
+ renderVsCodeSettings(),
178
+ );
179
+ await writeFileEnsureDir(
180
+ path.join(targetDir, ".vscode/extensions.json"),
181
+ renderVsCodeExtensions(language, features),
182
+ );
183
+ if (installDeps) {
184
+ const s2 = spinner();
185
+ s2.start("安装依赖");
186
+ try {
187
+ const { cmd, args } = installCommand(packageManager);
188
+ run(cmd, args, targetDir);
189
+ s2.stop("依赖已安装");
190
+ } catch (e) {
191
+ s2.stop("依赖安装失败");
192
+ throw e;
193
+ }
194
+ }
195
+ outro(
196
+ `${pc.green("完成")}:${pc.cyan(projectName)}\n\n${pc.dim("下一步:")}\n cd ${projectName}\n ${devCommand(packageManager)}`,
197
+ );
198
+ return;
199
+ }
200
+ const projectPkgPath = path.join(targetDir, "package.json");
201
+ const pkg = await loadPackageJson(projectPkgPath);
202
+ pkg.name = pkg.name || projectName;
203
+ if (features.has("router")) addDep(pkg.dependencies, "vue-router", "^4.5.1");
204
+ if (features.has("pinia")) addDep(pkg.dependencies, "pinia", "^3.0.3");
205
+ if (features.has("piniaPersist"))
206
+ addDep(pkg.dependencies, "pinia-plugin-persistedstate", "^4.2.0");
207
+ if (features.has("vant")) addDep(pkg.dependencies, "vant", "^4.9.22");
208
+ if (features.has("elementPlus"))
209
+ addDep(pkg.dependencies, "element-plus", "^2.7.6");
210
+ if (features.has("pages"))
211
+ addDep(pkg.devDependencies, "vite-plugin-pages", "^0.33.0");
212
+ if (features.has("autoImport"))
213
+ addDep(pkg.devDependencies, "unplugin-auto-import", "^0.19.0");
214
+ if (features.has("components"))
215
+ addDep(pkg.devDependencies, "unplugin-vue-components", "^0.28.0");
216
+ if (features.has("vant"))
217
+ addDep(pkg.devDependencies, "@vant/auto-import-resolver", "^1.3.0");
218
+ if (features.has("unocss")) addDep(pkg.devDependencies, "unocss", "^0.64.0");
219
+ if (features.has("eslintAntfu")) {
220
+ addDep(pkg.devDependencies, "eslint", "^9.0.0");
221
+ addDep(pkg.devDependencies, "@antfu/eslint-config", "^4.0.0");
222
+ }
223
+ if (features.has("stylelint")) {
224
+ addDep(pkg.devDependencies, "stylelint", "^16.0.0");
225
+ addDep(pkg.devDependencies, "@stylistic/stylelint-config", "^3.0.0");
226
+ addDep(pkg.devDependencies, "stylelint-config-recess-order", "^6.0.0");
227
+ addDep(pkg.devDependencies, "stylelint-config-standard-scss", "^14.0.0");
228
+ addDep(pkg.devDependencies, "stylelint-config-standard-vue", "^1.0.0");
229
+ addDep(pkg.devDependencies, "stylelint-scss", "^6.0.0");
230
+ }
231
+ if (features.has("gitHooks")) {
232
+ addDep(pkg.devDependencies, "simple-git-hooks", "^2.11.1");
233
+ addDep(pkg.devDependencies, "lint-staged", "^15.2.0");
234
+ }
235
+ if (features.has("sassEmbedded"))
236
+ addDep(pkg.devDependencies, "sass-embedded", "^1.77.0");
237
+ if (features.has("eslintAntfu") && features.has("stylelint")) {
238
+ addDep(pkg.devDependencies, "npm-run-all2", "^7.0.2");
239
+ pkg.scripts.lint = "npm-run-all -s lint:eslint lint:stylelint";
240
+ pkg.scripts["lint:eslint"] = "eslint --fix";
241
+ pkg.scripts["lint:stylelint"] = 'stylelint "src/**/*.{css,scss,vue}" --fix';
242
+ } else if (features.has("eslintAntfu")) {
243
+ pkg.scripts.lint = "eslint";
244
+ pkg.scripts["lint:fix"] = "eslint --fix";
245
+ } else if (features.has("stylelint")) {
246
+ pkg.scripts.lint = "stylelint";
247
+ pkg.scripts["lint:stylelint"] = 'stylelint "src/**/*.{css,scss,vue}" --fix';
248
+ }
249
+ if (features.has("gitHooks")) {
250
+ pkg.scripts.prepare = "simple-git-hooks";
251
+ pkg["simple-git-hooks"] = {
252
+ "pre-commit": "pnpm lint-staged",
253
+ preserveUnused: true,
254
+ };
255
+ pkg["lint-staged"] = { "*.{js,ts,vue}": "pnpm lint" };
256
+ }
257
+ await finalizePackageJson(pkg, targetDir);
258
+ await writePackageJson(projectPkgPath, pkg);
259
+ const viteConfigPath = path.join(targetDir, "vite.config.ts");
260
+ await writeFileEnsureDir(
261
+ viteConfigPath,
262
+ renderViteConfig(language, features),
263
+ );
264
+ await writeFileEnsureDir(
265
+ path.join(targetDir, ".vscode/settings.json"),
266
+ renderVsCodeSettings(),
267
+ );
268
+ await writeFileEnsureDir(
269
+ path.join(targetDir, ".vscode/extensions.json"),
270
+ renderVsCodeExtensions(language, features),
271
+ );
272
+ await writeFileEnsureDir(
273
+ path.join(targetDir, ".editorconfig"),
274
+ renderEditorConfig(),
275
+ );
276
+ await writeFileEnsureDir(
277
+ path.join(targetDir, ".env.development"),
278
+ renderEnv(projectName),
279
+ );
280
+ await writeFileEnsureDir(
281
+ path.join(targetDir, ".env.production"),
282
+ renderEnv(projectName),
283
+ );
284
+ await patchTsconfigApp(targetDir);
285
+ if (features.has("unocss")) {
286
+ const unoPath = path.join(targetDir, "uno.config.ts");
287
+ await writeFileEnsureDir(unoPath, renderUnoConfig(language));
288
+ }
289
+ const mainPath = path.join(targetDir, "src/main.ts");
290
+ const appPath = path.join(targetDir, "src/App.vue");
291
+ if (features.has("router")) {
292
+ const router = renderRouter(language, features.has("pages"));
293
+ await writeFileEnsureDir(
294
+ path.join(targetDir, router.fileName),
295
+ router.content,
296
+ );
297
+ await writeFileEnsureDir(appPath, renderAppWithRouter());
298
+ if (features.has("pages")) {
299
+ const pagesDir = "src/pages";
300
+ await writeFileEnsureDir(
301
+ path.join(targetDir, pagesDir, "index.vue"),
302
+ renderPagesIndex(),
303
+ );
304
+ await updateTsconfigTypes(targetDir, ["vite-plugin-pages/client"]);
305
+ } else {
306
+ await writeFileEnsureDir(
307
+ path.join(targetDir, "src/views/HomeView.vue"),
308
+ renderHomeView(),
309
+ );
310
+ }
311
+ }
312
+ if (features.has("pinia")) {
313
+ const store = renderStore(language, features.has("piniaPersist"));
314
+ await writeFileEnsureDir(
315
+ path.join(targetDir, store.fileName),
316
+ store.content,
317
+ );
318
+ await ensureCounterStoreInModules(targetDir, language);
319
+ }
320
+ if (await pathExists(mainPath)) {
321
+ const mainRaw = await readTextFile(mainPath);
322
+ const patched = patchMain(mainRaw, {
323
+ useRouter: features.has("router"),
324
+ useStore: features.has("pinia"),
325
+ useVant: features.has("vant"),
326
+ useElementPlus: features.has("elementPlus"),
327
+ useUno: features.has("unocss"),
328
+ });
329
+ await writeTextFile(mainPath, patched);
330
+ }
331
+ await writeEslintConfig(targetDir, features);
332
+ await writeStylelintConfig(targetDir, features);
333
+ if (installDeps) {
334
+ const s2 = spinner();
335
+ s2.start("安装依赖");
336
+ try {
337
+ const { cmd, args } = installCommand(packageManager);
338
+ run(cmd, args, targetDir);
339
+ s2.stop("依赖已安装");
340
+ } catch (e) {
341
+ s2.stop("依赖安装失败");
342
+ throw e;
343
+ }
344
+ }
345
+ outro(
346
+ `${pc.green("完成")}:${pc.cyan(projectName)}\n\n${pc.dim("下一步:")}\n cd ${projectName}\n ${devCommand(packageManager)}`,
347
+ );
348
+ }
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import pc from "picocolors";
2
+ import { main } from "./cli.js";
3
+ main().catch((e) => {
4
+ console.error(pc.red(String(e?.message ?? e)));
5
+ process.exit(1);
6
+ });