create-blitzpack 0.1.18 → 0.1.20
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/README.md +5 -3
- package/dist/index.js +565 -259
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -31,9 +31,11 @@ pnpm create blitzpack [project-name] [options]
|
|
|
31
31
|
|
|
32
32
|
The scaffold wizard supports three profiles:
|
|
33
33
|
|
|
34
|
-
- **Recommended**:
|
|
35
|
-
- **Platform-
|
|
36
|
-
- **
|
|
34
|
+
- **Recommended**: Core app with everything included.
|
|
35
|
+
- **Platform-Agnostic**: Core app without dockerfiles.
|
|
36
|
+
- **Modular**: Core app with features of your choice.
|
|
37
|
+
|
|
38
|
+
Each profile is a setup path. The **Modular** path opens full feature customization before files are created.
|
|
37
39
|
|
|
38
40
|
## Requirements
|
|
39
41
|
|
package/dist/index.js
CHANGED
|
@@ -7,16 +7,85 @@ import { dirname, join } from "path";
|
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
9
9
|
// src/commands/create.ts
|
|
10
|
+
import { confirm as confirm2, isCancel as isCancel2 } from "@clack/prompts";
|
|
10
11
|
import chalk4 from "chalk";
|
|
11
12
|
import { spawn } from "child_process";
|
|
12
13
|
import fs3 from "fs-extra";
|
|
13
|
-
import
|
|
14
|
+
import ora2 from "ora";
|
|
14
15
|
import path4 from "path";
|
|
15
|
-
import prompts2 from "prompts";
|
|
16
16
|
|
|
17
17
|
// src/checks.ts
|
|
18
18
|
import chalk from "chalk";
|
|
19
|
+
import { execSync as execSync3 } from "child_process";
|
|
20
|
+
import ora from "ora";
|
|
21
|
+
|
|
22
|
+
// src/docker.ts
|
|
19
23
|
import { execSync } from "child_process";
|
|
24
|
+
function isDockerInstalled() {
|
|
25
|
+
try {
|
|
26
|
+
execSync("docker --version", { stdio: "ignore" });
|
|
27
|
+
return true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function isDockerRunning() {
|
|
33
|
+
try {
|
|
34
|
+
execSync("docker info", { stdio: "ignore", timeout: 3e3 });
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function runDockerCompose(targetDir) {
|
|
41
|
+
try {
|
|
42
|
+
execSync("docker compose up -d", {
|
|
43
|
+
cwd: targetDir,
|
|
44
|
+
stdio: "inherit"
|
|
45
|
+
});
|
|
46
|
+
return true;
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function runDatabaseMigrations(targetDir) {
|
|
52
|
+
try {
|
|
53
|
+
const isWindows = process.platform === "win32";
|
|
54
|
+
execSync(isWindows ? "pnpm.cmd db:migrate" : "pnpm db:migrate", {
|
|
55
|
+
cwd: targetDir,
|
|
56
|
+
stdio: "inherit"
|
|
57
|
+
});
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/git.ts
|
|
65
|
+
import { execSync as execSync2 } from "child_process";
|
|
66
|
+
function isGitInstalled() {
|
|
67
|
+
try {
|
|
68
|
+
execSync2("git --version", { stdio: "ignore" });
|
|
69
|
+
return true;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function initGit(targetDir) {
|
|
75
|
+
try {
|
|
76
|
+
execSync2("git init", { cwd: targetDir, stdio: "ignore" });
|
|
77
|
+
execSync2("git add -A", { cwd: targetDir, stdio: "ignore" });
|
|
78
|
+
execSync2('git commit -m "Initial commit from create-blitzpack"', {
|
|
79
|
+
cwd: targetDir,
|
|
80
|
+
stdio: "ignore"
|
|
81
|
+
});
|
|
82
|
+
return true;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/checks.ts
|
|
20
89
|
function checkNodeVersion() {
|
|
21
90
|
try {
|
|
22
91
|
const nodeVersion = process.version;
|
|
@@ -24,127 +93,126 @@ function checkNodeVersion() {
|
|
|
24
93
|
if (majorVersion >= 20) {
|
|
25
94
|
return {
|
|
26
95
|
passed: true,
|
|
27
|
-
name: "Node.js"
|
|
96
|
+
name: "Node.js",
|
|
97
|
+
required: true,
|
|
98
|
+
message: nodeVersion
|
|
28
99
|
};
|
|
29
100
|
}
|
|
30
101
|
return {
|
|
31
102
|
passed: false,
|
|
32
103
|
name: "Node.js",
|
|
104
|
+
required: true,
|
|
33
105
|
message: `Node.js >= 20.0.0 required (found ${nodeVersion})`
|
|
34
106
|
};
|
|
35
107
|
} catch {
|
|
36
108
|
return {
|
|
37
109
|
passed: false,
|
|
38
110
|
name: "Node.js",
|
|
111
|
+
required: true,
|
|
39
112
|
message: "Failed to check Node.js version"
|
|
40
113
|
};
|
|
41
114
|
}
|
|
42
115
|
}
|
|
43
116
|
function checkPnpmInstalled() {
|
|
44
117
|
try {
|
|
45
|
-
|
|
118
|
+
const version = execSync3("pnpm --version", {
|
|
119
|
+
encoding: "utf-8",
|
|
120
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
121
|
+
}).trim();
|
|
46
122
|
return {
|
|
47
123
|
passed: true,
|
|
48
|
-
name: "pnpm"
|
|
124
|
+
name: "pnpm",
|
|
125
|
+
required: true,
|
|
126
|
+
message: `v${version}`
|
|
49
127
|
};
|
|
50
128
|
} catch {
|
|
51
129
|
return {
|
|
52
130
|
passed: false,
|
|
53
131
|
name: "pnpm",
|
|
132
|
+
required: true,
|
|
54
133
|
message: "pnpm not found. Install: npm install -g pnpm"
|
|
55
134
|
};
|
|
56
135
|
}
|
|
57
136
|
}
|
|
137
|
+
function checkGit() {
|
|
138
|
+
const installed = isGitInstalled();
|
|
139
|
+
return {
|
|
140
|
+
passed: installed,
|
|
141
|
+
name: "git",
|
|
142
|
+
required: false,
|
|
143
|
+
message: installed ? "available (repository initialization supported)" : "not found (git init step will be skipped)"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function checkDocker() {
|
|
147
|
+
const installed = isDockerInstalled();
|
|
148
|
+
return {
|
|
149
|
+
passed: installed,
|
|
150
|
+
name: "Docker",
|
|
151
|
+
required: false,
|
|
152
|
+
message: installed ? "available (automatic local DB setup supported)" : "not found (start PostgreSQL separately)"
|
|
153
|
+
};
|
|
154
|
+
}
|
|
58
155
|
async function runPreflightChecks() {
|
|
59
156
|
console.log();
|
|
60
|
-
console.log(chalk.bold("
|
|
157
|
+
console.log(chalk.bold(" System readiness"));
|
|
158
|
+
console.log(chalk.dim(" Validating required and optional local tooling..."));
|
|
61
159
|
console.log();
|
|
62
|
-
const checks = [
|
|
63
|
-
|
|
160
|
+
const checks = [
|
|
161
|
+
checkNodeVersion(),
|
|
162
|
+
checkPnpmInstalled(),
|
|
163
|
+
checkGit(),
|
|
164
|
+
checkDocker()
|
|
165
|
+
];
|
|
166
|
+
const requiredFailures = [];
|
|
167
|
+
const optionalWarnings = [];
|
|
64
168
|
for (const check of checks) {
|
|
169
|
+
const spinner = ora(`Checking ${check.name}...`).start();
|
|
65
170
|
if (check.passed) {
|
|
66
|
-
|
|
171
|
+
spinner.succeed(chalk.bold(check.name));
|
|
67
172
|
} else {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
173
|
+
if (check.required) {
|
|
174
|
+
requiredFailures.push(check);
|
|
175
|
+
spinner.fail(chalk.bold(check.name));
|
|
176
|
+
} else {
|
|
177
|
+
optionalWarnings.push(check);
|
|
178
|
+
spinner.warn(chalk.bold(check.name));
|
|
72
179
|
}
|
|
180
|
+
console.log(chalk.dim(` ${check.message}`));
|
|
73
181
|
}
|
|
74
182
|
}
|
|
75
183
|
console.log();
|
|
76
|
-
if (
|
|
184
|
+
if (optionalWarnings.length > 0) {
|
|
185
|
+
console.log(chalk.yellow(" Optional tools missing:"));
|
|
186
|
+
for (const warning of optionalWarnings) {
|
|
187
|
+
console.log(chalk.dim(` \u2022 ${warning.name}: ${warning.message}`));
|
|
188
|
+
}
|
|
189
|
+
console.log();
|
|
190
|
+
}
|
|
191
|
+
if (requiredFailures.length > 0) {
|
|
77
192
|
console.log(
|
|
78
193
|
chalk.red(" \u2716"),
|
|
79
|
-
"
|
|
194
|
+
"Required dependencies are missing. Fix the items below and try again:"
|
|
80
195
|
);
|
|
196
|
+
for (const failure of requiredFailures) {
|
|
197
|
+
console.log(chalk.dim(` \u2022 ${failure.name}: ${failure.message}`));
|
|
198
|
+
}
|
|
81
199
|
console.log();
|
|
82
200
|
return false;
|
|
83
201
|
}
|
|
84
202
|
return true;
|
|
85
203
|
}
|
|
86
204
|
|
|
87
|
-
// src/docker.ts
|
|
88
|
-
import { execSync as execSync2 } from "child_process";
|
|
89
|
-
function isDockerRunning() {
|
|
90
|
-
try {
|
|
91
|
-
execSync2("docker info", { stdio: "ignore", timeout: 3e3 });
|
|
92
|
-
return true;
|
|
93
|
-
} catch {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function runDockerCompose(targetDir) {
|
|
98
|
-
try {
|
|
99
|
-
execSync2("docker compose up -d", {
|
|
100
|
-
cwd: targetDir,
|
|
101
|
-
stdio: "inherit"
|
|
102
|
-
});
|
|
103
|
-
return true;
|
|
104
|
-
} catch {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function runDatabaseMigrations(targetDir) {
|
|
109
|
-
try {
|
|
110
|
-
const isWindows = process.platform === "win32";
|
|
111
|
-
execSync2(isWindows ? "pnpm.cmd db:migrate" : "pnpm db:migrate", {
|
|
112
|
-
cwd: targetDir,
|
|
113
|
-
stdio: "inherit"
|
|
114
|
-
});
|
|
115
|
-
return true;
|
|
116
|
-
} catch {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// src/git.ts
|
|
122
|
-
import { execSync as execSync3 } from "child_process";
|
|
123
|
-
function isGitInstalled() {
|
|
124
|
-
try {
|
|
125
|
-
execSync3("git --version", { stdio: "ignore" });
|
|
126
|
-
return true;
|
|
127
|
-
} catch {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function initGit(targetDir) {
|
|
132
|
-
try {
|
|
133
|
-
execSync3("git init", { cwd: targetDir, stdio: "ignore" });
|
|
134
|
-
execSync3("git add -A", { cwd: targetDir, stdio: "ignore" });
|
|
135
|
-
execSync3('git commit -m "Initial commit from create-blitzpack"', {
|
|
136
|
-
cwd: targetDir,
|
|
137
|
-
stdio: "ignore"
|
|
138
|
-
});
|
|
139
|
-
return true;
|
|
140
|
-
} catch {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
205
|
// src/prompts.ts
|
|
206
|
+
import {
|
|
207
|
+
cancel,
|
|
208
|
+
confirm,
|
|
209
|
+
isCancel,
|
|
210
|
+
multiselect,
|
|
211
|
+
note,
|
|
212
|
+
select,
|
|
213
|
+
text
|
|
214
|
+
} from "@clack/prompts";
|
|
146
215
|
import chalk3 from "chalk";
|
|
147
|
-
import prompts from "prompts";
|
|
148
216
|
|
|
149
217
|
// src/constants.ts
|
|
150
218
|
var REPLACEABLE_FILES = [
|
|
@@ -234,6 +302,44 @@ var FEATURE_EXCLUSIONS = {
|
|
|
234
302
|
],
|
|
235
303
|
ciCd: [".github/workflows/cd.yml"]
|
|
236
304
|
};
|
|
305
|
+
var PROJECT_PROFILES = [
|
|
306
|
+
{
|
|
307
|
+
key: "recommended",
|
|
308
|
+
name: "Recommended",
|
|
309
|
+
description: "Core app with everything included",
|
|
310
|
+
defaultFeatures: {
|
|
311
|
+
testing: true,
|
|
312
|
+
admin: true,
|
|
313
|
+
uploads: true,
|
|
314
|
+
dockerDeploy: true,
|
|
315
|
+
ciCd: true
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
key: "platformAgnostic",
|
|
320
|
+
name: "Platform-Agnostic",
|
|
321
|
+
description: "Core app without dockerfiles",
|
|
322
|
+
defaultFeatures: {
|
|
323
|
+
testing: true,
|
|
324
|
+
admin: true,
|
|
325
|
+
uploads: true,
|
|
326
|
+
dockerDeploy: false,
|
|
327
|
+
ciCd: false
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
key: "modular",
|
|
332
|
+
name: "Modular",
|
|
333
|
+
description: "Core app with features of your choice",
|
|
334
|
+
defaultFeatures: {
|
|
335
|
+
testing: false,
|
|
336
|
+
admin: false,
|
|
337
|
+
uploads: false,
|
|
338
|
+
dockerDeploy: false,
|
|
339
|
+
ciCd: false
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
];
|
|
237
343
|
|
|
238
344
|
// src/utils.ts
|
|
239
345
|
import chalk2 from "chalk";
|
|
@@ -351,46 +457,87 @@ function printError(message) {
|
|
|
351
457
|
}
|
|
352
458
|
|
|
353
459
|
// src/prompts.ts
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
questions.push({
|
|
372
|
-
type: "text",
|
|
373
|
-
name: "projectDescription",
|
|
374
|
-
message: "Project description:",
|
|
375
|
-
initial: DEFAULT_DESCRIPTION
|
|
376
|
-
});
|
|
377
|
-
let cancelled = false;
|
|
378
|
-
const response = await prompts(questions, {
|
|
379
|
-
onCancel: () => {
|
|
380
|
-
cancelled = true;
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
if (cancelled) {
|
|
460
|
+
var FEATURE_LABELS = {
|
|
461
|
+
testing: "Testing",
|
|
462
|
+
admin: "Admin Dashboard",
|
|
463
|
+
uploads: "File Uploads",
|
|
464
|
+
dockerDeploy: "Docker deploy assets",
|
|
465
|
+
ciCd: "CD workflow"
|
|
466
|
+
};
|
|
467
|
+
var FEATURE_HINTS = {
|
|
468
|
+
testing: "Vitest, integration tests, and test helpers",
|
|
469
|
+
admin: "Admin routes, dashboard views, and management hooks",
|
|
470
|
+
uploads: "Upload APIs, storage service, and UI upload components",
|
|
471
|
+
dockerDeploy: "API/Web Dockerfiles and production Docker Compose",
|
|
472
|
+
ciCd: "GitHub Actions workflow for image build and publish"
|
|
473
|
+
};
|
|
474
|
+
function handleCancelledPrompt(value) {
|
|
475
|
+
if (isCancel(value)) {
|
|
476
|
+
cancel("Setup cancelled.");
|
|
384
477
|
return null;
|
|
385
478
|
}
|
|
386
|
-
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
function getEnabledFeatureKeys(features) {
|
|
482
|
+
return Object.keys(features).filter((key) => features[key]);
|
|
483
|
+
}
|
|
484
|
+
function deriveProfileFeatureSelection(profileKey) {
|
|
485
|
+
const profile = PROJECT_PROFILES.find((item) => item.key === profileKey) ?? PROJECT_PROFILES[0];
|
|
486
|
+
return getEnabledFeatureKeys(profile.defaultFeatures);
|
|
487
|
+
}
|
|
488
|
+
function resolveFeatureSelection(selected) {
|
|
489
|
+
const unique = Array.from(new Set(selected));
|
|
490
|
+
const hasCiCd = unique.includes("ciCd");
|
|
491
|
+
const hasDockerDeploy = unique.includes("dockerDeploy");
|
|
492
|
+
if (hasCiCd && !hasDockerDeploy) {
|
|
493
|
+
return [...unique.filter((key) => key !== "dockerDeploy"), "dockerDeploy"];
|
|
494
|
+
}
|
|
495
|
+
return unique;
|
|
496
|
+
}
|
|
497
|
+
function buildFeatureOptions(selectedFeatures) {
|
|
498
|
+
const normalized = resolveFeatureSelection(selectedFeatures);
|
|
499
|
+
return {
|
|
500
|
+
testing: normalized.includes("testing"),
|
|
501
|
+
admin: normalized.includes("admin"),
|
|
502
|
+
uploads: normalized.includes("uploads"),
|
|
503
|
+
dockerDeploy: normalized.includes("dockerDeploy"),
|
|
504
|
+
ciCd: normalized.includes("ciCd")
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function printWizardStep(step, total, title) {
|
|
508
|
+
console.log();
|
|
509
|
+
console.log(chalk3.bold(` Step ${step}/${total}`), chalk3.dim(title));
|
|
510
|
+
console.log();
|
|
511
|
+
}
|
|
512
|
+
function getStepTotal(profileKey) {
|
|
513
|
+
return profileKey === "modular" ? 4 : 2;
|
|
514
|
+
}
|
|
515
|
+
function printMultiselectControls() {
|
|
516
|
+
console.log(
|
|
517
|
+
chalk3.dim(" Controls: \u2191/\u2193 navigate \u2022 Space toggle \u2022 Enter confirm")
|
|
518
|
+
);
|
|
519
|
+
console.log();
|
|
520
|
+
}
|
|
521
|
+
function printConfigurationSummary(profileName, features) {
|
|
522
|
+
const lines = [
|
|
523
|
+
`${chalk3.dim("Profile")}: ${profileName}`,
|
|
524
|
+
`${chalk3.dim("Testing")}: ${features.testing ? "yes" : "no"}`,
|
|
525
|
+
`${chalk3.dim("Admin dashboard")}: ${features.admin ? "yes" : "no"}`,
|
|
526
|
+
`${chalk3.dim("File uploads")}: ${features.uploads ? "yes" : "no"}`,
|
|
527
|
+
`${chalk3.dim("Docker deploy assets")}: ${features.dockerDeploy ? "yes" : "no"}`,
|
|
528
|
+
`${chalk3.dim("CD workflow")}: ${features.ciCd ? "yes" : "no"}`
|
|
529
|
+
];
|
|
530
|
+
note(lines.join("\n"), chalk3.cyan("Configuration summary"));
|
|
531
|
+
}
|
|
532
|
+
function finalizeOptions(state, providedName, flags) {
|
|
533
|
+
const projectName = providedName || state.projectNameInput;
|
|
387
534
|
const validation = validateProjectName(projectName);
|
|
388
535
|
if (!validation.valid) {
|
|
389
|
-
console.log(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
536
|
+
console.log();
|
|
537
|
+
console.log(
|
|
538
|
+
chalk3.red(" \u2716"),
|
|
539
|
+
validation.problems?.[0] ?? "Invalid project name"
|
|
540
|
+
);
|
|
394
541
|
return null;
|
|
395
542
|
}
|
|
396
543
|
const useCurrentDir = projectName === ".";
|
|
@@ -398,134 +545,215 @@ async function getProjectOptions(providedName, flags = {}) {
|
|
|
398
545
|
return {
|
|
399
546
|
projectName: actualProjectName,
|
|
400
547
|
projectSlug: toSlug(actualProjectName),
|
|
401
|
-
projectDescription:
|
|
548
|
+
projectDescription: state.projectDescription || DEFAULT_DESCRIPTION,
|
|
402
549
|
skipGit: flags.skipGit || false,
|
|
403
550
|
skipInstall: flags.skipInstall || false,
|
|
404
551
|
useCurrentDir,
|
|
405
|
-
features
|
|
552
|
+
features: buildFeatureOptions(state.selectedFeatures)
|
|
406
553
|
};
|
|
407
554
|
}
|
|
408
|
-
async function
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
555
|
+
async function getProjectOptions(providedName, flags = {}) {
|
|
556
|
+
const initialProjectName = providedName || "my-app";
|
|
557
|
+
const initialProfileKey = "recommended";
|
|
558
|
+
const state = {
|
|
559
|
+
projectNameInput: initialProjectName,
|
|
560
|
+
projectDescription: DEFAULT_DESCRIPTION,
|
|
561
|
+
profileKey: initialProfileKey,
|
|
562
|
+
selectedFeatures: deriveProfileFeatureSelection(initialProfileKey)
|
|
563
|
+
};
|
|
564
|
+
let stage = "details";
|
|
565
|
+
while (true) {
|
|
566
|
+
if (stage === "details") {
|
|
567
|
+
printWizardStep(1, getStepTotal(state.profileKey), "Project details");
|
|
568
|
+
if (!providedName) {
|
|
569
|
+
const projectNameInput = handleCancelledPrompt(
|
|
570
|
+
await text({
|
|
571
|
+
message: chalk3.cyan("Project name"),
|
|
572
|
+
initialValue: state.projectNameInput,
|
|
573
|
+
validate: (value) => {
|
|
574
|
+
if (typeof value !== "string") {
|
|
575
|
+
return "Project name is required";
|
|
576
|
+
}
|
|
577
|
+
const result = validateProjectName(value);
|
|
578
|
+
return result.valid ? void 0 : result.problems?.[0] ?? "Invalid project name";
|
|
579
|
+
}
|
|
580
|
+
})
|
|
581
|
+
);
|
|
582
|
+
if (projectNameInput === null) {
|
|
583
|
+
return null;
|
|
430
584
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
585
|
+
state.projectNameInput = projectNameInput;
|
|
586
|
+
} else {
|
|
587
|
+
console.log(chalk3.dim(" Project name:"), chalk3.white(providedName));
|
|
588
|
+
}
|
|
589
|
+
const descriptionInput = handleCancelledPrompt(
|
|
590
|
+
await text({
|
|
591
|
+
message: chalk3.cyan("Project description"),
|
|
592
|
+
initialValue: state.projectDescription
|
|
593
|
+
})
|
|
594
|
+
);
|
|
595
|
+
if (descriptionInput === null) {
|
|
596
|
+
return null;
|
|
438
597
|
}
|
|
598
|
+
state.projectDescription = descriptionInput;
|
|
599
|
+
stage = "profile";
|
|
600
|
+
continue;
|
|
439
601
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
type: "multiselect",
|
|
471
|
-
name: "selectedAppFeatures",
|
|
472
|
-
message: "Select app features:",
|
|
473
|
-
choices: appFeatureChoices,
|
|
474
|
-
hint: "- Space to toggle, Enter to confirm",
|
|
475
|
-
instructions: false
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
onCancel: () => {
|
|
479
|
-
cancelled = true;
|
|
602
|
+
if (stage === "profile") {
|
|
603
|
+
printWizardStep(
|
|
604
|
+
2,
|
|
605
|
+
getStepTotal(state.profileKey),
|
|
606
|
+
"Choose a setup preset"
|
|
607
|
+
);
|
|
608
|
+
const profileAction = handleCancelledPrompt(
|
|
609
|
+
await select({
|
|
610
|
+
message: chalk3.cyan("Setup preset"),
|
|
611
|
+
options: [
|
|
612
|
+
...PROJECT_PROFILES.map((profile2) => ({
|
|
613
|
+
value: profile2.key,
|
|
614
|
+
label: profile2.name,
|
|
615
|
+
hint: profile2.description
|
|
616
|
+
})),
|
|
617
|
+
{
|
|
618
|
+
value: "__back__",
|
|
619
|
+
label: "Back",
|
|
620
|
+
hint: "Return to project details"
|
|
621
|
+
}
|
|
622
|
+
],
|
|
623
|
+
initialValue: state.profileKey
|
|
624
|
+
})
|
|
625
|
+
);
|
|
626
|
+
if (profileAction === null) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
if (profileAction === "__back__") {
|
|
630
|
+
stage = "details";
|
|
631
|
+
continue;
|
|
480
632
|
}
|
|
633
|
+
state.profileKey = profileAction;
|
|
634
|
+
state.selectedFeatures = deriveProfileFeatureSelection(state.profileKey);
|
|
635
|
+
if (state.profileKey !== "modular") {
|
|
636
|
+
const result = finalizeOptions(state, providedName, flags);
|
|
637
|
+
if (!result) {
|
|
638
|
+
stage = "details";
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
return result;
|
|
642
|
+
}
|
|
643
|
+
stage = "features";
|
|
644
|
+
continue;
|
|
481
645
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
646
|
+
if (stage === "features") {
|
|
647
|
+
const isModular = state.profileKey === "modular";
|
|
648
|
+
printWizardStep(
|
|
649
|
+
3,
|
|
650
|
+
4,
|
|
651
|
+
isModular ? "Select features" : "Deployment options"
|
|
652
|
+
);
|
|
653
|
+
if (isModular) {
|
|
654
|
+
printMultiselectControls();
|
|
655
|
+
const selectedFeatures = handleCancelledPrompt(
|
|
656
|
+
await multiselect({
|
|
657
|
+
message: chalk3.cyan("Select features"),
|
|
658
|
+
options: [...APP_FEATURES, ...DEPLOYMENT_FEATURES].map(
|
|
659
|
+
(feature) => ({
|
|
660
|
+
value: feature.key,
|
|
661
|
+
label: FEATURE_LABELS[feature.key],
|
|
662
|
+
hint: FEATURE_HINTS[feature.key]
|
|
663
|
+
})
|
|
664
|
+
),
|
|
665
|
+
initialValues: state.selectedFeatures,
|
|
666
|
+
required: false
|
|
667
|
+
})
|
|
668
|
+
);
|
|
669
|
+
if (selectedFeatures === null) {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
state.selectedFeatures = resolveFeatureSelection(
|
|
673
|
+
selectedFeatures
|
|
674
|
+
);
|
|
675
|
+
if (state.selectedFeatures.includes("ciCd") && !selectedFeatures.includes("dockerDeploy")) {
|
|
676
|
+
console.log();
|
|
677
|
+
console.log(
|
|
678
|
+
chalk3.dim(
|
|
679
|
+
" \u2139 CD workflow requires Docker deployment assets, enabling both."
|
|
680
|
+
)
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const nextAction = handleCancelledPrompt(
|
|
685
|
+
await select({
|
|
686
|
+
message: chalk3.cyan("Continue"),
|
|
687
|
+
options: [
|
|
688
|
+
{
|
|
689
|
+
value: "review",
|
|
690
|
+
label: "Review configuration"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
value: "profile",
|
|
694
|
+
label: "Back",
|
|
695
|
+
hint: "Return to preset selection"
|
|
696
|
+
}
|
|
697
|
+
],
|
|
698
|
+
initialValue: "review"
|
|
699
|
+
})
|
|
700
|
+
);
|
|
701
|
+
if (nextAction === null) {
|
|
702
|
+
return null;
|
|
504
703
|
}
|
|
704
|
+
stage = nextAction;
|
|
705
|
+
continue;
|
|
505
706
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
707
|
+
const profile = PROJECT_PROFILES.find((item) => item.key === state.profileKey) ?? PROJECT_PROFILES[0];
|
|
708
|
+
const options = finalizeOptions(state, providedName, flags);
|
|
709
|
+
if (!options) {
|
|
710
|
+
stage = "details";
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
const features = options.features;
|
|
714
|
+
const reviewStep = state.profileKey === "modular" ? 4 : 3;
|
|
715
|
+
printWizardStep(
|
|
716
|
+
reviewStep,
|
|
717
|
+
getStepTotal(state.profileKey),
|
|
718
|
+
"Review and confirm"
|
|
719
|
+
);
|
|
720
|
+
printConfigurationSummary(profile.name, features);
|
|
721
|
+
const reviewOptions = [
|
|
722
|
+
{
|
|
723
|
+
value: "create",
|
|
724
|
+
label: "Create project"
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
value: "profile",
|
|
728
|
+
label: "Edit preset"
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
value: "details",
|
|
732
|
+
label: "Edit project details"
|
|
733
|
+
}
|
|
734
|
+
];
|
|
735
|
+
if (state.profileKey === "modular") {
|
|
736
|
+
reviewOptions.splice(1, 0, {
|
|
737
|
+
value: "features",
|
|
738
|
+
label: "Edit features"
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
const reviewAction = handleCancelledPrompt(
|
|
742
|
+
await select({
|
|
743
|
+
message: chalk3.cyan("Ready to scaffold?"),
|
|
744
|
+
options: reviewOptions,
|
|
745
|
+
initialValue: "create"
|
|
746
|
+
})
|
|
520
747
|
);
|
|
748
|
+
if (reviewAction === null) {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
if (reviewAction !== "create") {
|
|
752
|
+
stage = reviewAction;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
return options;
|
|
521
756
|
}
|
|
522
|
-
return {
|
|
523
|
-
testing: selectedApp.includes("testing"),
|
|
524
|
-
admin: selectedApp.includes("admin"),
|
|
525
|
-
uploads: selectedApp.includes("uploads"),
|
|
526
|
-
dockerDeploy: includesDockerDeploy,
|
|
527
|
-
ciCd: includesCiCd
|
|
528
|
-
};
|
|
529
757
|
}
|
|
530
758
|
async function promptAutomaticSetup() {
|
|
531
759
|
const dockerRunning = isDockerRunning();
|
|
@@ -542,13 +770,16 @@ async function promptAutomaticSetup() {
|
|
|
542
770
|
return false;
|
|
543
771
|
}
|
|
544
772
|
console.log();
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
773
|
+
const runSetup = handleCancelledPrompt(
|
|
774
|
+
await confirm({
|
|
775
|
+
message: "Run local setup now? (Docker PostgreSQL + database migrations)",
|
|
776
|
+
initialValue: true
|
|
777
|
+
})
|
|
778
|
+
);
|
|
779
|
+
if (runSetup === null) {
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
return runSetup;
|
|
552
783
|
}
|
|
553
784
|
|
|
554
785
|
// src/template.ts
|
|
@@ -559,7 +790,9 @@ var GITHUB_REPO = "github:CarboxyDev/blitzpack";
|
|
|
559
790
|
var POST_DOWNLOAD_EXCLUDES = [
|
|
560
791
|
"create-blitzpack",
|
|
561
792
|
"apps/marketing",
|
|
562
|
-
"CONTRIBUTING.md"
|
|
793
|
+
"CONTRIBUTING.md",
|
|
794
|
+
"docs/create-blitzpack-scaffolding-maintenance-plan.md",
|
|
795
|
+
"pnpm-lock.yaml"
|
|
563
796
|
];
|
|
564
797
|
function getFeatureExclusions(features) {
|
|
565
798
|
const exclusions = [];
|
|
@@ -1146,7 +1379,8 @@ ${vars.projectDescription}
|
|
|
1146
1379
|
|
|
1147
1380
|
\`\`\`bash
|
|
1148
1381
|
pnpm install
|
|
1149
|
-
|
|
1382
|
+
docker compose up -d
|
|
1383
|
+
pnpm db:migrate
|
|
1150
1384
|
pnpm dev
|
|
1151
1385
|
\`\`\`
|
|
1152
1386
|
|
|
@@ -1444,6 +1678,52 @@ function runInstall(cwd) {
|
|
|
1444
1678
|
child.on("error", () => resolve(false));
|
|
1445
1679
|
});
|
|
1446
1680
|
}
|
|
1681
|
+
function installGitHooks(cwd) {
|
|
1682
|
+
return new Promise((resolve) => {
|
|
1683
|
+
const isWindows = process.platform === "win32";
|
|
1684
|
+
const child = spawn(
|
|
1685
|
+
isWindows ? "pnpm.cmd" : "pnpm",
|
|
1686
|
+
["exec", "husky"],
|
|
1687
|
+
{
|
|
1688
|
+
cwd,
|
|
1689
|
+
stdio: "ignore"
|
|
1690
|
+
}
|
|
1691
|
+
);
|
|
1692
|
+
child.on("close", (code) => resolve(code === 0));
|
|
1693
|
+
child.on("error", () => resolve(false));
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
function renderProgressBar(current, total) {
|
|
1697
|
+
const width = 28;
|
|
1698
|
+
const clampedTotal = Math.max(total, 1);
|
|
1699
|
+
const ratio = Math.min(Math.max(current / clampedTotal, 0), 1);
|
|
1700
|
+
const filled = Math.round(width * ratio);
|
|
1701
|
+
const empty = width - filled;
|
|
1702
|
+
const filledBar = chalk4.cyan("\u2588".repeat(filled));
|
|
1703
|
+
const emptyBar = chalk4.dim("\u2591".repeat(empty));
|
|
1704
|
+
const percentage = `${Math.round(ratio * 100)}`.padStart(3, " ");
|
|
1705
|
+
return `[${filledBar}${emptyBar}] ${percentage}%`;
|
|
1706
|
+
}
|
|
1707
|
+
function renderStepTrack(step, total) {
|
|
1708
|
+
const segments = [];
|
|
1709
|
+
for (let index = 1; index <= total; index++) {
|
|
1710
|
+
if (index < step) {
|
|
1711
|
+
segments.push(chalk4.green("\u25CF"));
|
|
1712
|
+
} else if (index === step) {
|
|
1713
|
+
segments.push(chalk4.cyan("\u25C6"));
|
|
1714
|
+
} else {
|
|
1715
|
+
segments.push(chalk4.dim("\u25C7"));
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
return segments.join(chalk4.dim("\u2500\u2500"));
|
|
1719
|
+
}
|
|
1720
|
+
function printStepHeader(step, total, title) {
|
|
1721
|
+
const completed = step - 1;
|
|
1722
|
+
console.log();
|
|
1723
|
+
console.log(chalk4.cyan(` Step ${step}/${total}`), chalk4.bold(title));
|
|
1724
|
+
console.log(` ${renderProgressBar(completed, total)}`);
|
|
1725
|
+
console.log(` ${renderStepTrack(step, total)}`);
|
|
1726
|
+
}
|
|
1447
1727
|
function printDryRun(options) {
|
|
1448
1728
|
console.log(chalk4.yellow(" Dry run mode - no changes will be made"));
|
|
1449
1729
|
console.log();
|
|
@@ -1480,12 +1760,12 @@ function printDryRun(options) {
|
|
|
1480
1760
|
console.log(` ${chalk4.dim("\u2022")} Download template from GitHub`);
|
|
1481
1761
|
console.log(` ${chalk4.dim("\u2022")} Transform package.json files`);
|
|
1482
1762
|
console.log(` ${chalk4.dim("\u2022")} Create .env.local files`);
|
|
1483
|
-
if (!options.skipGit) {
|
|
1484
|
-
console.log(` ${chalk4.dim("\u2022")} Initialize git repository`);
|
|
1485
|
-
}
|
|
1486
1763
|
if (!options.skipInstall) {
|
|
1487
1764
|
console.log(` ${chalk4.dim("\u2022")} Install dependencies (pnpm install)`);
|
|
1488
1765
|
}
|
|
1766
|
+
if (!options.skipGit) {
|
|
1767
|
+
console.log(` ${chalk4.dim("\u2022")} Initialize git repository`);
|
|
1768
|
+
}
|
|
1489
1769
|
console.log();
|
|
1490
1770
|
}
|
|
1491
1771
|
async function create(projectName, flags) {
|
|
@@ -1515,13 +1795,11 @@ async function create(projectName, flags) {
|
|
|
1515
1795
|
const files = await fs3.readdir(targetDir);
|
|
1516
1796
|
if (files.length > 0) {
|
|
1517
1797
|
if (options.useCurrentDir) {
|
|
1518
|
-
const
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
message: `Current directory is not empty. Continue?`,
|
|
1522
|
-
initial: false
|
|
1798
|
+
const shouldContinue = await confirm2({
|
|
1799
|
+
message: "Current directory is not empty. Continue?",
|
|
1800
|
+
initialValue: false
|
|
1523
1801
|
});
|
|
1524
|
-
if (!
|
|
1802
|
+
if (isCancel2(shouldContinue) || !shouldContinue) {
|
|
1525
1803
|
return;
|
|
1526
1804
|
}
|
|
1527
1805
|
} else {
|
|
@@ -1530,11 +1808,19 @@ async function create(projectName, flags) {
|
|
|
1530
1808
|
}
|
|
1531
1809
|
}
|
|
1532
1810
|
}
|
|
1533
|
-
const
|
|
1811
|
+
const shouldRunSetup = await promptAutomaticSetup();
|
|
1812
|
+
const totalSteps = 2 + (options.skipGit ? 0 : 1) + (options.skipInstall ? 0 : 1) + (shouldRunSetup ? 1 : 0);
|
|
1813
|
+
let currentStep = 0;
|
|
1814
|
+
let spinner;
|
|
1815
|
+
let installSucceeded = false;
|
|
1534
1816
|
try {
|
|
1535
|
-
|
|
1817
|
+
currentStep += 1;
|
|
1818
|
+
printStepHeader(currentStep, totalSteps, "Scaffold template");
|
|
1819
|
+
spinner = ora2("Downloading template from GitHub...").start();
|
|
1536
1820
|
await downloadAndPrepareTemplate(targetDir, spinner, options.features);
|
|
1537
|
-
|
|
1821
|
+
currentStep += 1;
|
|
1822
|
+
printStepHeader(currentStep, totalSteps, "Configure project files");
|
|
1823
|
+
spinner.start("Applying template transforms...");
|
|
1538
1824
|
await transformFiles(
|
|
1539
1825
|
targetDir,
|
|
1540
1826
|
{
|
|
@@ -1546,19 +1832,12 @@ async function create(projectName, flags) {
|
|
|
1546
1832
|
);
|
|
1547
1833
|
await copyEnvFiles(targetDir);
|
|
1548
1834
|
spinner.succeed("Configured project");
|
|
1549
|
-
if (!options.skipGit && isGitInstalled()) {
|
|
1550
|
-
spinner.start("Initializing git repository...");
|
|
1551
|
-
const gitSuccess = initGit(targetDir);
|
|
1552
|
-
if (gitSuccess) {
|
|
1553
|
-
spinner.succeed("Initialized git repository");
|
|
1554
|
-
} else {
|
|
1555
|
-
spinner.warn("Failed to initialize git repository");
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
1835
|
if (!options.skipInstall) {
|
|
1836
|
+
currentStep += 1;
|
|
1837
|
+
printStepHeader(currentStep, totalSteps, "Install dependencies");
|
|
1559
1838
|
spinner.start("Installing dependencies...");
|
|
1560
|
-
|
|
1561
|
-
if (
|
|
1839
|
+
installSucceeded = await runInstall(targetDir);
|
|
1840
|
+
if (installSucceeded) {
|
|
1562
1841
|
spinner.succeed("Installed dependencies");
|
|
1563
1842
|
} else {
|
|
1564
1843
|
spinner.warn(
|
|
@@ -1566,10 +1845,37 @@ async function create(projectName, flags) {
|
|
|
1566
1845
|
);
|
|
1567
1846
|
}
|
|
1568
1847
|
}
|
|
1848
|
+
if (!options.skipGit && isGitInstalled()) {
|
|
1849
|
+
currentStep += 1;
|
|
1850
|
+
printStepHeader(currentStep, totalSteps, "Initialize git repository");
|
|
1851
|
+
spinner.start("Initializing git repository...");
|
|
1852
|
+
const gitSuccess = initGit(targetDir);
|
|
1853
|
+
if (gitSuccess) {
|
|
1854
|
+
if (installSucceeded) {
|
|
1855
|
+
spinner.start("Installing git hooks...");
|
|
1856
|
+
const hooksSuccess = await installGitHooks(targetDir);
|
|
1857
|
+
if (hooksSuccess) {
|
|
1858
|
+
spinner.succeed("Initialized git repository");
|
|
1859
|
+
} else {
|
|
1860
|
+
spinner.warn(
|
|
1861
|
+
"Initialized git repository, but failed to install git hooks"
|
|
1862
|
+
);
|
|
1863
|
+
}
|
|
1864
|
+
} else {
|
|
1865
|
+
spinner.succeed("Initialized git repository");
|
|
1866
|
+
}
|
|
1867
|
+
} else {
|
|
1868
|
+
spinner.warn("Failed to initialize git repository");
|
|
1869
|
+
}
|
|
1870
|
+
} else if (!options.skipGit) {
|
|
1871
|
+
currentStep += 1;
|
|
1872
|
+
printStepHeader(currentStep, totalSteps, "Initialize git repository");
|
|
1873
|
+
spinner.warn("Skipped git initialization (git not installed)");
|
|
1874
|
+
}
|
|
1569
1875
|
let ranAutomaticSetup = false;
|
|
1570
|
-
const shouldRunSetup = await promptAutomaticSetup();
|
|
1571
1876
|
if (shouldRunSetup) {
|
|
1572
|
-
|
|
1877
|
+
currentStep += 1;
|
|
1878
|
+
printStepHeader(currentStep, totalSteps, "Run local database setup");
|
|
1573
1879
|
spinner.start("Starting PostgreSQL database...");
|
|
1574
1880
|
const dockerSuccess = runDockerCompose(targetDir);
|
|
1575
1881
|
if (dockerSuccess) {
|
|
@@ -1596,7 +1902,7 @@ async function create(projectName, flags) {
|
|
|
1596
1902
|
ranAutomaticSetup
|
|
1597
1903
|
);
|
|
1598
1904
|
} catch (error) {
|
|
1599
|
-
spinner
|
|
1905
|
+
spinner?.fail();
|
|
1600
1906
|
printError(
|
|
1601
1907
|
error instanceof Error ? error.message : "Unknown error occurred"
|
|
1602
1908
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-blitzpack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "Create a new Blitzpack project - full-stack TypeScript monorepo with Next.js and Fastify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,18 +16,17 @@
|
|
|
16
16
|
"clean": "rm -rf dist"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@clack/prompts": "^1.0.0",
|
|
19
20
|
"chalk": "^5.6.2",
|
|
20
21
|
"commander": "^13.1.0",
|
|
21
22
|
"fs-extra": "^11.3.3",
|
|
22
23
|
"giget": "^2.0.0",
|
|
23
24
|
"ora": "^8.2.0",
|
|
24
|
-
"prompts": "^2.4.2",
|
|
25
25
|
"validate-npm-package-name": "^6.0.2"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/fs-extra": "^11.0.4",
|
|
29
29
|
"@types/node": "^22.19.3",
|
|
30
|
-
"@types/prompts": "^2.4.9",
|
|
31
30
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
32
31
|
"tsup": "^8.5.1",
|
|
33
32
|
"typescript": "^5.9.3"
|