create-for-yeyu 2.1.0 → 3.0.1

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 (2) hide show
  1. package/dist/index.js +260 -78
  2. package/package.json +7 -6
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
- import chalk2 from "chalk";
5
+ import chalk3 from "chalk";
6
6
 
7
7
  // src/prompts.ts
8
- import inquirer from "inquirer";
8
+ import { input, Separator } from "@inquirer/prompts";
9
9
 
10
10
  // src/templates.ts
11
11
  var templates = [
@@ -61,25 +61,167 @@ async function removeDirectory(projectName) {
61
61
  await fs.remove(targetPath);
62
62
  }
63
63
 
64
- // src/prompts.ts
65
- async function promptProjectName() {
66
- const { projectName } = await inquirer.prompt([
67
- {
68
- type: "input",
69
- name: "projectName",
70
- message: "Enter project name:",
71
- default: "my-project",
72
- validate: (input) => {
73
- if (!input.trim()) {
74
- return "Project name cannot be empty";
64
+ // src/utils/vim-select.ts
65
+ import {
66
+ createPrompt,
67
+ useState,
68
+ useKeypress,
69
+ usePrefix,
70
+ useMemo,
71
+ makeTheme,
72
+ isUpKey,
73
+ isDownKey,
74
+ isEnterKey
75
+ } from "@inquirer/core";
76
+ import chalk from "chalk";
77
+ var selectTheme = {
78
+ icon: { cursor: "\u276F" },
79
+ style: {
80
+ disabled: (text) => chalk.dim(`- ${text}`),
81
+ description: (text) => chalk.cyan(text),
82
+ helpTip: (text) => chalk.dim(text)
83
+ },
84
+ helpMode: "auto"
85
+ };
86
+ function isSeparator(item) {
87
+ return "type" in item && item.type === "separator";
88
+ }
89
+ function isSelectableChoice(item) {
90
+ return !isSeparator(item) && !item.disabled;
91
+ }
92
+ function isSeparatorInput(choice) {
93
+ if (typeof choice !== "object" || choice === null) return false;
94
+ const obj = choice;
95
+ return obj.type === "separator" || "separator" in obj;
96
+ }
97
+ function getSeparatorText(choice) {
98
+ if (typeof choice !== "object" || choice === null) return "\u2500".repeat(50);
99
+ const obj = choice;
100
+ return typeof obj.separator === "string" ? obj.separator : "\u2500".repeat(50);
101
+ }
102
+ function normalizeChoices(choices) {
103
+ return choices.map((choice) => {
104
+ if (isSeparatorInput(choice)) {
105
+ const separatorText = getSeparatorText(choice);
106
+ return {
107
+ type: "separator",
108
+ separator: chalk.dim(separatorText)
109
+ };
110
+ }
111
+ const typedChoice = choice;
112
+ const name = typedChoice.name ?? String(typedChoice.value);
113
+ return {
114
+ value: typedChoice.value,
115
+ name,
116
+ description: typedChoice.description,
117
+ disabled: typedChoice.disabled ?? false
118
+ };
119
+ });
120
+ }
121
+ function isCtrlN(key) {
122
+ return key.name === "n" && key.ctrl === true;
123
+ }
124
+ function isCtrlP(key) {
125
+ return key.name === "p" && key.ctrl === true;
126
+ }
127
+ function isJKey(key) {
128
+ return key.name === "j";
129
+ }
130
+ function isKKey(key) {
131
+ return key.name === "k";
132
+ }
133
+ var vimSelect = createPrompt(
134
+ (config, done) => {
135
+ const { loop = true } = config;
136
+ const theme = makeTheme(selectTheme, config.theme);
137
+ const prefix = usePrefix({ status: "idle", theme });
138
+ const items = useMemo(
139
+ () => normalizeChoices(config.choices),
140
+ [config.choices]
141
+ );
142
+ const selectableItems = useMemo(
143
+ () => items.filter(isSelectableChoice),
144
+ [items]
145
+ );
146
+ const defaultIndex = useMemo(() => {
147
+ if (config.default === void 0) return 0;
148
+ const foundIndex = selectableItems.findIndex(
149
+ (item) => item.value === config.default
150
+ );
151
+ return foundIndex === -1 ? 0 : foundIndex;
152
+ }, [config.default, selectableItems]);
153
+ const [active, setActive] = useState(defaultIndex);
154
+ const [status, setStatus] = useState("idle");
155
+ useKeypress((key) => {
156
+ if (isEnterKey(key)) {
157
+ const selectedChoice2 = selectableItems[active];
158
+ if (selectedChoice2) {
159
+ setStatus("done");
160
+ done(selectedChoice2.value);
75
161
  }
76
- if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
77
- return "Project name can only contain letters, numbers, hyphens and underscores";
162
+ } else if (isUpKey(key) || isCtrlP(key) || isKKey(key)) {
163
+ if (loop) {
164
+ setActive(active === 0 ? selectableItems.length - 1 : active - 1);
165
+ } else {
166
+ setActive(Math.max(0, active - 1));
167
+ }
168
+ } else if (isDownKey(key) || isCtrlN(key) || isJKey(key)) {
169
+ if (loop) {
170
+ setActive(active === selectableItems.length - 1 ? 0 : active + 1);
171
+ } else {
172
+ setActive(Math.min(selectableItems.length - 1, active + 1));
78
173
  }
79
- return true;
80
174
  }
175
+ });
176
+ const selectedChoice = selectableItems[active];
177
+ const message = theme.style.message(config.message, status);
178
+ if (status === "done") {
179
+ return `${prefix} ${message} ${theme.style.answer(
180
+ selectedChoice?.name ?? ""
181
+ )}`;
81
182
  }
82
- ]);
183
+ let selectableIndex = 0;
184
+ const choiceLines = items.map((item) => {
185
+ if (isSeparator(item)) {
186
+ return ` ${item.separator}`;
187
+ }
188
+ if (item.disabled) {
189
+ const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
190
+ return theme.style.disabled(`${item.name} ${disabledLabel}`);
191
+ }
192
+ const isActive = selectableIndex === active;
193
+ selectableIndex++;
194
+ const cursor = isActive ? theme.icon.cursor : " ";
195
+ const color = isActive ? theme.style.highlight : (x) => x;
196
+ return color(`${cursor} ${item.name}`);
197
+ });
198
+ const helpTip = theme.helpMode === "always" || theme.helpMode === "auto" && selectableItems.length > 6 ? theme.style.helpTip(
199
+ "(Use arrow keys, j/k, or ctrl+n/ctrl+p to navigate)"
200
+ ) : "";
201
+ const description = selectedChoice?.description ? `
202
+ ${theme.style.description(selectedChoice.description)}` : "";
203
+ return `${prefix} ${message}${helpTip}
204
+ ${choiceLines.join(
205
+ "\n"
206
+ )}${description}`;
207
+ }
208
+ );
209
+
210
+ // src/prompts.ts
211
+ async function promptProjectName() {
212
+ const projectName = await input({
213
+ message: "Enter project name:",
214
+ default: "my-project",
215
+ validate: (value) => {
216
+ if (!value.trim()) {
217
+ return "Project name cannot be empty";
218
+ }
219
+ if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
220
+ return "Project name can only contain letters, numbers, hyphens and underscores";
221
+ }
222
+ return true;
223
+ }
224
+ });
83
225
  return projectName;
84
226
  }
85
227
  async function promptTemplate() {
@@ -90,68 +232,72 @@ async function promptTemplate() {
90
232
  name: `${t.name.padEnd(20)} - ${t.description}`,
91
233
  value: t.value
92
234
  })),
93
- new inquirer.Separator("\u2500".repeat(50)),
235
+ new Separator("\u2500".repeat(50)),
94
236
  ...officialTemplates.map((t) => ({
95
237
  name: `${t.name.padEnd(20)} - ${t.description}`,
96
238
  value: t.value
97
239
  }))
98
240
  ];
99
- const { templateValue } = await inquirer.prompt([
100
- {
101
- type: "list",
102
- name: "templateValue",
103
- message: "Select a project template:",
104
- choices
105
- }
106
- ]);
241
+ const templateValue = await vimSelect({
242
+ message: "Select a project template:",
243
+ choices
244
+ });
107
245
  const selectedTemplate = templates.find((t) => t.value === templateValue);
108
246
  if (!selectedTemplate) {
109
247
  throw new Error(`Template not found: ${templateValue}`);
110
248
  }
111
249
  return selectedTemplate;
112
250
  }
251
+ async function promptInitGit() {
252
+ const initGit = await vimSelect({
253
+ message: "Initialize a new git repository?",
254
+ choices: [
255
+ {
256
+ name: "Yes",
257
+ value: true
258
+ },
259
+ {
260
+ name: "No",
261
+ value: false
262
+ }
263
+ ]
264
+ });
265
+ return initGit;
266
+ }
113
267
  async function promptCustomProjectName() {
114
- const { projectName } = await inquirer.prompt([
115
- {
116
- type: "input",
117
- name: "projectName",
118
- message: "Enter a new project name:",
119
- validate: (input) => {
120
- if (!input.trim()) {
121
- return "Project name cannot be empty";
122
- }
123
- if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
124
- return "Project name can only contain letters, numbers, hyphens and underscores";
125
- }
126
- return true;
268
+ const projectName = await input({
269
+ message: "Enter a new project name:",
270
+ validate: (value) => {
271
+ if (!value.trim()) {
272
+ return "Project name cannot be empty";
273
+ }
274
+ if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
275
+ return "Project name can only contain letters, numbers, hyphens and underscores";
127
276
  }
277
+ return true;
128
278
  }
129
- ]);
279
+ });
130
280
  return projectName;
131
281
  }
132
282
  async function promptConflictResolution(projectName) {
133
283
  const suggestedName = generateUniqueProjectName(projectName);
134
- const { action } = await inquirer.prompt([
135
- {
136
- type: "list",
137
- name: "action",
138
- message: `Directory "${projectName}" already exists. What would you like to do?`,
139
- choices: [
140
- {
141
- name: "Enter a custom name",
142
- value: "custom"
143
- },
144
- {
145
- name: "Overwrite existing directory",
146
- value: "overwrite"
147
- },
148
- {
149
- name: `Rename to "${suggestedName}"`,
150
- value: "rename"
151
- }
152
- ]
153
- }
154
- ]);
284
+ const action = await vimSelect({
285
+ message: `Directory "${projectName}" already exists. What would you like to do?`,
286
+ choices: [
287
+ {
288
+ name: "Enter a custom name",
289
+ value: "custom"
290
+ },
291
+ {
292
+ name: "Overwrite existing directory",
293
+ value: "overwrite"
294
+ },
295
+ {
296
+ name: `Rename to "${suggestedName}"`,
297
+ value: "rename"
298
+ }
299
+ ]
300
+ });
155
301
  if (action === "overwrite") {
156
302
  return {
157
303
  projectName,
@@ -179,7 +325,9 @@ async function resolveProjectName(projectName) {
179
325
 
180
326
  // src/actions/clone-repo.ts
181
327
  import degit from "degit";
328
+ import { execa } from "execa";
182
329
  import path2 from "path";
330
+ import fsExtra from "fs-extra";
183
331
 
184
332
  // src/utils/train-animation.ts
185
333
  import readline from "readline";
@@ -294,19 +442,19 @@ function createTrainAnimation(initialMessage) {
294
442
  }
295
443
 
296
444
  // src/utils/logger.ts
297
- import chalk from "chalk";
445
+ import chalk2 from "chalk";
298
446
  var logger = {
299
447
  info: (message) => {
300
- console.log(chalk.blue("\u2139"), message);
448
+ console.log(chalk2.blue("\u2139"), message);
301
449
  },
302
450
  success: (message) => {
303
- console.log(chalk.green("\u2714"), message);
451
+ console.log(chalk2.green("\u2714"), message);
304
452
  },
305
453
  warning: (message) => {
306
- console.log(chalk.yellow("\u26A0"), message);
454
+ console.log(chalk2.yellow("\u26A0"), message);
307
455
  },
308
456
  error: (message) => {
309
- console.log(chalk.red("\u2716"), message);
457
+ console.log(chalk2.red("\u2716"), message);
310
458
  },
311
459
  log: (message) => {
312
460
  console.log(message);
@@ -314,7 +462,29 @@ var logger = {
314
462
  };
315
463
 
316
464
  // src/actions/clone-repo.ts
317
- async function cloneRepo(repo, projectName) {
465
+ async function updatePackageJsonName(targetPath, projectName) {
466
+ try {
467
+ const packageJsonPath = path2.join(targetPath, "package.json");
468
+ const packageJsonContent = await fsExtra.readFile(packageJsonPath, "utf-8");
469
+ const packageJson = JSON.parse(packageJsonContent);
470
+ const newPackageJson = {
471
+ name: projectName
472
+ };
473
+ for (const [key, value] of Object.entries(packageJson)) {
474
+ if (key !== "name") {
475
+ newPackageJson[key] = value;
476
+ }
477
+ }
478
+ await fsExtra.writeFile(
479
+ packageJsonPath,
480
+ JSON.stringify(newPackageJson, null, 2) + "\n"
481
+ );
482
+ } catch (error) {
483
+ logger.warning("Failed to update package.json name field");
484
+ }
485
+ }
486
+ async function cloneRepo(repo, projectName, options = {}) {
487
+ const { initGit = true } = options;
318
488
  const targetPath = path2.resolve(process.cwd(), projectName);
319
489
  const train = createTrainAnimation(`Cloning template ${repo}...`);
320
490
  train.start();
@@ -326,6 +496,17 @@ async function cloneRepo(repo, projectName) {
326
496
  });
327
497
  await emitter.clone(targetPath);
328
498
  train.stop(true, "Template cloned successfully!");
499
+ await updatePackageJsonName(targetPath, projectName);
500
+ if (initGit) {
501
+ await execa("git", ["init"], { cwd: targetPath });
502
+ await execa("git", ["add", "."], { cwd: targetPath });
503
+ await execa(
504
+ "git",
505
+ ["commit", "-m", "initial commit from create-for-yeyu", "--no-verify"],
506
+ { cwd: targetPath }
507
+ );
508
+ logger.success("Git repository initialized with initial commit");
509
+ }
329
510
  } catch (error) {
330
511
  train.stop(false, "Failed to clone template");
331
512
  if (error instanceof Error) {
@@ -336,12 +517,12 @@ async function cloneRepo(repo, projectName) {
336
517
  }
337
518
 
338
519
  // src/actions/create-vite.ts
339
- import { execa } from "execa";
520
+ import { execa as execa2 } from "execa";
340
521
  async function createViteProject(projectName) {
341
522
  logger.info("Creating project with Vite...");
342
523
  logger.log("");
343
524
  try {
344
- await execa("npm", ["create", "vite@latest", projectName], {
525
+ await execa2("npm", ["create", "vite@latest", projectName], {
345
526
  stdio: "inherit",
346
527
  cwd: process.cwd()
347
528
  });
@@ -354,12 +535,12 @@ async function createViteProject(projectName) {
354
535
  }
355
536
 
356
537
  // src/actions/create-next.ts
357
- import { execa as execa2 } from "execa";
538
+ import { execa as execa3 } from "execa";
358
539
  async function createNextProject(projectName) {
359
540
  logger.info("Creating project with Create Next App...");
360
541
  logger.log("");
361
542
  try {
362
- await execa2("npx", ["create-next-app@latest", projectName], {
543
+ await execa3("npx", ["create-next-app@latest", projectName], {
363
544
  stdio: "inherit",
364
545
  cwd: process.cwd()
365
546
  });
@@ -440,7 +621,8 @@ async function executeTemplate(template, projectName, shouldOverwrite) {
440
621
  logger.error("Template repository not configured");
441
622
  process.exit(1);
442
623
  }
443
- await cloneRepo(template.repo, projectName);
624
+ const initGit = await promptInitGit();
625
+ await cloneRepo(template.repo, projectName, { initGit });
444
626
  printSuccessMessage(projectName);
445
627
  } else if (template.type === "vite") {
446
628
  await createViteProject(projectName);
@@ -450,17 +632,17 @@ async function executeTemplate(template, projectName, shouldOverwrite) {
450
632
  }
451
633
  function printSuccessMessage(projectName) {
452
634
  console.log();
453
- logger.success(chalk2.green.bold("\u{1F389} Project created successfully!"));
635
+ logger.success(chalk3.green.bold("\u{1F389} Project created successfully!"));
454
636
  console.log();
455
- logger.log(chalk2.cyan(" Next steps:"));
456
- logger.log(` cd ${chalk2.yellow(projectName)}`);
457
- logger.log(` ${chalk2.yellow("pnpm install")}`);
458
- logger.log(` ${chalk2.yellow("pnpm dev")}`);
637
+ logger.log(chalk3.cyan(" Next steps:"));
638
+ logger.log(` cd ${chalk3.yellow(projectName)}`);
639
+ logger.log(` ${chalk3.yellow("pnpm install")}`);
640
+ logger.log(` ${chalk3.yellow("pnpm dev")}`);
459
641
  console.log();
460
642
  }
461
643
  function printBanner() {
462
644
  console.log();
463
- console.log(chalk2.cyan(cowsay(getGreetingMessage())));
645
+ console.log(chalk3.cyan(cowsay(getGreetingMessage())));
464
646
  console.log();
465
647
  }
466
648
  async function run() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-for-yeyu",
3
- "version": "2.1.0",
3
+ "version": "3.0.1",
4
4
  "description": "A CLI tool to scaffold projects from templates",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,20 +17,21 @@
17
17
  "author": "yeyu",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
+ "@inquirer/core": "^11.1.0",
21
+ "@inquirer/prompts": "^7.2.1",
22
+ "@inquirer/type": "^4.0.2",
20
23
  "chalk": "^5.3.0",
21
- "commander": "^12.0.0",
24
+ "commander": "^14.0.2",
22
25
  "degit": "^2.8.4",
23
- "execa": "^8.0.1",
26
+ "execa": "^9.6.1",
24
27
  "fs-extra": "^11.2.0",
25
- "inquirer": "^9.2.12",
26
- "ora": "^8.0.1"
28
+ "ora": "^9.0.0"
27
29
  },
28
30
  "devDependencies": {
29
31
  "@changesets/cli": "^2.29.8",
30
32
  "@types/chalk": "^2.2.4",
31
33
  "@types/degit": "^2.8.6",
32
34
  "@types/fs-extra": "^11.0.4",
33
- "@types/inquirer": "^9.0.7",
34
35
  "@types/node": "^20.11.0",
35
36
  "tsup": "^8.0.1",
36
37
  "typescript": "^5.3.3"