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.
- package/dist/index.js +260 -78
- 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
|
|
5
|
+
import chalk3 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/prompts.ts
|
|
8
|
-
import
|
|
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/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
|
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
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
445
|
+
import chalk2 from "chalk";
|
|
298
446
|
var logger = {
|
|
299
447
|
info: (message) => {
|
|
300
|
-
console.log(
|
|
448
|
+
console.log(chalk2.blue("\u2139"), message);
|
|
301
449
|
},
|
|
302
450
|
success: (message) => {
|
|
303
|
-
console.log(
|
|
451
|
+
console.log(chalk2.green("\u2714"), message);
|
|
304
452
|
},
|
|
305
453
|
warning: (message) => {
|
|
306
|
-
console.log(
|
|
454
|
+
console.log(chalk2.yellow("\u26A0"), message);
|
|
307
455
|
},
|
|
308
456
|
error: (message) => {
|
|
309
|
-
console.log(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
635
|
+
logger.success(chalk3.green.bold("\u{1F389} Project created successfully!"));
|
|
454
636
|
console.log();
|
|
455
|
-
logger.log(
|
|
456
|
-
logger.log(` cd ${
|
|
457
|
-
logger.log(` ${
|
|
458
|
-
logger.log(` ${
|
|
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(
|
|
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": "
|
|
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": "^
|
|
24
|
+
"commander": "^14.0.2",
|
|
22
25
|
"degit": "^2.8.4",
|
|
23
|
-
"execa": "^
|
|
26
|
+
"execa": "^9.6.1",
|
|
24
27
|
"fs-extra": "^11.2.0",
|
|
25
|
-
"
|
|
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"
|