create-blitzpack 0.1.4 → 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 +279 -54
- 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";
|
|
@@ -181,7 +353,8 @@ var GITHUB_REPO = "github:CarboxyDev/blitzpack";
|
|
|
181
353
|
var POST_DOWNLOAD_EXCLUDES = [
|
|
182
354
|
"create-blitzpack",
|
|
183
355
|
"scripts/setup.js",
|
|
184
|
-
".github"
|
|
356
|
+
".github",
|
|
357
|
+
"apps/marketing"
|
|
185
358
|
];
|
|
186
359
|
async function cleanupExcludes(targetDir) {
|
|
187
360
|
for (const exclude of POST_DOWNLOAD_EXCLUDES) {
|
|
@@ -191,12 +364,36 @@ async function cleanupExcludes(targetDir) {
|
|
|
191
364
|
}
|
|
192
365
|
}
|
|
193
366
|
}
|
|
194
|
-
async function downloadAndPrepareTemplate(targetDir) {
|
|
367
|
+
async function downloadAndPrepareTemplate(targetDir, spinner) {
|
|
368
|
+
spinner.text = "Fetching template from GitHub...";
|
|
195
369
|
await downloadTemplate(GITHUB_REPO, {
|
|
196
370
|
dir: targetDir,
|
|
197
371
|
force: true
|
|
198
372
|
});
|
|
373
|
+
spinner.text = "Extracting files...";
|
|
374
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
375
|
+
spinner.text = "Cleaning up template files...";
|
|
199
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
|
+
}
|
|
200
397
|
}
|
|
201
398
|
|
|
202
399
|
// src/transform.ts
|
|
@@ -218,7 +415,7 @@ function transformSiteConfig(content, vars) {
|
|
|
218
415
|
return content.replace(/name: ['"].*['"]/, `name: '${vars.projectName}'`).replace(
|
|
219
416
|
/description: ['"].*['"]/,
|
|
220
417
|
`description: '${vars.projectDescription}'`
|
|
221
|
-
)
|
|
418
|
+
);
|
|
222
419
|
}
|
|
223
420
|
function transformLayout(content, vars) {
|
|
224
421
|
return content.replace(/title: ['"].*['"]/, `title: '${vars.projectName}'`).replace(
|
|
@@ -328,32 +525,36 @@ function runInstall(cwd) {
|
|
|
328
525
|
});
|
|
329
526
|
}
|
|
330
527
|
function printDryRun(options) {
|
|
331
|
-
console.log(
|
|
528
|
+
console.log(chalk4.yellow(" Dry run mode - no changes will be made"));
|
|
332
529
|
console.log();
|
|
333
|
-
console.log(
|
|
530
|
+
console.log(chalk4.bold(" Would create:"));
|
|
334
531
|
console.log();
|
|
335
|
-
console.log(` ${
|
|
336
|
-
console.log(` ${
|
|
337
|
-
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}`);
|
|
338
535
|
console.log(
|
|
339
|
-
` ${
|
|
536
|
+
` ${chalk4.cyan("Description:")} ${options.projectDescription}`
|
|
340
537
|
);
|
|
341
538
|
console.log();
|
|
342
|
-
console.log(
|
|
539
|
+
console.log(chalk4.bold(" Would run:"));
|
|
343
540
|
console.log();
|
|
344
|
-
console.log(` ${
|
|
345
|
-
console.log(` ${
|
|
346
|
-
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`);
|
|
347
544
|
if (!options.skipGit) {
|
|
348
|
-
console.log(` ${
|
|
545
|
+
console.log(` ${chalk4.dim("\u2022")} Initialize git repository`);
|
|
349
546
|
}
|
|
350
547
|
if (!options.skipInstall) {
|
|
351
|
-
console.log(` ${
|
|
548
|
+
console.log(` ${chalk4.dim("\u2022")} Install dependencies (pnpm install)`);
|
|
352
549
|
}
|
|
353
550
|
console.log();
|
|
354
551
|
}
|
|
355
552
|
async function create(projectName, flags) {
|
|
356
553
|
printHeader();
|
|
554
|
+
const checksPass = await runPreflightChecks();
|
|
555
|
+
if (!checksPass) {
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
357
558
|
const options = await getProjectOptions(projectName, flags);
|
|
358
559
|
if (!options) {
|
|
359
560
|
return;
|
|
@@ -392,8 +593,7 @@ async function create(projectName, flags) {
|
|
|
392
593
|
const spinner = ora();
|
|
393
594
|
try {
|
|
394
595
|
spinner.start("Downloading template from GitHub...");
|
|
395
|
-
await downloadAndPrepareTemplate(targetDir);
|
|
396
|
-
spinner.succeed("Downloaded template");
|
|
596
|
+
await downloadAndPrepareTemplate(targetDir, spinner);
|
|
397
597
|
spinner.start("Configuring project...");
|
|
398
598
|
await transformFiles(targetDir, {
|
|
399
599
|
projectName: options.projectName,
|
|
@@ -422,9 +622,34 @@ async function create(projectName, flags) {
|
|
|
422
622
|
);
|
|
423
623
|
}
|
|
424
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
|
+
}
|
|
425
649
|
printSuccess(
|
|
426
650
|
options.projectName,
|
|
427
|
-
options.useCurrentDir ? "." : options.projectName
|
|
651
|
+
options.useCurrentDir ? "." : options.projectName,
|
|
652
|
+
ranAutomaticSetup
|
|
428
653
|
);
|
|
429
654
|
} catch (error) {
|
|
430
655
|
spinner.fail();
|