create-vue-workspace 0.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/dist/index.mjs +910 -0
  4. package/package.json +41 -0
  5. package/templates/app-web/_env.development +2 -0
  6. package/templates/app-web/_env.production +2 -0
  7. package/templates/app-web/index.html +12 -0
  8. package/templates/app-web/package.json +25 -0
  9. package/templates/app-web/src/App.vue +18 -0
  10. package/templates/app-web/src/api/request.ts +15 -0
  11. package/templates/app-web/src/main.ts +7 -0
  12. package/templates/app-web/src/router/index.ts +16 -0
  13. package/templates/app-web/src/stores/app.ts +7 -0
  14. package/templates/app-web/src/styles/index.scss +8 -0
  15. package/templates/app-web/src/views/home-view.vue +16 -0
  16. package/templates/app-web/src/vite-env.d.ts +12 -0
  17. package/templates/app-web/tsconfig.json +7 -0
  18. package/templates/app-web/vite.config.ts +17 -0
  19. package/templates/base/README.md +53 -0
  20. package/templates/base/_changeset/config.json +11 -0
  21. package/templates/base/_editorconfig +12 -0
  22. package/templates/base/_gitignore +12 -0
  23. package/templates/base/eslint.config.mjs +25 -0
  24. package/templates/base/package.json +38 -0
  25. package/templates/base/pnpm-workspace.yaml +3 -0
  26. package/templates/base/prettier.config.mjs +8 -0
  27. package/templates/base/stylelint.config.mjs +8 -0
  28. package/templates/base/tsconfig.base.json +17 -0
  29. package/templates/base/tsconfig.json +4 -0
  30. package/templates/base/vitest.config.ts +10 -0
  31. package/templates/component/__tests__/{{fileName}}.component.spec.tsx +17 -0
  32. package/templates/component/components/{{fileName}}-sub.component.tsx +9 -0
  33. package/templates/component/composition/use-{{fileName}}.ts +46 -0
  34. package/templates/component/index.ts +12 -0
  35. package/templates/component/package.json +31 -0
  36. package/templates/component/tsconfig.json +5 -0
  37. package/templates/component/types.ts +5 -0
  38. package/templates/component/vite.config.ts +17 -0
  39. package/templates/component/{{fileName}}.component.tsx +22 -0
  40. package/templates/component/{{fileName}}.props.ts +12 -0
  41. package/templates/component/{{fileName}}.scss +11 -0
  42. package/templates/hook/use-{{kebab}}.ts +26 -0
  43. package/templates/pkg-lib/package.json +24 -0
  44. package/templates/pkg-lib/src/index.ts +5 -0
  45. package/templates/pkg-lib/tsconfig.json +4 -0
  46. package/templates/pkg-lib/vite.config.ts +17 -0
  47. package/templates/pkg-ui/package.json +31 -0
  48. package/templates/pkg-ui/src/index.ts +7 -0
  49. package/templates/pkg-ui/src/styles/index.scss +6 -0
  50. package/templates/pkg-ui/tsconfig.json +4 -0
  51. package/templates/pkg-ui/vite.config.ts +17 -0
  52. package/templates/pkg-utils/package.json +24 -0
  53. package/templates/pkg-utils/src/index.ts +20 -0
  54. package/templates/pkg-utils/tsconfig.json +4 -0
  55. package/templates/pkg-utils/vite.config.ts +13 -0
  56. package/templates/util/{{kebab}}.ts +4 -0
  57. package/templates/view/{{fileName}}.vue +15 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,910 @@
1
+ #!/usr/bin/env node
2
+ import { cac } from 'cac';
3
+ import path from 'node:path';
4
+ import * as p from '@clack/prompts';
5
+ import pc from 'picocolors';
6
+ import fs, { readFileSync } from 'node:fs';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { execa } from 'execa';
9
+
10
+ const CLI_NAME = "cvw";
11
+ const CLI_FULL_NAME = "create-vue-workspace";
12
+ const APP_DIR_NAME = "web";
13
+ const DEFAULT_PORT = 5173;
14
+ const DEFAULT_PACKAGES = ["ui", "utils"];
15
+ const WORKSPACE_PROTOCOL = "workspace:*";
16
+ const FARRIS_UI_VUE_VERSION = "^1.8.4";
17
+ const VUE_VERSION$1 = "^3.5.0";
18
+ const ROUTE_ANCHOR = "// cvw:routes";
19
+ function resolveVersion() {
20
+ try {
21
+ const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../package.json");
22
+ const content = JSON.parse(readFileSync(packageJsonPath, "utf8"));
23
+ return content.version ?? "0.0.0";
24
+ } catch {
25
+ return "0.0.0";
26
+ }
27
+ }
28
+ const VERSION = resolveVersion();
29
+
30
+ const PLACEHOLDER_PATTERN = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
31
+ function render(content, vars) {
32
+ return content.replace(PLACEHOLDER_PATTERN, (placeholder, key) => {
33
+ if (!Object.prototype.hasOwnProperty.call(vars, key)) {
34
+ return placeholder;
35
+ }
36
+ return String(vars[key]);
37
+ });
38
+ }
39
+
40
+ const BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
41
+ ".png",
42
+ ".jpg",
43
+ ".jpeg",
44
+ ".gif",
45
+ ".webp",
46
+ ".ico",
47
+ ".icns",
48
+ ".woff",
49
+ ".woff2",
50
+ ".ttf",
51
+ ".otf",
52
+ ".eot",
53
+ ".zip",
54
+ ".gz",
55
+ ".pdf"
56
+ ]);
57
+ function toTargetName(name) {
58
+ const isDotFileAlias = name.startsWith("_") && !name.startsWith("__");
59
+ return isDotFileAlias ? `.${name.slice(1)}` : name;
60
+ }
61
+ function isBinaryFile(filePath) {
62
+ return BINARY_EXTENSIONS.has(path.extname(filePath).toLowerCase());
63
+ }
64
+ function isDirectoryEmpty(dir) {
65
+ if (!fs.existsSync(dir)) {
66
+ return true;
67
+ }
68
+ return fs.readdirSync(dir).filter((entry) => entry !== ".git").length === 0;
69
+ }
70
+ function copyTemplateDir(sourceDir, targetDir, options) {
71
+ if (!fs.existsSync(sourceDir)) {
72
+ throw new Error(`\u6A21\u677F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${sourceDir}`);
73
+ }
74
+ const skip = new Set(options.skip ?? []);
75
+ const createdFiles = [];
76
+ if (!options.dryRun) {
77
+ fs.mkdirSync(targetDir, { recursive: true });
78
+ }
79
+ copyRecursive(sourceDir, targetDir, "", options, skip, createdFiles);
80
+ return createdFiles;
81
+ }
82
+ function copyRecursive(sourceDir, targetDir, relativeDir, options, skip, createdFiles) {
83
+ fs.readdirSync(sourceDir, { withFileTypes: true }).forEach((entry) => {
84
+ const relativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
85
+ if (skip.has(relativePath) || skip.has(render(relativePath, options.vars))) {
86
+ return;
87
+ }
88
+ const sourcePath = path.join(sourceDir, entry.name);
89
+ const targetPath = path.join(targetDir, render(toTargetName(entry.name), options.vars));
90
+ if (entry.isDirectory()) {
91
+ if (!options.dryRun) {
92
+ fs.mkdirSync(targetPath, { recursive: true });
93
+ }
94
+ copyRecursive(sourcePath, targetPath, relativePath, options, skip, createdFiles);
95
+ return;
96
+ }
97
+ copyFile(sourcePath, targetPath, options, createdFiles);
98
+ });
99
+ }
100
+ function copyFile(sourcePath, targetPath, options, createdFiles) {
101
+ createdFiles.push(targetPath);
102
+ if (options.dryRun) {
103
+ return;
104
+ }
105
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
106
+ if (isBinaryFile(sourcePath)) {
107
+ fs.copyFileSync(sourcePath, targetPath);
108
+ return;
109
+ }
110
+ fs.writeFileSync(targetPath, render(fs.readFileSync(sourcePath, "utf8"), options.vars));
111
+ }
112
+
113
+ let cachedTemplatesRoot;
114
+ function resolveTemplatesRoot() {
115
+ if (cachedTemplatesRoot) {
116
+ return cachedTemplatesRoot;
117
+ }
118
+ let currentDir = path.dirname(fileURLToPath(import.meta.url));
119
+ for (let depth = 0; depth < 6; depth += 1) {
120
+ const candidate = path.join(currentDir, "templates");
121
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
122
+ cachedTemplatesRoot = candidate;
123
+ return candidate;
124
+ }
125
+ const parentDir = path.dirname(currentDir);
126
+ if (parentDir === currentDir) {
127
+ break;
128
+ }
129
+ currentDir = parentDir;
130
+ }
131
+ throw new Error("\u672A\u627E\u5230 templates \u76EE\u5F55\uFF0C\u811A\u624B\u67B6\u5B89\u88C5\u53EF\u80FD\u4E0D\u5B8C\u6574\u3002");
132
+ }
133
+ function resolveTemplate(name) {
134
+ return path.join(resolveTemplatesRoot(), name);
135
+ }
136
+
137
+ function formatTarget(projectRoot, targetPath) {
138
+ return path.relative(projectRoot, targetPath).replace(/\\/g, "/");
139
+ }
140
+ function createGenerationContext(projectRoot, dryRun) {
141
+ function record(action, targetPath) {
142
+ const label = action === "create" ? pc.green("CREATE") : pc.yellow("UPDATE");
143
+ const suffix = dryRun ? pc.dim(" (dry-run)") : "";
144
+ console.log(`${label} ${formatTarget(projectRoot, targetPath)}${suffix}`);
145
+ }
146
+ return {
147
+ projectRoot,
148
+ dryRun,
149
+ record,
150
+ applyChange(targetPath, nextContent) {
151
+ record("update", targetPath);
152
+ if (!dryRun) {
153
+ fs.writeFileSync(targetPath, nextContent);
154
+ }
155
+ },
156
+ applyCreate(targetPath, content) {
157
+ record("create", targetPath);
158
+ if (!dryRun) {
159
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
160
+ fs.writeFileSync(targetPath, content);
161
+ }
162
+ },
163
+ warn(message) {
164
+ console.log(pc.yellow(`! ${message}`));
165
+ },
166
+ info(message) {
167
+ console.log(pc.dim(` ${message}`));
168
+ }
169
+ };
170
+ }
171
+ function copyAndRecord(context, sourceDir, targetDir, options) {
172
+ const created = copyTemplateDir(sourceDir, targetDir, { ...options, dryRun: context.dryRun });
173
+ created.forEach((filePath) => context.record("create", filePath));
174
+ return created;
175
+ }
176
+
177
+ const VALID_INPUT_PATTERN = /^[A-Za-z][A-Za-z0-9]*(?:[-_ ][A-Za-z0-9]+)*$/;
178
+ const VALID_KEBAB_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
179
+ function toWords(input) {
180
+ return input.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_\s.]+/g, "-").split("-").filter(Boolean).map((word) => word.toLowerCase());
181
+ }
182
+ function toKebabCase(input) {
183
+ return toWords(input).join("-");
184
+ }
185
+ function toPascalCase(input) {
186
+ return toWords(input).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
187
+ }
188
+ function toCamelCase(input) {
189
+ const pascalName = toPascalCase(input);
190
+ return pascalName.charAt(0).toLowerCase() + pascalName.slice(1);
191
+ }
192
+ function createNameTokens(input) {
193
+ const kebab = toKebabCase(input);
194
+ if (!VALID_INPUT_PATTERN.test(input) || !VALID_KEBAB_PATTERN.test(kebab)) {
195
+ throw new Error(
196
+ `\u975E\u6CD5\u7684\u540D\u79F0 "${input}"\uFF1A\u4EC5\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\uFF0C\u4EE5\u53CA - _ \u7A7A\u683C \u4F5C\u4E3A\u5206\u9694\u7B26\uFF0C\u4E14\u5FC5\u987B\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF08\u5982 button\u3001input-group\uFF09\u3002`
197
+ );
198
+ }
199
+ return {
200
+ kebab,
201
+ camel: toCamelCase(kebab),
202
+ pascal: toPascalCase(kebab)
203
+ };
204
+ }
205
+ function createPathTokens(input) {
206
+ const segments = input.split("/").map((segment) => segment.trim()).filter(Boolean);
207
+ if (segments.length === 0) {
208
+ throw new Error("\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u3002");
209
+ }
210
+ const segmentTokens = segments.map((segment) => createNameTokens(segment));
211
+ const last = segmentTokens[segmentTokens.length - 1];
212
+ return {
213
+ kebab: segmentTokens.map((token) => token.kebab).join("-"),
214
+ camel: segmentTokens.map((token, index) => index === 0 ? token.camel : token.pascal).join(""),
215
+ pascal: segmentTokens.map((token) => token.pascal).join(""),
216
+ dirPath: segmentTokens.map((token) => token.kebab).join("/"),
217
+ fileName: last.kebab,
218
+ compositePascal: segmentTokens.map((token) => token.pascal).join("")
219
+ };
220
+ }
221
+ function createComponentNaming(input) {
222
+ const tokens = createNameTokens(input);
223
+ return {
224
+ dirName: tokens.kebab,
225
+ packageName: tokens.kebab,
226
+ fileName: tokens.kebab,
227
+ camelName: tokens.camel,
228
+ pascalName: tokens.pascal,
229
+ componentName: `F${tokens.pascal}`,
230
+ selector: `f-${tokens.kebab}`,
231
+ propsName: `${tokens.camel}Props`,
232
+ propsTypeName: `${tokens.pascal}Props`
233
+ };
234
+ }
235
+
236
+ function unwrap$1(value) {
237
+ if (p.isCancel(value)) {
238
+ p.cancel("\u5DF2\u53D6\u6D88\u3002");
239
+ process.exit(0);
240
+ }
241
+ return value;
242
+ }
243
+ function normalizeScope(raw, projectName) {
244
+ const value = (raw ?? "").trim() || projectName;
245
+ return value.startsWith("@") ? value : `@${value}`;
246
+ }
247
+ function splitPackages(raw) {
248
+ if (!raw) {
249
+ return [...DEFAULT_PACKAGES];
250
+ }
251
+ return raw.split(",").map((item) => item.trim()).filter(Boolean);
252
+ }
253
+ function isInteractive(options) {
254
+ return !options.yes && Boolean(process.stdout.isTTY);
255
+ }
256
+ async function resolveNewAnswers(nameOption, options) {
257
+ const defaultName = nameOption ?? "my-app";
258
+ const projectName = toKebabCase(path.basename(path.resolve(defaultName))) || "my-app";
259
+ let name = defaultName;
260
+ let scope = normalizeScope(options.scope, projectName);
261
+ let packages = splitPackages(options.packages);
262
+ let port = options.port ? Number(options.port) : DEFAULT_PORT;
263
+ let ui = options.ui === "none" ? "none" : "farris";
264
+ if (isInteractive(options)) {
265
+ name = unwrap$1(
266
+ await p.text({ message: "\u5DE5\u7A0B\u76EE\u5F55\u540D", placeholder: defaultName, defaultValue: defaultName })
267
+ );
268
+ scope = normalizeScope(
269
+ unwrap$1(
270
+ await p.text({ message: "\u5305 scope\uFF08\u751F\u6210\u7269\u5305\u540D\u524D\u7F00\uFF09", placeholder: scope, defaultValue: scope })
271
+ ),
272
+ projectName
273
+ );
274
+ packages = unwrap$1(
275
+ await p.multiselect({
276
+ message: "\u521D\u59CB\u521B\u5EFA\u54EA\u4E9B packages",
277
+ options: [
278
+ { value: "ui", label: "ui \u2014\u2014 \u7EC4\u4EF6\u805A\u5408\u5305" },
279
+ { value: "utils", label: "utils \u2014\u2014 \u7EAF TS \u5DE5\u5177\u5305" }
280
+ ],
281
+ initialValues: packages,
282
+ required: false
283
+ })
284
+ );
285
+ port = Number(
286
+ unwrap$1(
287
+ await p.text({ message: "\u5E94\u7528\u5F00\u53D1\u7AEF\u53E3", placeholder: String(port), defaultValue: String(port) })
288
+ )
289
+ );
290
+ ui = unwrap$1(
291
+ await p.select({
292
+ message: "UI \u57FA\u7840\u5E93",
293
+ options: [
294
+ { value: "farris", label: "Farris (@farris/ui-vue)" },
295
+ { value: "none", label: "\u4E0D\u5F15\u5165" }
296
+ ],
297
+ initialValue: ui
298
+ })
299
+ );
300
+ }
301
+ return {
302
+ name,
303
+ projectName,
304
+ scope,
305
+ appDirName: APP_DIR_NAME,
306
+ template: "web",
307
+ packages,
308
+ ui,
309
+ port,
310
+ install: options.skipInstall !== true,
311
+ git: options.skipGit !== true,
312
+ dryRun: options.dryRun === true
313
+ };
314
+ }
315
+
316
+ async function runCommand(command, args, cwd) {
317
+ try {
318
+ await execa(command, args, { cwd, stdio: "inherit" });
319
+ } catch (error) {
320
+ const detail = error instanceof Error ? error.message : String(error);
321
+ throw new Error(`\u6267\u884C\u547D\u4EE4\u5931\u8D25\uFF1A${command} ${args.join(" ")}
322
+ ${detail}`);
323
+ }
324
+ }
325
+ async function tryRunCommand(command, args, cwd) {
326
+ try {
327
+ await execa(command, args, { cwd, stdio: "pipe" });
328
+ return true;
329
+ } catch {
330
+ return false;
331
+ }
332
+ }
333
+ async function hasCommand(command) {
334
+ try {
335
+ await execa(command, ["--version"], { stdio: "pipe" });
336
+ return true;
337
+ } catch {
338
+ return false;
339
+ }
340
+ }
341
+
342
+ async function newCommand(nameOption, options) {
343
+ p.intro(pc.bgCyan(pc.black(` ${CLI_FULL_NAME} `)));
344
+ const answers = await resolveNewAnswers(nameOption, options);
345
+ const targetDir = path.resolve(process.cwd(), answers.name);
346
+ if (!isDirectoryEmpty(targetDir)) {
347
+ throw new Error(`\u76EE\u6807\u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF1A${targetDir}
348
+ \u8BF7\u6362\u4E00\u4E2A\u76EE\u5F55\u540D\uFF0C\u6216\u5148\u6E05\u7A7A\u8BE5\u76EE\u5F55\u540E\u91CD\u8BD5\u3002`);
349
+ }
350
+ const context = createGenerationContext(targetDir, answers.dryRun);
351
+ const vars = buildTemplateVars(answers);
352
+ copyAndRecord(context, resolveTemplate("base"), targetDir, { vars });
353
+ copyAndRecord(
354
+ context,
355
+ resolveTemplate(`app-${answers.template}`),
356
+ path.join(targetDir, "apps", answers.appDirName),
357
+ { vars }
358
+ );
359
+ answers.packages.forEach((packageName) => {
360
+ copyAndRecord(context, resolveTemplate(`pkg-${packageName}`), path.join(targetDir, "packages", packageName), {
361
+ vars
362
+ });
363
+ });
364
+ if (answers.dryRun) {
365
+ p.outro(pc.green("dry-run \u5B8C\u6210\uFF0C\u672A\u5199\u5165\u4EFB\u4F55\u6587\u4EF6"));
366
+ return;
367
+ }
368
+ if (answers.install && !await hasCommand("pnpm")) {
369
+ p.log.warn("\u672A\u68C0\u6D4B\u5230 pnpm\uFF0C\u5DF2\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5\u3002\u5B89\u88C5 pnpm \u540E\u53EF\u5728\u5DE5\u7A0B\u76EE\u5F55\u624B\u52A8\u6267\u884C pnpm install\u3002");
370
+ answers.install = false;
371
+ }
372
+ await initializeGit(targetDir, answers);
373
+ await installDependencies(targetDir, answers);
374
+ p.note(
375
+ [
376
+ `cd ${answers.name}`,
377
+ answers.install ? "pnpm dev" : "pnpm install && pnpm dev",
378
+ "",
379
+ "\u65B0\u589E\u7EC4\u4EF6\u5305\uFF1A",
380
+ "cvw g c button"
381
+ ].join("\n"),
382
+ "\u4E0B\u4E00\u6B65"
383
+ );
384
+ p.outro(pc.green("\u5DE5\u4F5C\u533A\u521B\u5EFA\u5B8C\u6210"));
385
+ }
386
+ function buildTemplateVars(answers) {
387
+ const workspaceDependencies = answers.packages.map((packageName) => `,
388
+ "${answers.scope}/${packageName}": "${WORKSPACE_PROTOCOL}"`).join("");
389
+ const workspaceAliases = answers.packages.map(
390
+ (packageName) => ` '${answers.scope}/${packageName}': fileURLToPath(new URL('../../packages/${packageName}/src/index.ts', import.meta.url)),`
391
+ ).join("\n");
392
+ return {
393
+ projectName: answers.projectName,
394
+ scope: answers.scope,
395
+ appDirName: answers.appDirName,
396
+ port: answers.port,
397
+ workspaceDependencies,
398
+ workspaceAliases,
399
+ farrisDependency: answers.ui === "farris" ? `,
400
+ "@farris/ui-vue": "${FARRIS_UI_VUE_VERSION}"` : ""
401
+ };
402
+ }
403
+ async function initializeGit(targetDir, answers) {
404
+ if (!answers.git) {
405
+ return;
406
+ }
407
+ const spinner = p.spinner();
408
+ spinner.start("\u521D\u59CB\u5316 git \u4ED3\u5E93");
409
+ if (!await tryRunCommand("git", ["init"], targetDir)) {
410
+ spinner.stop("git init \u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7");
411
+ return;
412
+ }
413
+ await tryRunCommand("git", ["add", "-A"], targetDir);
414
+ const committed = await tryRunCommand(
415
+ "git",
416
+ ["commit", "-m", `chore: init from ${CLI_FULL_NAME}`],
417
+ targetDir
418
+ );
419
+ spinner.stop(committed ? "git \u4ED3\u5E93\u5DF2\u521D\u59CB\u5316" : "git \u4ED3\u5E93\u5DF2\u521D\u59CB\u5316\uFF08\u9996\u6B21\u63D0\u4EA4\u8DF3\u8FC7\uFF0C\u8BF7\u68C0\u67E5 git \u7528\u6237\u914D\u7F6E\uFF09");
420
+ }
421
+ async function installDependencies(targetDir, answers) {
422
+ if (!answers.install) {
423
+ return;
424
+ }
425
+ const spinner = p.spinner();
426
+ spinner.start("\u5B89\u88C5\u4F9D\u8D56\uFF08pnpm install\uFF09\uFF0C\u9996\u6B21\u5B89\u88C5\u53EF\u80FD\u8F83\u6162");
427
+ try {
428
+ await runCommand("pnpm", ["install"], targetDir);
429
+ } catch (error) {
430
+ spinner.stop("\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25");
431
+ throw error;
432
+ }
433
+ spinner.stop("\u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210");
434
+ }
435
+
436
+ function stringify(manifest) {
437
+ return `${JSON.stringify(manifest, null, 2)}
438
+ `;
439
+ }
440
+ function sortKeys(source) {
441
+ return Object.keys(source).sort().reduce((sorted, key) => {
442
+ sorted[key] = source[key];
443
+ return sorted;
444
+ }, {});
445
+ }
446
+ function addDependency(packageJsonContent, name, version, field = "dependencies") {
447
+ const manifest = JSON.parse(packageJsonContent);
448
+ const dependencies = manifest[field] ?? {};
449
+ if (dependencies[name] === version) {
450
+ return packageJsonContent;
451
+ }
452
+ manifest[field] = sortKeys({ ...dependencies, [name]: version });
453
+ return stringify(manifest);
454
+ }
455
+ function addScript(packageJsonContent, key, value) {
456
+ const manifest = JSON.parse(packageJsonContent);
457
+ const scripts = manifest.scripts ?? {};
458
+ if (scripts[key] === value) {
459
+ return packageJsonContent;
460
+ }
461
+ manifest.scripts = { ...scripts, [key]: value };
462
+ return stringify(manifest);
463
+ }
464
+ function addReExport(indexContent, specifier) {
465
+ const statement = `export * from '${specifier}';`;
466
+ const lines = indexContent.split("\n");
467
+ if (lines.some((line) => line.trim() === statement)) {
468
+ return indexContent;
469
+ }
470
+ const trimmed = indexContent.replace(/\s+$/, "");
471
+ return trimmed ? `${trimmed}
472
+
473
+ ${statement}
474
+ ` : `${statement}
475
+ `;
476
+ }
477
+ function addIgnoreEntry(configContent, packageName) {
478
+ const config = JSON.parse(configContent);
479
+ const ignore = config.ignore ?? [];
480
+ if (ignore.includes(packageName)) {
481
+ return configContent;
482
+ }
483
+ config.ignore = [...ignore, packageName];
484
+ return stringify(config);
485
+ }
486
+ function insertBeforeAnchor(content, anchor, snippet) {
487
+ const lines = content.split("\n");
488
+ const index = lines.findIndex((line) => line.includes(anchor));
489
+ if (index === -1) {
490
+ return void 0;
491
+ }
492
+ const anchorLine = lines[index];
493
+ const indent = anchorLine.slice(0, anchorLine.length - anchorLine.trimStart().length);
494
+ const snippetLines = snippet.split("\n").map((line) => line ? `${indent}${line}` : line);
495
+ lines.splice(index, 0, ...snippetLines);
496
+ return lines.join("\n");
497
+ }
498
+
499
+ function readManifest(filePath) {
500
+ if (!fs.existsSync(filePath)) {
501
+ return void 0;
502
+ }
503
+ try {
504
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
505
+ } catch {
506
+ return void 0;
507
+ }
508
+ }
509
+ function assertWorkspaceRoot(projectRoot) {
510
+ const hasManifest = fs.existsSync(path.join(projectRoot, "package.json"));
511
+ const hasWorkspace = fs.existsSync(path.join(projectRoot, "pnpm-workspace.yaml"));
512
+ if (!hasManifest || !hasWorkspace) {
513
+ throw new Error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F pnpm monorepo \u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9700\u540C\u65F6\u5B58\u5728 package.json \u4E0E pnpm-workspace.yaml\uFF09\u3002");
514
+ }
515
+ }
516
+ function listWorkspacePackages(projectRoot) {
517
+ const packagesDir = path.join(projectRoot, "packages");
518
+ if (!fs.existsSync(packagesDir)) {
519
+ return [];
520
+ }
521
+ return fs.readdirSync(packagesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
522
+ const dirPath = path.join(packagesDir, entry.name);
523
+ const manifestPath = path.join(dirPath, "package.json");
524
+ const manifest = readManifest(manifestPath);
525
+ return manifest ? { dirName: entry.name, dirPath, manifestPath, manifest } : void 0;
526
+ }).filter((item) => Boolean(item));
527
+ }
528
+ function findWorkspacePackage(projectRoot, dirName) {
529
+ return listWorkspacePackages(projectRoot).find((item) => item.dirName === dirName);
530
+ }
531
+ function detectScope(projectRoot) {
532
+ const packages = listWorkspacePackages(projectRoot);
533
+ const candidates = [
534
+ packages.find((item) => item.dirName === "ui"),
535
+ ...packages
536
+ ].filter((item) => Boolean(item));
537
+ for (const item of candidates) {
538
+ const name = item.manifest.name;
539
+ if (name?.startsWith("@") && name.includes("/")) {
540
+ return name.split("/")[0];
541
+ }
542
+ }
543
+ return `@${toKebabCase(path.basename(projectRoot)) || "app"}`;
544
+ }
545
+ function hasVueDependency(manifest) {
546
+ const fields = ["dependencies", "devDependencies", "peerDependencies"];
547
+ return fields.some((field) => Boolean(manifest[field]?.vue));
548
+ }
549
+
550
+ function appGenerator(inputName, options, context) {
551
+ assertWorkspaceRoot(context.projectRoot);
552
+ const tokens = createNameTokens(inputName);
553
+ const appDir = path.join(context.projectRoot, "apps", tokens.kebab);
554
+ if (fs.existsSync(appDir)) {
555
+ throw new Error(`apps/${tokens.kebab} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u6362\u4E00\u4E2A\u540D\u79F0\u3002`);
556
+ }
557
+ const scope = detectScope(context.projectRoot);
558
+ const packageName = `${scope}/${tokens.kebab}`;
559
+ const packages = listWorkspacePackages(context.projectRoot);
560
+ const port = options.port ? Number(options.port) : DEFAULT_PORT + 1;
561
+ const workspaceDependencies = packages.map((item) => `,
562
+ "${item.manifest.name}": "${WORKSPACE_PROTOCOL}"`).join("");
563
+ const workspaceAliases = packages.map(
564
+ (item) => ` '${item.manifest.name}': fileURLToPath(new URL('../../packages/${item.dirName}/src/index.ts', import.meta.url)),`
565
+ ).join("\n");
566
+ copyAndRecord(context, resolveTemplate(`app-${options.template ?? "web"}`), appDir, {
567
+ vars: {
568
+ scope,
569
+ projectName: tokens.kebab,
570
+ appDirName: tokens.kebab,
571
+ port,
572
+ workspaceDependencies,
573
+ workspaceAliases,
574
+ farrisDependency: options.ui === "none" ? "" : `,
575
+ "@farris/ui-vue": "${FARRIS_UI_VUE_VERSION}"`,
576
+ vueVersion: VUE_VERSION$1
577
+ }
578
+ });
579
+ context.info(`package: ${packageName} \u7AEF\u53E3: ${port}`);
580
+ updateRootScripts(context, tokens.kebab, packageName);
581
+ updateChangesetIgnore(context, packageName);
582
+ }
583
+ function updateRootScripts(context, appName, packageName) {
584
+ const manifestPath = path.join(context.projectRoot, "package.json");
585
+ if (!fs.existsSync(manifestPath)) {
586
+ return;
587
+ }
588
+ let content = fs.readFileSync(manifestPath, "utf8");
589
+ content = addScript(content, `${appName}:dev`, `pnpm --filter ${packageName} dev`);
590
+ content = addScript(content, `${appName}:build`, `pnpm --filter ${packageName} build`);
591
+ context.applyChange(manifestPath, content);
592
+ }
593
+ function updateChangesetIgnore(context, packageName) {
594
+ const configPath = path.join(context.projectRoot, ".changeset", "config.json");
595
+ if (!fs.existsSync(configPath)) {
596
+ context.warn("\u672A\u627E\u5230 .changeset/config.json\uFF0C\u8BF7\u624B\u52A8\u628A\u65B0\u5E94\u7528\u52A0\u5165 ignore\u3002");
597
+ return;
598
+ }
599
+ context.applyChange(configPath, addIgnoreEntry(fs.readFileSync(configPath, "utf8"), packageName));
600
+ }
601
+
602
+ function componentGenerator(inputName, options, context) {
603
+ assertWorkspaceRoot(context.projectRoot);
604
+ const naming = createComponentNaming(inputName);
605
+ const targetDir = path.join(context.projectRoot, "packages", naming.dirName);
606
+ if (fs.existsSync(targetDir)) {
607
+ throw new Error(`packages/${naming.dirName} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u6362\u4E00\u4E2A\u7EC4\u4EF6\u540D\u3002`);
608
+ }
609
+ const aggregatorName = options.aggregator ?? "ui";
610
+ const scope = detectScope(context.projectRoot);
611
+ const packageName = `${scope}/${naming.packageName}`;
612
+ const skip = [];
613
+ if (!options.withSub) {
614
+ skip.push("components");
615
+ }
616
+ if (options.skipTests) {
617
+ skip.push("__tests__");
618
+ }
619
+ copyAndRecord(context, resolveTemplate("component"), targetDir, {
620
+ vars: { ...naming, scope, packageName, workspaceProtocol: WORKSPACE_PROTOCOL },
621
+ skip
622
+ });
623
+ context.info(`package: ${packageName}`);
624
+ context.info(`\u7EC4\u4EF6\u540D: ${naming.componentName} \u9009\u62E9\u5668: ${naming.selector}`);
625
+ wireIntoAggregator(context, aggregatorName, packageName);
626
+ }
627
+ function wireIntoAggregator(context, aggregatorName, packageName) {
628
+ const aggregatorDir = path.join(context.projectRoot, "packages", aggregatorName);
629
+ const manifestPath = path.join(aggregatorDir, "package.json");
630
+ if (!readManifest(manifestPath)) {
631
+ context.warn(`\u672A\u627E\u5230\u805A\u5408\u5305 packages/${aggregatorName}\uFF0C\u8BF7\u624B\u52A8\u63A5\u7EBF\u4F9D\u8D56\u4E0E\u5BFC\u51FA\u3002`);
632
+ return;
633
+ }
634
+ context.applyChange(
635
+ manifestPath,
636
+ addDependency(fs.readFileSync(manifestPath, "utf8"), packageName, WORKSPACE_PROTOCOL)
637
+ );
638
+ const indexPath = path.join(aggregatorDir, "src", "index.ts");
639
+ const exists = fs.existsSync(indexPath);
640
+ const indexContent = exists ? fs.readFileSync(indexPath, "utf8") : "";
641
+ const nextIndex = addReExport(indexContent, packageName);
642
+ if (exists) {
643
+ context.applyChange(indexPath, nextIndex);
644
+ } else {
645
+ context.applyCreate(indexPath, nextIndex);
646
+ }
647
+ }
648
+
649
+ function stripUsePrefix(input) {
650
+ return input.replace(/^use(?=[-_ ])/i, "").replace(/^use(?=[A-Z])/, "");
651
+ }
652
+ function hookGenerator(inputName, options, context) {
653
+ assertWorkspaceRoot(context.projectRoot);
654
+ const tokens = createNameTokens(stripUsePrefix(inputName));
655
+ const packageDirName = options.pkg ?? "hooks";
656
+ const targetPackage = findWorkspacePackage(context.projectRoot, packageDirName);
657
+ if (!targetPackage) {
658
+ throw new Error(
659
+ `packages/${packageDirName} \u4E0D\u5B58\u5728\u3002\u8BF7\u5148\u6267\u884C\uFF1Acvw g pkg ${packageDirName} --vue`
660
+ );
661
+ }
662
+ if (!hasVueDependency(targetPackage.manifest)) {
663
+ context.warn(`packages/${packageDirName} \u672A\u58F0\u660E vue \u4F9D\u8D56\uFF0C\u751F\u6210\u7684 composable \u53EF\u80FD\u65E0\u6CD5\u901A\u8FC7\u7C7B\u578B\u68C0\u67E5\u3002`);
664
+ }
665
+ copyAndRecord(
666
+ context,
667
+ resolveTemplate("hook"),
668
+ path.join(targetPackage.dirPath, "src", "composition"),
669
+ { vars: { kebab: tokens.kebab, camel: tokens.camel, pascal: tokens.pascal } }
670
+ );
671
+ appendReExport(context, targetPackage.dirPath, `./composition/use-${tokens.kebab}`);
672
+ context.info(`composable: use${tokens.pascal}`);
673
+ }
674
+ function appendReExport(context, packageDir, specifier) {
675
+ const indexPath = path.join(packageDir, "src", "index.ts");
676
+ const exists = fs.existsSync(indexPath);
677
+ const indexContent = exists ? fs.readFileSync(indexPath, "utf8") : "";
678
+ const nextIndex = addReExport(indexContent, specifier);
679
+ if (exists) {
680
+ context.applyChange(indexPath, nextIndex);
681
+ } else {
682
+ context.applyCreate(indexPath, nextIndex);
683
+ }
684
+ }
685
+
686
+ const VUE_VERSION = "^3.5.0";
687
+ function packageGenerator(inputName, options, context) {
688
+ assertWorkspaceRoot(context.projectRoot);
689
+ const tokens = createNameTokens(inputName);
690
+ const targetDir = path.join(context.projectRoot, "packages", tokens.kebab);
691
+ if (fs.existsSync(targetDir)) {
692
+ throw new Error(`packages/${tokens.kebab} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u6362\u4E00\u4E2A\u540D\u79F0\u3002`);
693
+ }
694
+ const scope = detectScope(context.projectRoot);
695
+ const packageName = `${scope}/${tokens.kebab}`;
696
+ copyAndRecord(context, resolveTemplate("pkg-lib"), targetDir, {
697
+ vars: {
698
+ kebab: tokens.kebab,
699
+ camel: tokens.camel,
700
+ pascal: tokens.pascal,
701
+ scope,
702
+ packageName,
703
+ vueDevDependency: options.vue ? `,
704
+ "vue": "${VUE_VERSION}"` : "",
705
+ vuePeerDependencies: options.vue ? `,
706
+ "peerDependencies": {
707
+ "vue": "${VUE_VERSION}"
708
+ }` : ""
709
+ }
710
+ });
711
+ context.info(`package: ${packageName}`);
712
+ }
713
+
714
+ function utilGenerator(inputName, options, context) {
715
+ assertWorkspaceRoot(context.projectRoot);
716
+ const tokens = createNameTokens(inputName);
717
+ const packageDirName = options.pkg ?? "utils";
718
+ const targetPackage = findWorkspacePackage(context.projectRoot, packageDirName);
719
+ if (!targetPackage) {
720
+ throw new Error(`packages/${packageDirName} \u4E0D\u5B58\u5728\u3002\u8BF7\u5148\u6267\u884C\uFF1Acvw g pkg ${packageDirName}`);
721
+ }
722
+ copyAndRecord(context, resolveTemplate("util"), path.join(targetPackage.dirPath, "src"), {
723
+ vars: { kebab: tokens.kebab, camel: tokens.camel, pascal: tokens.pascal }
724
+ });
725
+ appendReExport(context, targetPackage.dirPath, `./${tokens.kebab}`);
726
+ context.info(`export: ${tokens.camel}`);
727
+ }
728
+
729
+ function viewGenerator(inputName, options, context) {
730
+ assertWorkspaceRoot(context.projectRoot);
731
+ const tokens = createPathTokens(inputName);
732
+ const appDirName = options.app ?? "web";
733
+ const appDir = path.join(context.projectRoot, "apps", appDirName);
734
+ if (!fs.existsSync(appDir)) {
735
+ throw new Error(`apps/${appDirName} \u4E0D\u5B58\u5728\u3002\u8BF7\u68C0\u67E5 --app \u53C2\u6570\uFF0C\u6216\u5148\u6267\u884C\uFF1Acvw g app ${appDirName}`);
736
+ }
737
+ copyAndRecord(
738
+ context,
739
+ resolveTemplate("view"),
740
+ path.join(appDir, "src", "views", path.dirname(tokens.dirPath)),
741
+ {
742
+ vars: {
743
+ kebab: tokens.kebab,
744
+ camel: tokens.camel,
745
+ pascal: tokens.compositePascal,
746
+ fileName: tokens.fileName
747
+ }
748
+ }
749
+ );
750
+ if (options.skipRoute) {
751
+ context.info("\u5DF2\u8DF3\u8FC7\u8DEF\u7531\u6CE8\u518C\uFF08--skip-route\uFF09");
752
+ return;
753
+ }
754
+ registerRoute(context, appDirName, appDir, tokens, options.route);
755
+ }
756
+ function registerRoute(context, appDirName, appDir, tokens, routeOption) {
757
+ const routerPath = path.join(appDir, "src", "router", "index.ts");
758
+ if (!fs.existsSync(routerPath)) {
759
+ context.warn(`\u672A\u627E\u5230 apps/${appDirName}/src/router/index.ts\uFF0C\u8BF7\u624B\u52A8\u6CE8\u518C\u8DEF\u7531\u3002`);
760
+ return;
761
+ }
762
+ const snippet = [
763
+ "{",
764
+ ` path: '${routeOption ?? `/${tokens.dirPath}`}',`,
765
+ ` name: '${tokens.kebab}',`,
766
+ ` component: () => import('../views/${tokens.dirPath}.vue'),`,
767
+ "},"
768
+ ].join("\n");
769
+ const next = insertBeforeAnchor(fs.readFileSync(routerPath, "utf8"), ROUTE_ANCHOR, snippet);
770
+ if (!next) {
771
+ context.warn(`\u672A\u627E\u5230 ${ROUTE_ANCHOR} \u951A\u70B9\uFF0C\u8BF7\u624B\u52A8\u6CE8\u518C\u8DEF\u7531\u3002`);
772
+ return;
773
+ }
774
+ context.applyChange(routerPath, next);
775
+ }
776
+
777
+ const GENERATORS = [
778
+ {
779
+ type: "component",
780
+ aliases: ["c"],
781
+ description: "\u751F\u6210\u72EC\u7ACB\u7EC4\u4EF6\u5305\uFF0C\u5E76\u63A5\u7EBF\u805A\u5408\u5305",
782
+ run: componentGenerator
783
+ },
784
+ {
785
+ type: "package",
786
+ aliases: ["pkg"],
787
+ description: "\u751F\u6210\u901A\u7528 package",
788
+ run: packageGenerator
789
+ },
790
+ {
791
+ type: "hook",
792
+ aliases: ["h"],
793
+ description: "\u751F\u6210 composable",
794
+ run: hookGenerator
795
+ },
796
+ {
797
+ type: "view",
798
+ aliases: ["v"],
799
+ description: "\u751F\u6210\u9875\u9762\u5E76\u6CE8\u518C\u8DEF\u7531",
800
+ run: viewGenerator
801
+ },
802
+ {
803
+ type: "util",
804
+ aliases: ["u"],
805
+ description: "\u751F\u6210\u5DE5\u5177\u51FD\u6570\u6A21\u5757",
806
+ run: utilGenerator
807
+ },
808
+ {
809
+ type: "app",
810
+ aliases: ["a", "application"],
811
+ description: "\u65B0\u589E\u5E94\u7528",
812
+ run: appGenerator
813
+ }
814
+ ];
815
+ function resolveGenerator(type) {
816
+ const normalized = type.trim().toLowerCase();
817
+ return GENERATORS.find(
818
+ (item) => item.type === normalized || item.aliases.includes(normalized)
819
+ );
820
+ }
821
+ function formatGeneratorList() {
822
+ return GENERATORS.map(
823
+ (item) => ` ${item.type.padEnd(11)}${item.aliases.join("/").padEnd(15)}${item.description}`
824
+ ).join("\n");
825
+ }
826
+
827
+ function unwrap(value) {
828
+ if (p.isCancel(value)) {
829
+ p.cancel("\u5DF2\u53D6\u6D88\u3002");
830
+ process.exit(0);
831
+ }
832
+ return value;
833
+ }
834
+ async function resolveGenerateInput(type, name, options) {
835
+ const interactive = !options.yes && Boolean(process.stdout.isTTY);
836
+ let generator = type ? resolveGenerator(type) : void 0;
837
+ if (type && !generator) {
838
+ throw new Error(`\u672A\u77E5\u7684\u751F\u6210\u5668 "${type}"\u3002\u53EF\u9009\u9879\uFF1A
839
+ ${formatGeneratorList()}`);
840
+ }
841
+ if (!generator) {
842
+ if (!interactive) {
843
+ throw new Error(`\u7F3A\u5C11\u751F\u6210\u5668\u7C7B\u578B\uFF0C\u7528\u6CD5\uFF1Acvw g <type> <name>\u3002\u53EF\u9009\u9879\uFF1A
844
+ ${formatGeneratorList()}`);
845
+ }
846
+ const selected = unwrap(
847
+ await p.select({
848
+ message: "\u9009\u62E9\u751F\u6210\u5668",
849
+ options: GENERATORS.map((item) => ({
850
+ value: item.type,
851
+ label: `${item.type} (${item.aliases.join("/")})`,
852
+ hint: item.description
853
+ }))
854
+ })
855
+ );
856
+ generator = resolveGenerator(selected);
857
+ }
858
+ if (!generator) {
859
+ throw new Error("\u672A\u80FD\u89E3\u6790\u751F\u6210\u5668\u7C7B\u578B\u3002");
860
+ }
861
+ let resolvedName = name;
862
+ if (!resolvedName) {
863
+ if (!interactive) {
864
+ throw new Error(`\u7F3A\u5C11\u540D\u79F0\uFF0C\u7528\u6CD5\uFF1Acvw g ${generator.type} <name>`);
865
+ }
866
+ resolvedName = unwrap(
867
+ await p.text({ message: `${generator.type} \u540D\u79F0`, placeholder: "my-name" })
868
+ );
869
+ }
870
+ return { generator, name: resolvedName };
871
+ }
872
+
873
+ function handleCommand(task) {
874
+ Promise.resolve().then(task).catch((error) => {
875
+ const message = error instanceof Error ? error.message : String(error);
876
+ console.error(`
877
+ \u2716 ${message}
878
+ `);
879
+ process.exitCode = 1;
880
+ });
881
+ }
882
+ function registerGenerate(cli, commandName, showList) {
883
+ const description = showList ? `\u8FD0\u884C\u751F\u6210\u5668\u3002type \u53EF\u7701\u7565\u4EE5\u4EA4\u4E92\u9009\u62E9\u3002
884
+ \u53EF\u7528\u751F\u6210\u5668\uFF1A
885
+ ${formatGeneratorList()}` : "generate \u7684\u522B\u540D\uFF0C\u7528\u6CD5\u4E0E\u9009\u9879\u5B8C\u5168\u4E00\u81F4";
886
+ cli.command(`${commandName} [type] [name]`, description).option("--dry-run", "\u53EA\u6253\u5370\u5C06\u751F\u6210\u7684\u6587\u4EF6\uFF0C\u4E0D\u843D\u76D8").option("--aggregator <pkg>", "\u7EC4\u4EF6\u63A5\u7EBF\u5230\u7684\u805A\u5408\u5305\uFF08component\uFF09", { default: "ui" }).option("--pkg <pkg>", "\u76EE\u6807 package\uFF08hook / util\uFF09").option("--app <app>", "\u76EE\u6807\u5E94\u7528\uFF08view / app\uFF09", { default: "web" }).option("--route <path>", "\u8DEF\u7531\u8DEF\u5F84\uFF08view\uFF09").option("--skip-route", "\u4E0D\u81EA\u52A8\u6CE8\u518C\u8DEF\u7531\uFF08view\uFF09").option("--skip-tests", "\u4E0D\u751F\u6210\u5355\u5143\u6D4B\u8BD5").option("--with-sub", "\u751F\u6210\u5B50\u7EC4\u4EF6\u5360\u4F4D\uFF08component\uFF09").option("--port <port>", "\u5E94\u7528\u7AEF\u53E3\uFF08app\uFF09").option("--template <template>", "\u5E94\u7528\u6A21\u677F\uFF08app\uFF09", { default: "web" }).option("--vue", "\u9644\u5E26 vue \u4F9D\u8D56\uFF08package\uFF09").option("--ui <provider>", "UI \u57FA\u7840\u5E93\uFF08app\uFF09\uFF1Afarris \u6216 none", { default: "farris" }).option("-y, --yes", "\u975E\u4EA4\u4E92\u6267\u884C").action((type, name, options) => {
887
+ handleCommand(async () => {
888
+ if (type === "list") {
889
+ console.log(formatGeneratorList());
890
+ return;
891
+ }
892
+ const { generator, name: resolvedName } = await resolveGenerateInput(type, name, options);
893
+ const context = createGenerationContext(process.cwd(), options.dryRun === true);
894
+ generator.run(resolvedName, options, context);
895
+ });
896
+ });
897
+ }
898
+ function runCli(argv = process.argv) {
899
+ const cli = cac(CLI_NAME);
900
+ cli.command("new [name]", "\u521B\u5EFA Vue 3 + Vite + TypeScript \u7684 pnpm monorepo \u5DE5\u4F5C\u533A").option("--scope <scope>", "\u751F\u6210\u7269\u7684\u5305 scope\uFF0C\u5982 @my-app").option("--packages <list>", "\u521D\u59CB\u521B\u5EFA\u7684 packages\uFF0C\u9017\u53F7\u5206\u9694", { default: "ui,utils" }).option("--ui <provider>", "UI \u57FA\u7840\u5E93\uFF1Afarris \u6216 none", { default: "farris" }).option("--port <port>", "\u5E94\u7528\u5F00\u53D1\u7AEF\u53E3").option("--skip-install", "\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5").option("--skip-git", "\u8DF3\u8FC7 git \u521D\u59CB\u5316").option("--dry-run", "\u53EA\u6253\u5370\u5C06\u751F\u6210\u7684\u6587\u4EF6\uFF0C\u4E0D\u843D\u76D8").option("-y, --yes", "\u5168\u90E8\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u975E\u4EA4\u4E92\u6267\u884C").action((name, options) => {
901
+ handleCommand(() => newCommand(name, options));
902
+ });
903
+ registerGenerate(cli, "generate", true);
904
+ registerGenerate(cli, "g", false);
905
+ cli.help();
906
+ cli.version(VERSION);
907
+ cli.parse(argv);
908
+ }
909
+
910
+ runCli();