create-blitzpack 0.1.5 → 0.1.6
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 +276 -52
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,18 +7,122 @@ import { dirname, join } from "path";
|
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
9
9
|
// src/commands/create.ts
|
|
10
|
-
import
|
|
10
|
+
import chalk4 from "chalk";
|
|
11
11
|
import { spawn } from "child_process";
|
|
12
12
|
import fs3 from "fs-extra";
|
|
13
13
|
import ora from "ora";
|
|
14
14
|
import path4 from "path";
|
|
15
15
|
import prompts2 from "prompts";
|
|
16
16
|
|
|
17
|
-
// src/
|
|
17
|
+
// src/checks.ts
|
|
18
|
+
import chalk from "chalk";
|
|
18
19
|
import { execSync } from "child_process";
|
|
20
|
+
function checkNodeVersion() {
|
|
21
|
+
try {
|
|
22
|
+
const nodeVersion = process.version;
|
|
23
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0], 10);
|
|
24
|
+
if (majorVersion >= 20) {
|
|
25
|
+
return {
|
|
26
|
+
passed: true,
|
|
27
|
+
name: "Node.js"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
passed: false,
|
|
32
|
+
name: "Node.js",
|
|
33
|
+
message: `Node.js >= 20.0.0 required (found ${nodeVersion})`
|
|
34
|
+
};
|
|
35
|
+
} catch {
|
|
36
|
+
return {
|
|
37
|
+
passed: false,
|
|
38
|
+
name: "Node.js",
|
|
39
|
+
message: "Failed to check Node.js version"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function checkPnpmInstalled() {
|
|
44
|
+
try {
|
|
45
|
+
execSync("pnpm --version", { stdio: "ignore" });
|
|
46
|
+
return {
|
|
47
|
+
passed: true,
|
|
48
|
+
name: "pnpm"
|
|
49
|
+
};
|
|
50
|
+
} catch {
|
|
51
|
+
return {
|
|
52
|
+
passed: false,
|
|
53
|
+
name: "pnpm",
|
|
54
|
+
message: "pnpm not found. Install: npm install -g pnpm"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function runPreflightChecks() {
|
|
59
|
+
console.log();
|
|
60
|
+
console.log(chalk.bold(" Checking requirements..."));
|
|
61
|
+
console.log();
|
|
62
|
+
const checks = [checkNodeVersion(), checkPnpmInstalled()];
|
|
63
|
+
let hasErrors = false;
|
|
64
|
+
for (const check of checks) {
|
|
65
|
+
if (check.passed) {
|
|
66
|
+
console.log(chalk.green(" \u2714"), check.name);
|
|
67
|
+
} else {
|
|
68
|
+
hasErrors = true;
|
|
69
|
+
console.log(chalk.red(" \u2716"), check.name);
|
|
70
|
+
if (check.message) {
|
|
71
|
+
console.log(chalk.dim(` ${check.message}`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
console.log();
|
|
76
|
+
if (hasErrors) {
|
|
77
|
+
console.log(
|
|
78
|
+
chalk.red(" \u2716"),
|
|
79
|
+
"Requirements not met. Please fix the errors above."
|
|
80
|
+
);
|
|
81
|
+
console.log();
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
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";
|
|
19
123
|
function isGitInstalled() {
|
|
20
124
|
try {
|
|
21
|
-
|
|
125
|
+
execSync3("git --version", { stdio: "ignore" });
|
|
22
126
|
return true;
|
|
23
127
|
} catch {
|
|
24
128
|
return false;
|
|
@@ -26,9 +130,9 @@ function isGitInstalled() {
|
|
|
26
130
|
}
|
|
27
131
|
function initGit(targetDir) {
|
|
28
132
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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"', {
|
|
32
136
|
cwd: targetDir,
|
|
33
137
|
stdio: "ignore"
|
|
34
138
|
});
|
|
@@ -39,6 +143,7 @@ function initGit(targetDir) {
|
|
|
39
143
|
}
|
|
40
144
|
|
|
41
145
|
// src/prompts.ts
|
|
146
|
+
import chalk3 from "chalk";
|
|
42
147
|
import prompts from "prompts";
|
|
43
148
|
|
|
44
149
|
// src/constants.ts
|
|
@@ -52,7 +157,7 @@ var REPLACEABLE_FILES = [
|
|
|
52
157
|
var DEFAULT_DESCRIPTION = "A full-stack TypeScript monorepo built with Blitzpack";
|
|
53
158
|
|
|
54
159
|
// src/utils.ts
|
|
55
|
-
import
|
|
160
|
+
import chalk2 from "chalk";
|
|
56
161
|
import path from "path";
|
|
57
162
|
import validatePackageName from "validate-npm-package-name";
|
|
58
163
|
function getCurrentDirName() {
|
|
@@ -74,51 +179,95 @@ function validateProjectName(name) {
|
|
|
74
179
|
}
|
|
75
180
|
function printHeader() {
|
|
76
181
|
console.log();
|
|
77
|
-
console.log(
|
|
78
|
-
console.log(
|
|
182
|
+
console.log(chalk2.bold.cyan(" Create Blitzpack"));
|
|
183
|
+
console.log(chalk2.dim(" Full-stack TypeScript monorepo"));
|
|
79
184
|
console.log();
|
|
80
185
|
}
|
|
81
|
-
function printSuccess(projectName, targetDir) {
|
|
82
|
-
console.log();
|
|
83
|
-
console.log(chalk.green("\u2714"), chalk.bold(`Created ${projectName}`));
|
|
186
|
+
function printSuccess(projectName, targetDir, ranAutomaticSetup = false) {
|
|
84
187
|
console.log();
|
|
85
|
-
console.log(
|
|
188
|
+
console.log(chalk2.green("\u2714"), chalk2.bold(`Created ${projectName}`));
|
|
86
189
|
console.log();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
190
|
+
if (ranAutomaticSetup) {
|
|
191
|
+
console.log(
|
|
192
|
+
chalk2.green("\u2714"),
|
|
193
|
+
chalk2.dim("Database setup complete! Ready to start developing.")
|
|
194
|
+
);
|
|
195
|
+
console.log();
|
|
196
|
+
console.log(chalk2.bold(" Next steps:"));
|
|
197
|
+
console.log();
|
|
198
|
+
let stepNumber = 1;
|
|
199
|
+
if (targetDir !== ".") {
|
|
200
|
+
console.log(
|
|
201
|
+
chalk2.cyan(` ${stepNumber}.`),
|
|
202
|
+
chalk2.bold(`cd ${targetDir}`)
|
|
203
|
+
);
|
|
204
|
+
stepNumber++;
|
|
205
|
+
}
|
|
206
|
+
console.log(chalk2.cyan(` ${stepNumber}.`), chalk2.bold("pnpm dev"));
|
|
207
|
+
console.log(chalk2.dim(" Start development servers (web + api)"));
|
|
208
|
+
console.log();
|
|
209
|
+
console.log(chalk2.dim(" Optional commands:"));
|
|
210
|
+
console.log(
|
|
211
|
+
chalk2.dim(
|
|
212
|
+
" \u2022 pnpm db:seed Add test data (admin accounts, users)"
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
console.log(
|
|
216
|
+
chalk2.dim(" \u2022 pnpm db:studio Open Prisma Studio to view data")
|
|
217
|
+
);
|
|
218
|
+
} else {
|
|
219
|
+
console.log(
|
|
220
|
+
chalk2.dim(" Setup complete! Follow these steps to start developing:")
|
|
221
|
+
);
|
|
222
|
+
console.log();
|
|
223
|
+
let stepNumber = 1;
|
|
224
|
+
if (targetDir !== ".") {
|
|
225
|
+
console.log(
|
|
226
|
+
chalk2.cyan(` ${stepNumber}.`),
|
|
227
|
+
chalk2.bold(`cd ${targetDir}`)
|
|
228
|
+
);
|
|
229
|
+
stepNumber++;
|
|
230
|
+
}
|
|
231
|
+
console.log(
|
|
232
|
+
chalk2.cyan(` ${stepNumber}.`),
|
|
233
|
+
chalk2.bold("docker compose up -d")
|
|
234
|
+
);
|
|
235
|
+
console.log(chalk2.dim(" Start PostgreSQL database (requires Docker)"));
|
|
236
|
+
console.log();
|
|
237
|
+
stepNumber++;
|
|
238
|
+
console.log(chalk2.cyan(` ${stepNumber}.`), chalk2.bold("pnpm db:migrate"));
|
|
239
|
+
console.log(chalk2.dim(" Run database migrations and setup schema"));
|
|
240
|
+
console.log();
|
|
90
241
|
stepNumber++;
|
|
242
|
+
console.log(chalk2.cyan(` ${stepNumber}.`), chalk2.bold("pnpm dev"));
|
|
243
|
+
console.log(chalk2.dim(" Start development servers (web + api)"));
|
|
244
|
+
console.log();
|
|
245
|
+
console.log(chalk2.dim(" Optional commands:"));
|
|
246
|
+
console.log(
|
|
247
|
+
chalk2.dim(
|
|
248
|
+
" \u2022 pnpm db:seed Add test data (admin accounts, users)"
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
console.log(
|
|
252
|
+
chalk2.dim(" \u2022 pnpm db:studio Open Prisma Studio to view data")
|
|
253
|
+
);
|
|
91
254
|
}
|
|
255
|
+
console.log();
|
|
256
|
+
console.log(chalk2.bold(" Your app will be running at:"));
|
|
92
257
|
console.log(
|
|
93
|
-
|
|
94
|
-
"docker compose up -d",
|
|
95
|
-
chalk.dim(" # Start PostgreSQL")
|
|
96
|
-
);
|
|
97
|
-
stepNumber++;
|
|
98
|
-
console.log(
|
|
99
|
-
chalk.cyan(` ${stepNumber}.`),
|
|
100
|
-
"pnpm db:migrate",
|
|
101
|
-
chalk.dim(" # Run database migrations")
|
|
258
|
+
chalk2.dim(" \u2022 Web: ") + chalk2.cyan("http://localhost:3000")
|
|
102
259
|
);
|
|
103
|
-
stepNumber++;
|
|
104
260
|
console.log(
|
|
105
|
-
|
|
106
|
-
"pnpm dev",
|
|
107
|
-
chalk.dim(" # Start dev servers")
|
|
261
|
+
chalk2.dim(" \u2022 API: ") + chalk2.cyan("http://localhost:8080/api")
|
|
108
262
|
);
|
|
109
|
-
console.log();
|
|
110
|
-
console.log(
|
|
111
|
-
chalk.dim(" Optional: pnpm db:seed to add test data like admin accounts")
|
|
112
|
-
);
|
|
113
|
-
console.log();
|
|
114
263
|
console.log(
|
|
115
|
-
|
|
264
|
+
chalk2.dim(" \u2022 API Docs: ") + chalk2.cyan("http://localhost:8080/docs")
|
|
116
265
|
);
|
|
117
266
|
console.log();
|
|
118
267
|
}
|
|
119
268
|
function printError(message) {
|
|
120
269
|
console.log();
|
|
121
|
-
console.log(
|
|
270
|
+
console.log(chalk2.red("\u2716"), message);
|
|
122
271
|
console.log();
|
|
123
272
|
}
|
|
124
273
|
|
|
@@ -172,6 +321,29 @@ async function getProjectOptions(providedName, flags = {}) {
|
|
|
172
321
|
useCurrentDir
|
|
173
322
|
};
|
|
174
323
|
}
|
|
324
|
+
async function promptAutomaticSetup() {
|
|
325
|
+
const dockerRunning = isDockerRunning();
|
|
326
|
+
if (!dockerRunning) {
|
|
327
|
+
console.log();
|
|
328
|
+
console.log(
|
|
329
|
+
chalk3.yellow(" \u26A0"),
|
|
330
|
+
"Docker is not running. Skipping automatic setup."
|
|
331
|
+
);
|
|
332
|
+
console.log(
|
|
333
|
+
chalk3.dim(" Start Docker and run setup steps manually (see below).")
|
|
334
|
+
);
|
|
335
|
+
console.log();
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
console.log();
|
|
339
|
+
const { runSetup } = await prompts({
|
|
340
|
+
type: "confirm",
|
|
341
|
+
name: "runSetup",
|
|
342
|
+
message: "Run initial setup now? (docker compose + database migrations)",
|
|
343
|
+
initial: true
|
|
344
|
+
});
|
|
345
|
+
return runSetup || false;
|
|
346
|
+
}
|
|
175
347
|
|
|
176
348
|
// src/template.ts
|
|
177
349
|
import fs from "fs-extra";
|
|
@@ -192,12 +364,36 @@ async function cleanupExcludes(targetDir) {
|
|
|
192
364
|
}
|
|
193
365
|
}
|
|
194
366
|
}
|
|
195
|
-
async function downloadAndPrepareTemplate(targetDir) {
|
|
367
|
+
async function downloadAndPrepareTemplate(targetDir, spinner) {
|
|
368
|
+
spinner.text = "Fetching template from GitHub...";
|
|
196
369
|
await downloadTemplate(GITHUB_REPO, {
|
|
197
370
|
dir: targetDir,
|
|
198
371
|
force: true
|
|
199
372
|
});
|
|
373
|
+
spinner.text = "Extracting files...";
|
|
374
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
375
|
+
spinner.text = "Cleaning up template files...";
|
|
200
376
|
await cleanupExcludes(targetDir);
|
|
377
|
+
const files = await countFiles(targetDir);
|
|
378
|
+
spinner.succeed(`Downloaded template (${files} files)`);
|
|
379
|
+
}
|
|
380
|
+
async function countFiles(dir) {
|
|
381
|
+
try {
|
|
382
|
+
let count = 0;
|
|
383
|
+
const items = await fs.readdir(dir);
|
|
384
|
+
for (const item of items) {
|
|
385
|
+
const fullPath = path2.join(dir, item);
|
|
386
|
+
const stat = await fs.stat(fullPath);
|
|
387
|
+
if (stat.isDirectory()) {
|
|
388
|
+
count += await countFiles(fullPath);
|
|
389
|
+
} else {
|
|
390
|
+
count++;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return count;
|
|
394
|
+
} catch {
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
201
397
|
}
|
|
202
398
|
|
|
203
399
|
// src/transform.ts
|
|
@@ -329,32 +525,36 @@ function runInstall(cwd) {
|
|
|
329
525
|
});
|
|
330
526
|
}
|
|
331
527
|
function printDryRun(options) {
|
|
332
|
-
console.log(
|
|
528
|
+
console.log(chalk4.yellow(" Dry run mode - no changes will be made"));
|
|
333
529
|
console.log();
|
|
334
|
-
console.log(
|
|
530
|
+
console.log(chalk4.bold(" Would create:"));
|
|
335
531
|
console.log();
|
|
336
|
-
console.log(` ${
|
|
337
|
-
console.log(` ${
|
|
338
|
-
console.log(` ${
|
|
532
|
+
console.log(` ${chalk4.cyan("Directory:")} ${options.targetDir}`);
|
|
533
|
+
console.log(` ${chalk4.cyan("Name:")} ${options.projectName}`);
|
|
534
|
+
console.log(` ${chalk4.cyan("Slug:")} ${options.projectSlug}`);
|
|
339
535
|
console.log(
|
|
340
|
-
` ${
|
|
536
|
+
` ${chalk4.cyan("Description:")} ${options.projectDescription}`
|
|
341
537
|
);
|
|
342
538
|
console.log();
|
|
343
|
-
console.log(
|
|
539
|
+
console.log(chalk4.bold(" Would run:"));
|
|
344
540
|
console.log();
|
|
345
|
-
console.log(` ${
|
|
346
|
-
console.log(` ${
|
|
347
|
-
console.log(` ${
|
|
541
|
+
console.log(` ${chalk4.dim("\u2022")} Download template from GitHub`);
|
|
542
|
+
console.log(` ${chalk4.dim("\u2022")} Transform package.json files`);
|
|
543
|
+
console.log(` ${chalk4.dim("\u2022")} Create .env.local files`);
|
|
348
544
|
if (!options.skipGit) {
|
|
349
|
-
console.log(` ${
|
|
545
|
+
console.log(` ${chalk4.dim("\u2022")} Initialize git repository`);
|
|
350
546
|
}
|
|
351
547
|
if (!options.skipInstall) {
|
|
352
|
-
console.log(` ${
|
|
548
|
+
console.log(` ${chalk4.dim("\u2022")} Install dependencies (pnpm install)`);
|
|
353
549
|
}
|
|
354
550
|
console.log();
|
|
355
551
|
}
|
|
356
552
|
async function create(projectName, flags) {
|
|
357
553
|
printHeader();
|
|
554
|
+
const checksPass = await runPreflightChecks();
|
|
555
|
+
if (!checksPass) {
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
358
558
|
const options = await getProjectOptions(projectName, flags);
|
|
359
559
|
if (!options) {
|
|
360
560
|
return;
|
|
@@ -393,8 +593,7 @@ async function create(projectName, flags) {
|
|
|
393
593
|
const spinner = ora();
|
|
394
594
|
try {
|
|
395
595
|
spinner.start("Downloading template from GitHub...");
|
|
396
|
-
await downloadAndPrepareTemplate(targetDir);
|
|
397
|
-
spinner.succeed("Downloaded template");
|
|
596
|
+
await downloadAndPrepareTemplate(targetDir, spinner);
|
|
398
597
|
spinner.start("Configuring project...");
|
|
399
598
|
await transformFiles(targetDir, {
|
|
400
599
|
projectName: options.projectName,
|
|
@@ -423,9 +622,34 @@ async function create(projectName, flags) {
|
|
|
423
622
|
);
|
|
424
623
|
}
|
|
425
624
|
}
|
|
625
|
+
let ranAutomaticSetup = false;
|
|
626
|
+
const shouldRunSetup = await promptAutomaticSetup();
|
|
627
|
+
if (shouldRunSetup) {
|
|
628
|
+
console.log();
|
|
629
|
+
spinner.start("Starting PostgreSQL database...");
|
|
630
|
+
const dockerSuccess = runDockerCompose(targetDir);
|
|
631
|
+
if (dockerSuccess) {
|
|
632
|
+
spinner.succeed("Started PostgreSQL database");
|
|
633
|
+
spinner.start("Running database migrations...");
|
|
634
|
+
const migrationsSuccess = runDatabaseMigrations(targetDir);
|
|
635
|
+
if (migrationsSuccess) {
|
|
636
|
+
spinner.succeed("Database migrations complete");
|
|
637
|
+
ranAutomaticSetup = true;
|
|
638
|
+
} else {
|
|
639
|
+
spinner.warn(
|
|
640
|
+
'Failed to run migrations. Run "pnpm db:migrate" manually.'
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
spinner.warn(
|
|
645
|
+
'Failed to start Docker. Run "docker compose up -d" manually.'
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
426
649
|
printSuccess(
|
|
427
650
|
options.projectName,
|
|
428
|
-
options.useCurrentDir ? "." : options.projectName
|
|
651
|
+
options.useCurrentDir ? "." : options.projectName,
|
|
652
|
+
ranAutomaticSetup
|
|
429
653
|
);
|
|
430
654
|
} catch (error) {
|
|
431
655
|
spinner.fail();
|