@codecanvascollective/scaffold 0.1.0 → 0.1.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/LICENSE +21 -21
- package/README.md +7 -7
- package/dist/index.js +126 -71
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/templates/shared/eslint.config.js.hbs +0 -2
- package/dist/chunk-2A65KFCS.js +0 -76
- package/dist/chunk-2A65KFCS.js.map +0 -1
- package/dist/file-5IKT7CEX.js +0 -24
- package/dist/file-5IKT7CEX.js.map +0 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 CodeCanvas Collective
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CodeCanvas Collective
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -24,13 +24,13 @@ npx @codecanvascollective/scaffold create my-app --express
|
|
|
24
24
|
|
|
25
25
|
## Supported Frameworks
|
|
26
26
|
|
|
27
|
-
| Framework
|
|
28
|
-
|
|
29
|
-
| React + Vite
|
|
30
|
-
| Next.js (App Router) | Frontend | Base, NextAuth, Prisma
|
|
31
|
-
| Angular 18
|
|
32
|
-
| Express.js
|
|
33
|
-
| FastAPI
|
|
27
|
+
| Framework | Type | Variants |
|
|
28
|
+
| -------------------- | -------- | ------------------------- |
|
|
29
|
+
| React + Vite | Frontend | Base, Tailwind, shadcn/ui |
|
|
30
|
+
| Next.js (App Router) | Frontend | Base, NextAuth, Prisma |
|
|
31
|
+
| Angular 18 | Frontend | Base |
|
|
32
|
+
| Express.js | Backend | Base, Prisma |
|
|
33
|
+
| FastAPI | Backend | Base |
|
|
34
34
|
|
|
35
35
|
## Features
|
|
36
36
|
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
createDir,
|
|
4
|
-
directoryExists,
|
|
5
|
-
getTemplatesDir,
|
|
6
|
-
renderAndWrite
|
|
7
|
-
} from "./chunk-2A65KFCS.js";
|
|
8
2
|
|
|
9
3
|
// src/cli.ts
|
|
10
4
|
import { Command as Command4 } from "commander";
|
|
11
5
|
|
|
12
6
|
// src/commands/create.ts
|
|
13
7
|
import { Command } from "commander";
|
|
14
|
-
import
|
|
15
|
-
import
|
|
8
|
+
import path5 from "path";
|
|
9
|
+
import fs3 from "fs-extra";
|
|
16
10
|
|
|
17
11
|
// src/prompts/framework.ts
|
|
18
12
|
import inquirer from "inquirer";
|
|
@@ -87,13 +81,23 @@ async function promptFeatures(projectType, framework) {
|
|
|
87
81
|
checked: true,
|
|
88
82
|
disabled: isPython ? "N/A for Python" : false
|
|
89
83
|
},
|
|
90
|
-
{
|
|
84
|
+
{
|
|
85
|
+
name: "ESLint + Prettier",
|
|
86
|
+
value: "eslint",
|
|
87
|
+
checked: !isPython,
|
|
88
|
+
disabled: isPython ? "N/A for Python" : false
|
|
89
|
+
},
|
|
91
90
|
{ name: "Tailwind CSS", value: "tailwind", checked: false },
|
|
92
91
|
{ name: "shadcn/ui components", value: "shadcn", checked: false },
|
|
93
92
|
{ name: isPython ? "Pytest testing" : "Vitest testing", value: "testing", checked: true },
|
|
94
93
|
{ name: "GitHub Actions CI/CD", value: "githubActions", checked: false },
|
|
95
94
|
{ name: "Docker setup", value: "docker", checked: false },
|
|
96
|
-
{
|
|
95
|
+
{
|
|
96
|
+
name: "Husky pre-commit hooks",
|
|
97
|
+
value: "husky",
|
|
98
|
+
checked: false,
|
|
99
|
+
disabled: isPython ? "N/A for Python" : false
|
|
100
|
+
},
|
|
97
101
|
{ name: ".env example file", value: "envExample", checked: false }
|
|
98
102
|
];
|
|
99
103
|
const filteredChoices = projectType === "backend" ? choices.filter((c) => !["tailwind", "shadcn"].includes(c.value)) : choices;
|
|
@@ -241,11 +245,11 @@ async function runPrompts(projectName) {
|
|
|
241
245
|
}
|
|
242
246
|
|
|
243
247
|
// src/generators/react.ts
|
|
244
|
-
import
|
|
248
|
+
import path3 from "path";
|
|
245
249
|
|
|
246
250
|
// src/generators/base.ts
|
|
247
|
-
import
|
|
248
|
-
import
|
|
251
|
+
import path2 from "path";
|
|
252
|
+
import fs2 from "fs-extra";
|
|
249
253
|
import { execSync } from "child_process";
|
|
250
254
|
|
|
251
255
|
// src/utils/logger.ts
|
|
@@ -276,6 +280,53 @@ function banner(text) {
|
|
|
276
280
|
console.log(chalk.bold.cyan(text));
|
|
277
281
|
}
|
|
278
282
|
|
|
283
|
+
// src/utils/file.ts
|
|
284
|
+
import path from "path";
|
|
285
|
+
import fs from "fs-extra";
|
|
286
|
+
import Handlebars from "handlebars";
|
|
287
|
+
import { fileURLToPath } from "url";
|
|
288
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
289
|
+
var __dirname2 = path.dirname(__filename2);
|
|
290
|
+
Handlebars.registerHelper("eq", (a, b) => a === b);
|
|
291
|
+
Handlebars.registerHelper("neq", (a, b) => a !== b);
|
|
292
|
+
Handlebars.registerHelper("or", (a, b) => a || b);
|
|
293
|
+
Handlebars.registerHelper("and", (a, b) => a && b);
|
|
294
|
+
Handlebars.registerHelper("lowercase", (str) => str?.toLowerCase());
|
|
295
|
+
Handlebars.registerHelper("year", () => (/* @__PURE__ */ new Date()).getFullYear());
|
|
296
|
+
Handlebars.registerHelper(
|
|
297
|
+
"join",
|
|
298
|
+
(arr, sep) => Array.isArray(arr) ? arr.join(typeof sep === "string" ? sep : ", ") : ""
|
|
299
|
+
);
|
|
300
|
+
function getTemplatesDir() {
|
|
301
|
+
const isBundled = __dirname2.replace(/\\/g, "/").endsWith("/dist") || __dirname2.replace(/\\/g, "/").includes("/dist/");
|
|
302
|
+
const projectRoot = isBundled ? path.resolve(__dirname2, "..") : path.resolve(__dirname2, "..", "..");
|
|
303
|
+
return path.resolve(projectRoot, "src", "templates");
|
|
304
|
+
}
|
|
305
|
+
async function createDir(dirPath) {
|
|
306
|
+
await fs.ensureDir(dirPath);
|
|
307
|
+
}
|
|
308
|
+
async function writeFile(filePath, content) {
|
|
309
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
310
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
311
|
+
}
|
|
312
|
+
async function renderTemplate(templatePath, data) {
|
|
313
|
+
const templateContent = await fs.readFile(templatePath, "utf-8");
|
|
314
|
+
const template = Handlebars.compile(templateContent, { noEscape: true });
|
|
315
|
+
return template(data);
|
|
316
|
+
}
|
|
317
|
+
async function renderAndWrite(templatePath, outputPath, data) {
|
|
318
|
+
const content = await renderTemplate(templatePath, data);
|
|
319
|
+
await writeFile(outputPath, content);
|
|
320
|
+
}
|
|
321
|
+
async function directoryExists(dirPath) {
|
|
322
|
+
try {
|
|
323
|
+
const stat = await fs.stat(dirPath);
|
|
324
|
+
return stat.isDirectory();
|
|
325
|
+
} catch {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
279
330
|
// src/utils/validator.ts
|
|
280
331
|
var VALID_PROJECT_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
281
332
|
function validateProjectName(name) {
|
|
@@ -320,7 +371,7 @@ var BaseGenerator = class {
|
|
|
320
371
|
async applyVariant() {
|
|
321
372
|
if (this.config.variant === "base") return;
|
|
322
373
|
const variantDir = this.getVariantTemplateDir();
|
|
323
|
-
if (await
|
|
374
|
+
if (await fs2.pathExists(variantDir)) {
|
|
324
375
|
const s = spinner(`Applying ${this.config.variant} variant`);
|
|
325
376
|
s.start();
|
|
326
377
|
await this.renderDirectory(variantDir, this.targetDir);
|
|
@@ -329,46 +380,46 @@ var BaseGenerator = class {
|
|
|
329
380
|
}
|
|
330
381
|
}
|
|
331
382
|
async applySharedConfigs() {
|
|
332
|
-
const sharedDir =
|
|
383
|
+
const sharedDir = path2.join(this.templatesDir, "shared");
|
|
333
384
|
if (this.config.features.eslint) {
|
|
334
385
|
const s = spinner("Setting up ESLint + Prettier");
|
|
335
386
|
s.start();
|
|
336
387
|
await this.renderTemplateFile(
|
|
337
|
-
|
|
338
|
-
|
|
388
|
+
path2.join(sharedDir, "eslint.config.js.hbs"),
|
|
389
|
+
path2.join(this.targetDir, "eslint.config.js")
|
|
339
390
|
);
|
|
340
391
|
await this.renderTemplateFile(
|
|
341
|
-
|
|
342
|
-
|
|
392
|
+
path2.join(sharedDir, "prettier.config.js.hbs"),
|
|
393
|
+
path2.join(this.targetDir, ".prettierrc")
|
|
343
394
|
);
|
|
344
395
|
s.stop();
|
|
345
396
|
success("Setting up ESLint + Prettier");
|
|
346
397
|
}
|
|
347
398
|
await this.renderTemplateFile(
|
|
348
|
-
|
|
349
|
-
|
|
399
|
+
path2.join(sharedDir, "gitignore.hbs"),
|
|
400
|
+
path2.join(this.targetDir, ".gitignore")
|
|
350
401
|
);
|
|
351
402
|
const s2 = spinner("Generating README.md");
|
|
352
403
|
s2.start();
|
|
353
404
|
await this.renderTemplateFile(
|
|
354
|
-
|
|
355
|
-
|
|
405
|
+
path2.join(sharedDir, "readme.md.hbs"),
|
|
406
|
+
path2.join(this.targetDir, "README.md")
|
|
356
407
|
);
|
|
357
408
|
s2.stop();
|
|
358
409
|
success("Generating README.md");
|
|
359
410
|
await this.renderTemplateFile(
|
|
360
|
-
|
|
361
|
-
|
|
411
|
+
path2.join(sharedDir, "license.hbs"),
|
|
412
|
+
path2.join(this.targetDir, "LICENSE")
|
|
362
413
|
);
|
|
363
414
|
}
|
|
364
415
|
async applyFeatures() {
|
|
365
|
-
const sharedDir =
|
|
416
|
+
const sharedDir = path2.join(this.templatesDir, "shared");
|
|
366
417
|
if (this.config.features.githubActions) {
|
|
367
418
|
const s = spinner("Creating GitHub Actions workflow");
|
|
368
419
|
s.start();
|
|
369
420
|
await this.renderTemplateFile(
|
|
370
|
-
|
|
371
|
-
|
|
421
|
+
path2.join(sharedDir, "github-ci.yml.hbs"),
|
|
422
|
+
path2.join(this.targetDir, ".github", "workflows", "ci.yml")
|
|
372
423
|
);
|
|
373
424
|
s.stop();
|
|
374
425
|
success("Creating GitHub Actions workflow");
|
|
@@ -377,12 +428,12 @@ var BaseGenerator = class {
|
|
|
377
428
|
const s = spinner("Adding Docker configuration");
|
|
378
429
|
s.start();
|
|
379
430
|
await this.renderTemplateFile(
|
|
380
|
-
|
|
381
|
-
|
|
431
|
+
path2.join(sharedDir, "docker", "Dockerfile.hbs"),
|
|
432
|
+
path2.join(this.targetDir, "Dockerfile")
|
|
382
433
|
);
|
|
383
434
|
await this.renderTemplateFile(
|
|
384
|
-
|
|
385
|
-
|
|
435
|
+
path2.join(sharedDir, "docker", "docker-compose.yml.hbs"),
|
|
436
|
+
path2.join(this.targetDir, "docker-compose.yml")
|
|
386
437
|
);
|
|
387
438
|
s.stop();
|
|
388
439
|
success("Adding Docker configuration");
|
|
@@ -415,7 +466,7 @@ var BaseGenerator = class {
|
|
|
415
466
|
}
|
|
416
467
|
async renderTemplateFile(templatePath, outputPath) {
|
|
417
468
|
const templateData = this.getTemplateData();
|
|
418
|
-
if (await
|
|
469
|
+
if (await fs2.pathExists(templatePath)) {
|
|
419
470
|
await renderAndWrite(templatePath, outputPath, templateData);
|
|
420
471
|
this.result.filesCreated.push(outputPath);
|
|
421
472
|
} else {
|
|
@@ -423,18 +474,18 @@ var BaseGenerator = class {
|
|
|
423
474
|
}
|
|
424
475
|
}
|
|
425
476
|
async renderDirectory(srcDir, destDir) {
|
|
426
|
-
const entries = await
|
|
477
|
+
const entries = await fs2.readdir(srcDir, { withFileTypes: true });
|
|
427
478
|
for (const entry of entries) {
|
|
428
|
-
const srcPath =
|
|
479
|
+
const srcPath = path2.join(srcDir, entry.name);
|
|
429
480
|
const destName = entry.name.replace(/\.hbs$/, "");
|
|
430
|
-
const destPath =
|
|
481
|
+
const destPath = path2.join(destDir, destName);
|
|
431
482
|
if (entry.isDirectory()) {
|
|
432
483
|
await this.renderDirectory(srcPath, destPath);
|
|
433
484
|
} else if (entry.name.endsWith(".hbs")) {
|
|
434
485
|
await this.renderTemplateFile(srcPath, destPath);
|
|
435
486
|
} else {
|
|
436
|
-
await
|
|
437
|
-
await
|
|
487
|
+
await fs2.ensureDir(path2.dirname(destPath));
|
|
488
|
+
await fs2.copy(srcPath, destPath);
|
|
438
489
|
this.result.filesCreated.push(destPath);
|
|
439
490
|
}
|
|
440
491
|
}
|
|
@@ -446,10 +497,10 @@ var BaseGenerator = class {
|
|
|
446
497
|
};
|
|
447
498
|
}
|
|
448
499
|
getBaseTemplateDir() {
|
|
449
|
-
return
|
|
500
|
+
return path2.join(this.templatesDir, this.config.framework, "base");
|
|
450
501
|
}
|
|
451
502
|
getVariantTemplateDir() {
|
|
452
|
-
return
|
|
503
|
+
return path2.join(this.templatesDir, this.config.framework, this.config.variant);
|
|
453
504
|
}
|
|
454
505
|
};
|
|
455
506
|
|
|
@@ -467,7 +518,7 @@ var ReactGenerator = class extends BaseGenerator {
|
|
|
467
518
|
}
|
|
468
519
|
}
|
|
469
520
|
getVariantPath(variant) {
|
|
470
|
-
return
|
|
521
|
+
return path3.join(this.templatesDir, "react", variant);
|
|
471
522
|
}
|
|
472
523
|
};
|
|
473
524
|
|
|
@@ -527,35 +578,35 @@ var FastAPIGenerator = class extends BaseGenerator {
|
|
|
527
578
|
}
|
|
528
579
|
}
|
|
529
580
|
async applySharedConfigs() {
|
|
530
|
-
const
|
|
531
|
-
const sharedDir =
|
|
581
|
+
const path6 = await import("path");
|
|
582
|
+
const sharedDir = path6.join(this.templatesDir, "shared");
|
|
532
583
|
await this.renderTemplateFile(
|
|
533
|
-
|
|
534
|
-
|
|
584
|
+
path6.join(sharedDir, "gitignore.hbs"),
|
|
585
|
+
path6.join(this.targetDir, ".gitignore")
|
|
535
586
|
);
|
|
536
587
|
const s = spinner("Generating README.md");
|
|
537
588
|
s.start();
|
|
538
589
|
await this.renderTemplateFile(
|
|
539
|
-
|
|
540
|
-
|
|
590
|
+
path6.join(sharedDir, "readme.md.hbs"),
|
|
591
|
+
path6.join(this.targetDir, "README.md")
|
|
541
592
|
);
|
|
542
593
|
s.stop();
|
|
543
594
|
success("Generating README.md");
|
|
544
595
|
await this.renderTemplateFile(
|
|
545
|
-
|
|
546
|
-
|
|
596
|
+
path6.join(sharedDir, "license.hbs"),
|
|
597
|
+
path6.join(this.targetDir, "LICENSE")
|
|
547
598
|
);
|
|
548
599
|
}
|
|
549
600
|
};
|
|
550
601
|
|
|
551
602
|
// src/generators/fullstack.ts
|
|
552
|
-
import
|
|
603
|
+
import path4 from "path";
|
|
553
604
|
var FullstackGenerator = class extends BaseGenerator {
|
|
554
605
|
async generateBase() {
|
|
555
606
|
const s = spinner("Setting up full-stack monorepo");
|
|
556
607
|
s.start();
|
|
557
|
-
const webDir =
|
|
558
|
-
const apiDir =
|
|
608
|
+
const webDir = path4.join(this.targetDir, "apps", "web");
|
|
609
|
+
const apiDir = path4.join(this.targetDir, "apps", "api");
|
|
559
610
|
await createDir(webDir);
|
|
560
611
|
await createDir(apiDir);
|
|
561
612
|
s.stop();
|
|
@@ -565,9 +616,8 @@ var FullstackGenerator = class extends BaseGenerator {
|
|
|
565
616
|
type: "frontend",
|
|
566
617
|
gitInit: false
|
|
567
618
|
};
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
await frontendGen.generateBase();
|
|
619
|
+
const frontendGen = this.createFrontendGenerator(frontendConfig, webDir);
|
|
620
|
+
await frontendGen.generate();
|
|
571
621
|
const backendConfig = {
|
|
572
622
|
...this.config,
|
|
573
623
|
type: "backend",
|
|
@@ -575,8 +625,8 @@ var FullstackGenerator = class extends BaseGenerator {
|
|
|
575
625
|
gitInit: false
|
|
576
626
|
};
|
|
577
627
|
const backendGen = new ExpressGenerator(backendConfig, apiDir);
|
|
578
|
-
await backendGen.
|
|
579
|
-
const rootPkgPath =
|
|
628
|
+
await backendGen.generate();
|
|
629
|
+
const rootPkgPath = path4.join(this.targetDir, "package.json");
|
|
580
630
|
const rootPkg = {
|
|
581
631
|
name: this.config.name,
|
|
582
632
|
private: true,
|
|
@@ -588,18 +638,17 @@ var FullstackGenerator = class extends BaseGenerator {
|
|
|
588
638
|
test: "npm run --workspaces test"
|
|
589
639
|
}
|
|
590
640
|
};
|
|
591
|
-
|
|
592
|
-
await writeFile2(rootPkgPath, JSON.stringify(rootPkg, null, 2) + "\n");
|
|
641
|
+
await writeFile(rootPkgPath, JSON.stringify(rootPkg, null, 2) + "\n");
|
|
593
642
|
}
|
|
594
|
-
|
|
595
|
-
switch (
|
|
643
|
+
createFrontendGenerator(config, targetDir) {
|
|
644
|
+
switch (config.framework) {
|
|
596
645
|
case "nextjs":
|
|
597
|
-
return NextjsGenerator;
|
|
646
|
+
return new NextjsGenerator(config, targetDir);
|
|
598
647
|
case "angular":
|
|
599
|
-
return AngularGenerator;
|
|
648
|
+
return new AngularGenerator(config, targetDir);
|
|
600
649
|
case "react":
|
|
601
650
|
default:
|
|
602
|
-
return ReactGenerator;
|
|
651
|
+
return new ReactGenerator(config, targetDir);
|
|
603
652
|
}
|
|
604
653
|
}
|
|
605
654
|
};
|
|
@@ -665,7 +714,7 @@ async function handleCreate(projectName, options) {
|
|
|
665
714
|
if (nameValidation !== true) {
|
|
666
715
|
throw new Error(nameValidation);
|
|
667
716
|
}
|
|
668
|
-
const targetDir =
|
|
717
|
+
const targetDir = path5.resolve(process.cwd(), projectName);
|
|
669
718
|
if (!options.force) {
|
|
670
719
|
const dirValidation = await validateDirectory(targetDir);
|
|
671
720
|
if (dirValidation !== true) {
|
|
@@ -725,8 +774,8 @@ function buildQuickConfig(projectName, options) {
|
|
|
725
774
|
return config;
|
|
726
775
|
}
|
|
727
776
|
async function loadConfigFromFile(configPath, projectName) {
|
|
728
|
-
const resolvedPath =
|
|
729
|
-
const content = await
|
|
777
|
+
const resolvedPath = path5.resolve(process.cwd(), configPath);
|
|
778
|
+
const content = await fs3.readJSON(resolvedPath);
|
|
730
779
|
return { ...DEFAULT_CONFIG, ...content, name: projectName };
|
|
731
780
|
}
|
|
732
781
|
|
|
@@ -748,7 +797,9 @@ function listCommand() {
|
|
|
748
797
|
newLine();
|
|
749
798
|
for (const fw of frameworks) {
|
|
750
799
|
const meta = FRAMEWORKS[fw];
|
|
751
|
-
console.log(
|
|
800
|
+
console.log(
|
|
801
|
+
` ${chalk2.green(meta.displayName.padEnd(25))} ${chalk2.gray(meta.description)}`
|
|
802
|
+
);
|
|
752
803
|
for (const variant of meta.variants) {
|
|
753
804
|
const label = VARIANT_DISPLAY_NAMES[variant];
|
|
754
805
|
console.log(` ${chalk2.gray("\u2022")} ${label}`);
|
|
@@ -795,10 +846,14 @@ function doctorCommand() {
|
|
|
795
846
|
console.log(` ${chalk3.green("\u2714")} ${check.name.padEnd(12)} ${chalk3.gray(versionStr)}`);
|
|
796
847
|
} catch {
|
|
797
848
|
if (check.required) {
|
|
798
|
-
console.log(
|
|
849
|
+
console.log(
|
|
850
|
+
` ${chalk3.red("\u2716")} ${check.name.padEnd(12)} ${chalk3.red("not found (required)")}`
|
|
851
|
+
);
|
|
799
852
|
allGood = false;
|
|
800
853
|
} else {
|
|
801
|
-
console.log(
|
|
854
|
+
console.log(
|
|
855
|
+
` ${chalk3.yellow("\u2013")} ${check.name.padEnd(12)} ${chalk3.gray("not found (optional)")}`
|
|
856
|
+
);
|
|
802
857
|
}
|
|
803
858
|
}
|
|
804
859
|
}
|
|
@@ -815,7 +870,7 @@ function doctorCommand() {
|
|
|
815
870
|
// src/cli.ts
|
|
816
871
|
function createCli() {
|
|
817
872
|
const program2 = new Command4();
|
|
818
|
-
program2.name("scaffold").description("CLI tool for scaffolding modern full-stack projects").version("0.1.
|
|
873
|
+
program2.name("scaffold").description("CLI tool for scaffolding modern full-stack projects").version("0.1.1").option("--verbose", "Enable verbose output").hook("preAction", (thisCommand) => {
|
|
819
874
|
const opts = thisCommand.opts();
|
|
820
875
|
if (opts.verbose) {
|
|
821
876
|
setVerbose(true);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/create.ts","../src/prompts/framework.ts","../src/prompts/features.ts","../src/prompts/config.ts","../src/constants.ts","../src/prompts/index.ts","../src/generators/react.ts","../src/generators/base.ts","../src/utils/logger.ts","../src/utils/validator.ts","../src/generators/nextjs.ts","../src/generators/angular.ts","../src/generators/express.ts","../src/generators/fastapi.ts","../src/generators/fullstack.ts","../src/generators/index.ts","../src/types/config.ts","../src/commands/list.ts","../src/commands/doctor.ts","../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { createCommand } from './commands/create.js';\nimport { listCommand } from './commands/list.js';\nimport { doctorCommand } from './commands/doctor.js';\nimport { setVerbose } from './utils/logger.js';\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('scaffold')\n .description('CLI tool for scaffolding modern full-stack projects')\n .version('0.1.0')\n .option('--verbose', 'Enable verbose output')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts.verbose) {\n setVerbose(true);\n }\n });\n\n program.addCommand(createCommand());\n program.addCommand(listCommand());\n program.addCommand(doctorCommand());\n\n return program;\n}\n","import { Command } from 'commander';\nimport path from 'node:path';\nimport fs from 'fs-extra';\nimport { runPrompts } from '../prompts/index.js';\nimport { createGenerator } from '../generators/index.js';\nimport { validateProjectName, validateDirectory, success, error, newLine, banner } from '../utils/index.js';\nimport type { ProjectConfig } from '../types/index.js';\nimport { DEFAULT_CONFIG } from '../types/index.js';\n\ninterface CreateOptions {\n react?: boolean;\n nextjs?: boolean;\n angular?: boolean;\n express?: boolean;\n fastapi?: boolean;\n yes?: boolean;\n from?: string;\n force?: boolean;\n}\n\nexport function createCommand(): Command {\n const cmd = new Command('create')\n .description('Create a new project')\n .argument('<project-name>', 'Name of the project')\n .option('--react', 'Create a React + Vite project')\n .option('--nextjs', 'Create a Next.js project')\n .option('--angular', 'Create an Angular project')\n .option('--express', 'Create an Express.js project')\n .option('--fastapi', 'Create a FastAPI project')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .option('--from <path>', 'Create from a config file')\n .option('--force', 'Overwrite existing directory')\n .action(async (projectName: string, options: CreateOptions) => {\n try {\n await handleCreate(projectName, options);\n } catch (err) {\n error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nasync function handleCreate(projectName: string, options: CreateOptions): Promise<void> {\n const nameValidation = validateProjectName(projectName);\n if (nameValidation !== true) {\n throw new Error(nameValidation);\n }\n\n const targetDir = path.resolve(process.cwd(), projectName);\n\n if (!options.force) {\n const dirValidation = await validateDirectory(targetDir);\n if (dirValidation !== true) {\n throw new Error(dirValidation);\n }\n }\n\n let config: ProjectConfig;\n\n if (options.from) {\n config = await loadConfigFromFile(options.from, projectName);\n } else if (options.yes || hasFrameworkFlag(options)) {\n config = buildQuickConfig(projectName, options);\n } else {\n config = await runPrompts(projectName);\n }\n\n newLine();\n banner(`Creating project \"${config.name}\"...`);\n newLine();\n\n const generator = createGenerator(config, targetDir);\n await generator.generate();\n\n newLine();\n success(`Project \"${config.name}\" created successfully! 🎉`);\n newLine();\n console.log('Next steps:');\n console.log(` cd ${config.name}`);\n\n if (config.framework === 'fastapi') {\n console.log(' pip install -r requirements.txt');\n console.log(' uvicorn app.main:app --reload');\n } else {\n console.log(` ${config.packageManager} install`);\n console.log(` ${config.packageManager} run dev`);\n }\n\n newLine();\n console.log('Happy coding! 🚀');\n newLine();\n}\n\nfunction hasFrameworkFlag(options: CreateOptions): boolean {\n return !!(options.react || options.nextjs || options.angular || options.express || options.fastapi);\n}\n\nfunction buildQuickConfig(projectName: string, options: CreateOptions): ProjectConfig {\n const config: ProjectConfig = { ...DEFAULT_CONFIG, name: projectName };\n\n if (options.react) {\n config.type = 'frontend';\n config.framework = 'react';\n } else if (options.nextjs) {\n config.type = 'frontend';\n config.framework = 'nextjs';\n } else if (options.angular) {\n config.type = 'frontend';\n config.framework = 'angular';\n } else if (options.express) {\n config.type = 'backend';\n config.framework = 'express';\n } else if (options.fastapi) {\n config.type = 'backend';\n config.framework = 'fastapi';\n }\n\n return config;\n}\n\nasync function loadConfigFromFile(configPath: string, projectName: string): Promise<ProjectConfig> {\n const resolvedPath = path.resolve(process.cwd(), configPath);\n const content = await fs.readJSON(resolvedPath);\n return { ...DEFAULT_CONFIG, ...content, name: projectName };\n}\n","import inquirer from 'inquirer';\nimport type { ProjectType, FrameworkType } from '../types/index.js';\n\nexport async function promptProjectType(): Promise<ProjectType> {\n const { projectType } = await inquirer.prompt([\n {\n type: 'list',\n name: 'projectType',\n message: 'What type of project?',\n choices: [\n { name: 'Frontend (React, Next.js, Angular)', value: 'frontend' },\n { name: 'Backend (Express, FastAPI)', value: 'backend' },\n { name: 'Full-Stack (Frontend + Backend)', value: 'fullstack' },\n ],\n },\n ]);\n\n return projectType;\n}\n\nexport async function promptFramework(projectType: ProjectType): Promise<FrameworkType> {\n if (projectType === 'frontend') {\n const { framework } = await inquirer.prompt([\n {\n type: 'list',\n name: 'framework',\n message: 'Which frontend framework?',\n choices: [\n { name: 'React + Vite', value: 'react' },\n { name: 'Next.js (App Router)', value: 'nextjs' },\n { name: 'Angular 18', value: 'angular' },\n ],\n },\n ]);\n return framework;\n }\n\n if (projectType === 'backend') {\n const { framework } = await inquirer.prompt([\n {\n type: 'list',\n name: 'framework',\n message: 'Which backend framework?',\n choices: [\n { name: 'Express.js', value: 'express' },\n { name: 'FastAPI (Python)', value: 'fastapi' },\n ],\n },\n ]);\n return framework;\n }\n\n // fullstack — prompt for frontend (we'll use express as the backend)\n const { framework } = await inquirer.prompt([\n {\n type: 'list',\n name: 'framework',\n message: 'Which frontend framework?',\n choices: [\n { name: 'React + Vite', value: 'react' },\n { name: 'Next.js', value: 'nextjs' },\n { name: 'Angular 18', value: 'angular' },\n ],\n },\n ]);\n return framework;\n}\n","import inquirer from 'inquirer';\nimport type { FeatureFlags, ProjectType, FrameworkType } from '../types/index.js';\n\ninterface FeatureChoice {\n name: string;\n value: keyof FeatureFlags;\n checked: boolean;\n disabled?: string | false;\n}\n\nexport async function promptFeatures(\n projectType: ProjectType,\n framework: FrameworkType,\n): Promise<FeatureFlags> {\n const isPython = framework === 'fastapi';\n\n const choices: FeatureChoice[] = [\n {\n name: 'TypeScript',\n value: 'typescript',\n checked: true,\n disabled: isPython ? 'N/A for Python' : false,\n },\n { name: 'ESLint + Prettier', value: 'eslint', checked: !isPython, disabled: isPython ? 'N/A for Python' : false },\n { name: 'Tailwind CSS', value: 'tailwind', checked: false },\n { name: 'shadcn/ui components', value: 'shadcn', checked: false },\n { name: isPython ? 'Pytest testing' : 'Vitest testing', value: 'testing', checked: true },\n { name: 'GitHub Actions CI/CD', value: 'githubActions', checked: false },\n { name: 'Docker setup', value: 'docker', checked: false },\n { name: 'Husky pre-commit hooks', value: 'husky', checked: false, disabled: isPython ? 'N/A for Python' : false },\n { name: '.env example file', value: 'envExample', checked: false },\n ];\n\n // Filter out frontend-only features for backend projects\n const filteredChoices =\n projectType === 'backend'\n ? choices.filter((c) => !['tailwind', 'shadcn'].includes(c.value))\n : choices;\n\n const { features } = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'features',\n message: 'Select features:',\n choices: filteredChoices,\n },\n ]);\n\n const selectedFeatures = features as (keyof FeatureFlags)[];\n\n return {\n typescript: !isPython && selectedFeatures.includes('typescript'),\n eslint: !isPython && selectedFeatures.includes('eslint'),\n prettier: !isPython && selectedFeatures.includes('eslint'),\n tailwind: selectedFeatures.includes('tailwind'),\n shadcn: selectedFeatures.includes('shadcn'),\n testing: selectedFeatures.includes('testing'),\n githubActions: selectedFeatures.includes('githubActions'),\n docker: selectedFeatures.includes('docker'),\n husky: !isPython && selectedFeatures.includes('husky'),\n envExample: selectedFeatures.includes('envExample'),\n };\n}\n","import inquirer from 'inquirer';\nimport type { VariantType, PackageManager, FrameworkType } from '../types/index.js';\nimport { FRAMEWORKS, VARIANT_DISPLAY_NAMES } from '../constants.js';\n\nexport async function promptVariant(framework: FrameworkType): Promise<VariantType> {\n const meta = FRAMEWORKS[framework];\n\n if (meta.variants.length <= 1) {\n return 'base';\n }\n\n const { variant } = await inquirer.prompt([\n {\n type: 'list',\n name: 'variant',\n message: 'Select a template variant:',\n choices: meta.variants.map((v) => ({\n name: VARIANT_DISPLAY_NAMES[v],\n value: v,\n })),\n },\n ]);\n\n return variant;\n}\n\nexport async function promptPackageManager(): Promise<PackageManager> {\n const { packageManager } = await inquirer.prompt([\n {\n type: 'list',\n name: 'packageManager',\n message: 'Package manager?',\n choices: [\n { name: 'npm', value: 'npm' },\n { name: 'yarn', value: 'yarn' },\n { name: 'pnpm', value: 'pnpm' },\n ],\n },\n ]);\n\n return packageManager;\n}\n\nexport async function promptGitInit(): Promise<boolean> {\n const { gitInit } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'gitInit',\n message: 'Initialize git repository?',\n default: true,\n },\n ]);\n\n return gitInit;\n}\n","import type { FrameworkMeta, FrameworkType, VariantType } from './types/index.js';\n\nexport const FRAMEWORKS: Record<FrameworkType, FrameworkMeta> = {\n react: {\n name: 'react',\n displayName: 'React + Vite',\n description: 'React 19 with Vite and TypeScript',\n type: 'frontend',\n variants: ['base', 'with-tailwind', 'with-shadcn'],\n language: 'typescript',\n },\n nextjs: {\n name: 'nextjs',\n displayName: 'Next.js (App Router)',\n description: 'Next.js 15 with App Router and TypeScript',\n type: 'frontend',\n variants: ['base', 'with-auth', 'with-prisma'],\n language: 'typescript',\n },\n angular: {\n name: 'angular',\n displayName: 'Angular 18',\n description: 'Angular 18 standalone with TypeScript',\n type: 'frontend',\n variants: ['base'],\n language: 'typescript',\n },\n express: {\n name: 'express',\n displayName: 'Express.js',\n description: 'Express 5 with TypeScript',\n type: 'backend',\n variants: ['base', 'with-prisma'],\n language: 'typescript',\n },\n fastapi: {\n name: 'fastapi',\n displayName: 'FastAPI',\n description: 'FastAPI with Pydantic and Python',\n type: 'backend',\n variants: ['base'],\n language: 'python',\n },\n};\n\nexport const FRONTEND_FRAMEWORKS: FrameworkType[] = ['react', 'nextjs', 'angular'];\nexport const BACKEND_FRAMEWORKS: FrameworkType[] = ['express', 'fastapi'];\n\nexport const VARIANT_DISPLAY_NAMES: Record<VariantType, string> = {\n base: 'Base (minimal setup)',\n 'with-tailwind': 'With Tailwind CSS',\n 'with-shadcn': 'With shadcn/ui',\n 'with-auth': 'With NextAuth.js',\n 'with-prisma': 'With Prisma ORM',\n};\n","import type { ProjectConfig } from '../types/index.js';\nimport { promptProjectType, promptFramework } from './framework.js';\nimport { promptFeatures } from './features.js';\nimport { promptVariant, promptPackageManager, promptGitInit } from './config.js';\n\nexport async function runPrompts(projectName: string): Promise<ProjectConfig> {\n const type = await promptProjectType();\n const framework = await promptFramework(type);\n const variant = await promptVariant(framework);\n const features = await promptFeatures(type, framework);\n\n const isPython = framework === 'fastapi';\n const packageManager = isPython ? 'npm' : await promptPackageManager();\n const gitInit = await promptGitInit();\n\n return {\n name: projectName,\n type,\n framework,\n variant,\n features,\n packageManager,\n gitInit,\n };\n}\n","import path from 'node:path';\nimport { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class ReactGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring React + Vite + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring React + Vite + TypeScript');\n\n if (this.config.features.testing) {\n success('Configuring Vitest');\n }\n }\n\n private getVariantPath(variant: string): string {\n return path.join(this.templatesDir, 'react', variant);\n }\n}\n","import path from 'node:path';\nimport fs from 'fs-extra';\nimport { execSync } from 'node:child_process';\nimport { createDir, renderAndWrite, getTemplatesDir, spinner, success, debug } from '../utils/index.js';\nimport type { ProjectConfig } from '../types/index.js';\nimport type { GeneratorResult } from '../types/generator.js';\n\nexport abstract class BaseGenerator {\n protected config: ProjectConfig;\n protected targetDir: string;\n protected templatesDir: string;\n protected result: GeneratorResult = { filesCreated: [], warnings: [] };\n\n constructor(config: ProjectConfig, targetDir: string) {\n this.config = config;\n this.targetDir = targetDir;\n this.templatesDir = getTemplatesDir();\n }\n\n async generate(): Promise<GeneratorResult> {\n await this.createProjectDir();\n await this.generateBase();\n await this.applyVariant();\n await this.applySharedConfigs();\n await this.applyFeatures();\n await this.initGit();\n return this.result;\n }\n\n protected abstract generateBase(): Promise<void>;\n\n protected async applyVariant(): Promise<void> {\n if (this.config.variant === 'base') return;\n\n const variantDir = this.getVariantTemplateDir();\n if (await fs.pathExists(variantDir)) {\n const s = spinner(`Applying ${this.config.variant} variant`);\n s.start();\n await this.renderDirectory(variantDir, this.targetDir);\n s.stop();\n success(`Applied ${this.config.variant} variant`);\n }\n }\n\n protected async applySharedConfigs(): Promise<void> {\n const sharedDir = path.join(this.templatesDir, 'shared');\n\n if (this.config.features.eslint) {\n const s = spinner('Setting up ESLint + Prettier');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'eslint.config.js.hbs'),\n path.join(this.targetDir, 'eslint.config.js'),\n );\n await this.renderTemplateFile(\n path.join(sharedDir, 'prettier.config.js.hbs'),\n path.join(this.targetDir, '.prettierrc'),\n );\n s.stop();\n success('Setting up ESLint + Prettier');\n }\n\n // Gitignore\n await this.renderTemplateFile(\n path.join(sharedDir, 'gitignore.hbs'),\n path.join(this.targetDir, '.gitignore'),\n );\n\n // README\n const s2 = spinner('Generating README.md');\n s2.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'readme.md.hbs'),\n path.join(this.targetDir, 'README.md'),\n );\n s2.stop();\n success('Generating README.md');\n\n // License\n await this.renderTemplateFile(\n path.join(sharedDir, 'license.hbs'),\n path.join(this.targetDir, 'LICENSE'),\n );\n }\n\n protected async applyFeatures(): Promise<void> {\n const sharedDir = path.join(this.templatesDir, 'shared');\n\n if (this.config.features.githubActions) {\n const s = spinner('Creating GitHub Actions workflow');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'github-ci.yml.hbs'),\n path.join(this.targetDir, '.github', 'workflows', 'ci.yml'),\n );\n s.stop();\n success('Creating GitHub Actions workflow');\n }\n\n if (this.config.features.docker) {\n const s = spinner('Adding Docker configuration');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'docker', 'Dockerfile.hbs'),\n path.join(this.targetDir, 'Dockerfile'),\n );\n await this.renderTemplateFile(\n path.join(sharedDir, 'docker', 'docker-compose.yml.hbs'),\n path.join(this.targetDir, 'docker-compose.yml'),\n );\n s.stop();\n success('Adding Docker configuration');\n }\n }\n\n protected async createProjectDir(): Promise<void> {\n const s = spinner('Creating project structure');\n s.start();\n await createDir(this.targetDir);\n s.stop();\n success('Creating project structure');\n }\n\n protected async initGit(): Promise<void> {\n if (!this.config.gitInit) return;\n\n const s = spinner('Initializing git repository');\n s.start();\n try {\n execSync('git init', { cwd: this.targetDir, stdio: 'pipe' });\n execSync('git add -A', { cwd: this.targetDir, stdio: 'pipe' });\n execSync('git commit -m \"Initial commit from scaffold\"', {\n cwd: this.targetDir,\n stdio: 'pipe',\n });\n s.stop();\n success('Initializing git repository');\n } catch {\n s.stop();\n debug('Git initialization skipped (git not available)');\n }\n }\n\n protected async renderTemplateFile(\n templatePath: string,\n outputPath: string,\n ): Promise<void> {\n const templateData = this.getTemplateData();\n\n if (await fs.pathExists(templatePath)) {\n await renderAndWrite(templatePath, outputPath, templateData);\n this.result.filesCreated.push(outputPath);\n } else {\n debug(`Template not found: ${templatePath}`);\n }\n }\n\n protected async renderDirectory(srcDir: string, destDir: string): Promise<void> {\n const entries = await fs.readdir(srcDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(srcDir, entry.name);\n const destName = entry.name.replace(/\\.hbs$/, '');\n const destPath = path.join(destDir, destName);\n\n if (entry.isDirectory()) {\n await this.renderDirectory(srcPath, destPath);\n } else if (entry.name.endsWith('.hbs')) {\n await this.renderTemplateFile(srcPath, destPath);\n } else {\n await fs.ensureDir(path.dirname(destPath));\n await fs.copy(srcPath, destPath);\n this.result.filesCreated.push(destPath);\n }\n }\n }\n\n protected getTemplateData(): object {\n return {\n ...this.config,\n year: new Date().getFullYear(),\n };\n }\n\n protected getBaseTemplateDir(): string {\n return path.join(this.templatesDir, this.config.framework, 'base');\n }\n\n protected getVariantTemplateDir(): string {\n return path.join(this.templatesDir, this.config.framework, this.config.variant);\n }\n}\n","import chalk from 'chalk';\nimport ora, { type Ora } from 'ora';\n\nlet verbose = false;\n\nexport function setVerbose(value: boolean): void {\n verbose = value;\n}\n\nexport function info(message: string): void {\n console.log(chalk.blue('ℹ'), message);\n}\n\nexport function success(message: string): void {\n console.log(chalk.green('✔'), message);\n}\n\nexport function warn(message: string): void {\n console.log(chalk.yellow('⚠'), message);\n}\n\nexport function error(message: string): void {\n console.log(chalk.red('✖'), message);\n}\n\nexport function debug(message: string): void {\n if (verbose) {\n console.log(chalk.gray('⬥'), chalk.gray(message));\n }\n}\n\nexport function spinner(text: string): Ora {\n return ora({ text, color: 'cyan' });\n}\n\nexport function newLine(): void {\n console.log();\n}\n\nexport function banner(text: string): void {\n console.log(chalk.bold.cyan(text));\n}\n","import { directoryExists } from './file.js';\n\nconst VALID_PROJECT_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/;\n\nexport function validateProjectName(name: string): string | true {\n if (!name || name.trim().length === 0) {\n return 'Project name is required.';\n }\n\n if (!VALID_PROJECT_NAME.test(name)) {\n return 'Project name must be a valid npm package name (lowercase, no spaces, can use hyphens and dots).';\n }\n\n if (name.length > 214) {\n return 'Project name must be less than 214 characters.';\n }\n\n return true;\n}\n\nexport async function validateDirectory(dirPath: string): Promise<string | true> {\n if (await directoryExists(dirPath)) {\n return `Directory \"${dirPath}\" already exists. Use --force to overwrite.`;\n }\n return true;\n}\n\nexport function checkNodeVersion(minMajor: number = 20): string | true {\n const version = process.version;\n const major = parseInt(version.slice(1).split('.')[0], 10);\n\n if (major < minMajor) {\n return `Node.js ${minMajor}+ is required. You are using ${version}.`;\n }\n\n return true;\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class NextjsGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring Next.js + App Router + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring Next.js + App Router + TypeScript');\n\n if (this.config.features.testing) {\n success('Configuring Vitest');\n }\n }\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class AngularGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring Angular 18 + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring Angular 18 + TypeScript');\n }\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class ExpressGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring Express 5 + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring Express 5 + TypeScript');\n\n if (this.config.features.testing) {\n success('Configuring Vitest');\n }\n }\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class FastAPIGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring FastAPI + Pydantic');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring FastAPI + Pydantic');\n\n if (this.config.features.testing) {\n success('Configuring Pytest');\n }\n }\n\n protected override async applySharedConfigs(): Promise<void> {\n // Python projects don't use ESLint/Prettier/tsconfig\n const path = await import('node:path');\n const sharedDir = path.join(this.templatesDir, 'shared');\n\n // Still apply gitignore, readme, license\n await this.renderTemplateFile(\n path.join(sharedDir, 'gitignore.hbs'),\n path.join(this.targetDir, '.gitignore'),\n );\n\n const s = spinner('Generating README.md');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'readme.md.hbs'),\n path.join(this.targetDir, 'README.md'),\n );\n s.stop();\n success('Generating README.md');\n\n await this.renderTemplateFile(\n path.join(sharedDir, 'license.hbs'),\n path.join(this.targetDir, 'LICENSE'),\n );\n }\n}\n","import path from 'node:path';\nimport { BaseGenerator } from './base.js';\nimport { ReactGenerator } from './react.js';\nimport { NextjsGenerator } from './nextjs.js';\nimport { AngularGenerator } from './angular.js';\nimport { ExpressGenerator } from './express.js';\nimport { spinner, success, createDir } from '../utils/index.js';\nimport { renderAndWrite, getTemplatesDir } from '../utils/file.js';\nimport type { ProjectConfig } from '../types/index.js';\n\nexport class FullstackGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const s = spinner('Setting up full-stack monorepo');\n s.start();\n\n // Create apps directories\n const webDir = path.join(this.targetDir, 'apps', 'web');\n const apiDir = path.join(this.targetDir, 'apps', 'api');\n await createDir(webDir);\n await createDir(apiDir);\n\n s.stop();\n success('Setting up full-stack monorepo');\n\n // Generate frontend in apps/web\n const frontendConfig: ProjectConfig = {\n ...this.config,\n type: 'frontend',\n gitInit: false,\n };\n\n const FrontendGenerator = this.getFrontendGeneratorClass();\n const frontendGen = new FrontendGenerator(frontendConfig, webDir);\n await frontendGen.generateBase();\n\n // Generate backend in apps/api\n const backendConfig: ProjectConfig = {\n ...this.config,\n type: 'backend',\n framework: 'express',\n gitInit: false,\n };\n\n const backendGen = new ExpressGenerator(backendConfig, apiDir);\n await backendGen.generateBase();\n\n // Write root package.json for workspaces\n const rootPkgPath = path.join(this.targetDir, 'package.json');\n const rootPkg = {\n name: this.config.name,\n private: true,\n workspaces: ['apps/*'],\n scripts: {\n dev: 'npm run --workspaces dev',\n build: 'npm run --workspaces build',\n lint: 'npm run --workspaces lint',\n test: 'npm run --workspaces test',\n },\n };\n const { writeFile } = await import('../utils/file.js');\n await writeFile(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\\n');\n }\n\n private getFrontendGeneratorClass() {\n switch (this.config.framework) {\n case 'nextjs':\n return NextjsGenerator;\n case 'angular':\n return AngularGenerator;\n case 'react':\n default:\n return ReactGenerator;\n }\n }\n}\n","import type { ProjectConfig } from '../types/index.js';\nimport { BaseGenerator } from './base.js';\nimport { ReactGenerator } from './react.js';\nimport { NextjsGenerator } from './nextjs.js';\nimport { AngularGenerator } from './angular.js';\nimport { ExpressGenerator } from './express.js';\nimport { FastAPIGenerator } from './fastapi.js';\nimport { FullstackGenerator } from './fullstack.js';\n\nexport function createGenerator(config: ProjectConfig, targetDir: string): BaseGenerator {\n if (config.type === 'fullstack') {\n return new FullstackGenerator(config, targetDir);\n }\n\n switch (config.framework) {\n case 'react':\n return new ReactGenerator(config, targetDir);\n case 'nextjs':\n return new NextjsGenerator(config, targetDir);\n case 'angular':\n return new AngularGenerator(config, targetDir);\n case 'express':\n return new ExpressGenerator(config, targetDir);\n case 'fastapi':\n return new FastAPIGenerator(config, targetDir);\n default:\n throw new Error(`Unknown framework: ${config.framework}`);\n }\n}\n\nexport { BaseGenerator } from './base.js';\n","export type ProjectType = 'frontend' | 'backend' | 'fullstack';\n\nexport type FrameworkType = 'react' | 'nextjs' | 'angular' | 'express' | 'fastapi';\n\nexport type VariantType =\n | 'base'\n | 'with-tailwind'\n | 'with-shadcn'\n | 'with-auth'\n | 'with-prisma';\n\nexport type PackageManager = 'npm' | 'yarn' | 'pnpm';\n\nexport interface FeatureFlags {\n typescript: boolean;\n eslint: boolean;\n prettier: boolean;\n tailwind: boolean;\n shadcn: boolean;\n testing: boolean;\n githubActions: boolean;\n docker: boolean;\n husky: boolean;\n envExample: boolean;\n}\n\nexport interface ProjectConfig {\n name: string;\n type: ProjectType;\n framework: FrameworkType;\n variant: VariantType;\n features: FeatureFlags;\n packageManager: PackageManager;\n gitInit: boolean;\n}\n\nexport interface FrameworkMeta {\n name: string;\n displayName: string;\n description: string;\n type: ProjectType;\n variants: VariantType[];\n language: 'typescript' | 'python';\n}\n\nexport const DEFAULT_FEATURES: FeatureFlags = {\n typescript: true,\n eslint: true,\n prettier: true,\n tailwind: false,\n shadcn: false,\n testing: true,\n githubActions: false,\n docker: false,\n husky: false,\n envExample: false,\n};\n\nexport const DEFAULT_CONFIG: ProjectConfig = {\n name: '',\n type: 'frontend',\n framework: 'react',\n variant: 'base',\n features: { ...DEFAULT_FEATURES },\n packageManager: 'npm',\n gitInit: true,\n};\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { FRAMEWORKS, VARIANT_DISPLAY_NAMES } from '../constants.js';\nimport type { FrameworkType } from '../types/index.js';\nimport { newLine } from '../utils/logger.js';\n\nexport function listCommand(): Command {\n return new Command('list')\n .description('List all available templates')\n .action(() => {\n newLine();\n console.log(chalk.bold.cyan('Available Templates'));\n console.log(chalk.gray('─'.repeat(60)));\n newLine();\n\n const categories = {\n Frontend: ['react', 'nextjs', 'angular'] as FrameworkType[],\n Backend: ['express', 'fastapi'] as FrameworkType[],\n };\n\n for (const [category, frameworks] of Object.entries(categories)) {\n console.log(chalk.bold.white(` ${category}`));\n newLine();\n\n for (const fw of frameworks) {\n const meta = FRAMEWORKS[fw];\n console.log(` ${chalk.green(meta.displayName.padEnd(25))} ${chalk.gray(meta.description)}`);\n\n for (const variant of meta.variants) {\n const label = VARIANT_DISPLAY_NAMES[variant];\n console.log(` ${chalk.gray('•')} ${label}`);\n }\n newLine();\n }\n }\n\n console.log(chalk.gray('─'.repeat(60)));\n console.log(\n chalk.gray(' Use'),\n chalk.cyan('scaffold create <name> --<framework>'),\n chalk.gray('for quick setup'),\n );\n newLine();\n });\n}\n","import { Command } from 'commander';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\nimport { newLine } from '../utils/logger.js';\n\ninterface Check {\n name: string;\n command: string;\n versionFlag?: string;\n required: boolean;\n}\n\nconst CHECKS: Check[] = [\n { name: 'Node.js', command: 'node', versionFlag: '--version', required: true },\n { name: 'npm', command: 'npm', versionFlag: '--version', required: false },\n { name: 'yarn', command: 'yarn', versionFlag: '--version', required: false },\n { name: 'pnpm', command: 'pnpm', versionFlag: '--version', required: false },\n { name: 'git', command: 'git', versionFlag: '--version', required: true },\n { name: 'Python', command: 'python3', versionFlag: '--version', required: false },\n];\n\nexport function doctorCommand(): Command {\n return new Command('doctor')\n .description('Check system dependencies')\n .action(() => {\n newLine();\n console.log(chalk.bold.cyan('System Check'));\n console.log(chalk.gray('─'.repeat(40)));\n newLine();\n\n let allGood = true;\n\n for (const check of CHECKS) {\n try {\n const version = execSync(`${check.command} ${check.versionFlag ?? '--version'}`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const versionStr = version.split('\\n')[0];\n console.log(` ${chalk.green('✔')} ${check.name.padEnd(12)} ${chalk.gray(versionStr)}`);\n } catch {\n if (check.required) {\n console.log(` ${chalk.red('✖')} ${check.name.padEnd(12)} ${chalk.red('not found (required)')}`);\n allGood = false;\n } else {\n console.log(` ${chalk.yellow('–')} ${check.name.padEnd(12)} ${chalk.gray('not found (optional)')}`);\n }\n }\n }\n\n newLine();\n\n if (allGood) {\n console.log(chalk.green(' All required dependencies are installed!'));\n } else {\n console.log(chalk.red(' Some required dependencies are missing.'));\n }\n\n newLine();\n });\n}\n","import { createCli } from './cli.js';\n\nconst program = createCli();\nprogram.parse();\n"],"mappings":"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","names":["Command","path","fs","framework","inquirer","inquirer","inquirer","path","path","path","path","path","writeFile","path","fs","Command","chalk","Command","chalk","Command","execSync","chalk","Command","chalk","execSync","program","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/create.ts","../src/prompts/framework.ts","../src/prompts/features.ts","../src/prompts/config.ts","../src/constants.ts","../src/prompts/index.ts","../src/generators/react.ts","../src/generators/base.ts","../src/utils/logger.ts","../src/utils/file.ts","../src/utils/validator.ts","../src/generators/nextjs.ts","../src/generators/angular.ts","../src/generators/express.ts","../src/generators/fastapi.ts","../src/generators/fullstack.ts","../src/generators/index.ts","../src/types/config.ts","../src/commands/list.ts","../src/commands/doctor.ts","../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { createCommand } from './commands/create.js';\nimport { listCommand } from './commands/list.js';\nimport { doctorCommand } from './commands/doctor.js';\nimport { setVerbose } from './utils/logger.js';\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('scaffold')\n .description('CLI tool for scaffolding modern full-stack projects')\n .version('0.1.1')\n .option('--verbose', 'Enable verbose output')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts.verbose) {\n setVerbose(true);\n }\n });\n\n program.addCommand(createCommand());\n program.addCommand(listCommand());\n program.addCommand(doctorCommand());\n\n return program;\n}\n","import { Command } from 'commander';\nimport path from 'node:path';\nimport fs from 'fs-extra';\nimport { runPrompts } from '../prompts/index.js';\nimport { createGenerator } from '../generators/index.js';\nimport {\n validateProjectName,\n validateDirectory,\n success,\n error,\n newLine,\n banner,\n} from '../utils/index.js';\nimport type { ProjectConfig } from '../types/index.js';\nimport { DEFAULT_CONFIG } from '../types/index.js';\n\ninterface CreateOptions {\n react?: boolean;\n nextjs?: boolean;\n angular?: boolean;\n express?: boolean;\n fastapi?: boolean;\n yes?: boolean;\n from?: string;\n force?: boolean;\n}\n\nexport function createCommand(): Command {\n const cmd = new Command('create')\n .description('Create a new project')\n .argument('<project-name>', 'Name of the project')\n .option('--react', 'Create a React + Vite project')\n .option('--nextjs', 'Create a Next.js project')\n .option('--angular', 'Create an Angular project')\n .option('--express', 'Create an Express.js project')\n .option('--fastapi', 'Create a FastAPI project')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .option('--from <path>', 'Create from a config file')\n .option('--force', 'Overwrite existing directory')\n .action(async (projectName: string, options: CreateOptions) => {\n try {\n await handleCreate(projectName, options);\n } catch (err) {\n error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nasync function handleCreate(projectName: string, options: CreateOptions): Promise<void> {\n const nameValidation = validateProjectName(projectName);\n if (nameValidation !== true) {\n throw new Error(nameValidation);\n }\n\n const targetDir = path.resolve(process.cwd(), projectName);\n\n if (!options.force) {\n const dirValidation = await validateDirectory(targetDir);\n if (dirValidation !== true) {\n throw new Error(dirValidation);\n }\n }\n\n let config: ProjectConfig;\n\n if (options.from) {\n config = await loadConfigFromFile(options.from, projectName);\n } else if (options.yes || hasFrameworkFlag(options)) {\n config = buildQuickConfig(projectName, options);\n } else {\n config = await runPrompts(projectName);\n }\n\n newLine();\n banner(`Creating project \"${config.name}\"...`);\n newLine();\n\n const generator = createGenerator(config, targetDir);\n await generator.generate();\n\n newLine();\n success(`Project \"${config.name}\" created successfully! 🎉`);\n newLine();\n console.log('Next steps:');\n console.log(` cd ${config.name}`);\n\n if (config.framework === 'fastapi') {\n console.log(' pip install -r requirements.txt');\n console.log(' uvicorn app.main:app --reload');\n } else {\n console.log(` ${config.packageManager} install`);\n console.log(` ${config.packageManager} run dev`);\n }\n\n newLine();\n console.log('Happy coding! 🚀');\n newLine();\n}\n\nfunction hasFrameworkFlag(options: CreateOptions): boolean {\n return !!(\n options.react ||\n options.nextjs ||\n options.angular ||\n options.express ||\n options.fastapi\n );\n}\n\nfunction buildQuickConfig(projectName: string, options: CreateOptions): ProjectConfig {\n const config: ProjectConfig = { ...DEFAULT_CONFIG, name: projectName };\n\n if (options.react) {\n config.type = 'frontend';\n config.framework = 'react';\n } else if (options.nextjs) {\n config.type = 'frontend';\n config.framework = 'nextjs';\n } else if (options.angular) {\n config.type = 'frontend';\n config.framework = 'angular';\n } else if (options.express) {\n config.type = 'backend';\n config.framework = 'express';\n } else if (options.fastapi) {\n config.type = 'backend';\n config.framework = 'fastapi';\n }\n\n return config;\n}\n\nasync function loadConfigFromFile(configPath: string, projectName: string): Promise<ProjectConfig> {\n const resolvedPath = path.resolve(process.cwd(), configPath);\n const content = await fs.readJSON(resolvedPath);\n return { ...DEFAULT_CONFIG, ...content, name: projectName };\n}\n","import inquirer from 'inquirer';\nimport type { ProjectType, FrameworkType } from '../types/index.js';\n\nexport async function promptProjectType(): Promise<ProjectType> {\n const { projectType } = await inquirer.prompt([\n {\n type: 'list',\n name: 'projectType',\n message: 'What type of project?',\n choices: [\n { name: 'Frontend (React, Next.js, Angular)', value: 'frontend' },\n { name: 'Backend (Express, FastAPI)', value: 'backend' },\n { name: 'Full-Stack (Frontend + Backend)', value: 'fullstack' },\n ],\n },\n ]);\n\n return projectType;\n}\n\nexport async function promptFramework(projectType: ProjectType): Promise<FrameworkType> {\n if (projectType === 'frontend') {\n const { framework } = await inquirer.prompt([\n {\n type: 'list',\n name: 'framework',\n message: 'Which frontend framework?',\n choices: [\n { name: 'React + Vite', value: 'react' },\n { name: 'Next.js (App Router)', value: 'nextjs' },\n { name: 'Angular 18', value: 'angular' },\n ],\n },\n ]);\n return framework;\n }\n\n if (projectType === 'backend') {\n const { framework } = await inquirer.prompt([\n {\n type: 'list',\n name: 'framework',\n message: 'Which backend framework?',\n choices: [\n { name: 'Express.js', value: 'express' },\n { name: 'FastAPI (Python)', value: 'fastapi' },\n ],\n },\n ]);\n return framework;\n }\n\n // fullstack — prompt for frontend (we'll use express as the backend)\n const { framework } = await inquirer.prompt([\n {\n type: 'list',\n name: 'framework',\n message: 'Which frontend framework?',\n choices: [\n { name: 'React + Vite', value: 'react' },\n { name: 'Next.js', value: 'nextjs' },\n { name: 'Angular 18', value: 'angular' },\n ],\n },\n ]);\n return framework;\n}\n","import inquirer from 'inquirer';\nimport type { FeatureFlags, ProjectType, FrameworkType } from '../types/index.js';\n\ninterface FeatureChoice {\n name: string;\n value: keyof FeatureFlags;\n checked: boolean;\n disabled?: string | false;\n}\n\nexport async function promptFeatures(\n projectType: ProjectType,\n framework: FrameworkType,\n): Promise<FeatureFlags> {\n const isPython = framework === 'fastapi';\n\n const choices: FeatureChoice[] = [\n {\n name: 'TypeScript',\n value: 'typescript',\n checked: true,\n disabled: isPython ? 'N/A for Python' : false,\n },\n {\n name: 'ESLint + Prettier',\n value: 'eslint',\n checked: !isPython,\n disabled: isPython ? 'N/A for Python' : false,\n },\n { name: 'Tailwind CSS', value: 'tailwind', checked: false },\n { name: 'shadcn/ui components', value: 'shadcn', checked: false },\n { name: isPython ? 'Pytest testing' : 'Vitest testing', value: 'testing', checked: true },\n { name: 'GitHub Actions CI/CD', value: 'githubActions', checked: false },\n { name: 'Docker setup', value: 'docker', checked: false },\n {\n name: 'Husky pre-commit hooks',\n value: 'husky',\n checked: false,\n disabled: isPython ? 'N/A for Python' : false,\n },\n { name: '.env example file', value: 'envExample', checked: false },\n ];\n\n // Filter out frontend-only features for backend projects\n const filteredChoices =\n projectType === 'backend'\n ? choices.filter((c) => !['tailwind', 'shadcn'].includes(c.value))\n : choices;\n\n const { features } = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'features',\n message: 'Select features:',\n choices: filteredChoices,\n },\n ]);\n\n const selectedFeatures = features as (keyof FeatureFlags)[];\n\n return {\n typescript: !isPython && selectedFeatures.includes('typescript'),\n eslint: !isPython && selectedFeatures.includes('eslint'),\n prettier: !isPython && selectedFeatures.includes('eslint'),\n tailwind: selectedFeatures.includes('tailwind'),\n shadcn: selectedFeatures.includes('shadcn'),\n testing: selectedFeatures.includes('testing'),\n githubActions: selectedFeatures.includes('githubActions'),\n docker: selectedFeatures.includes('docker'),\n husky: !isPython && selectedFeatures.includes('husky'),\n envExample: selectedFeatures.includes('envExample'),\n };\n}\n","import inquirer from 'inquirer';\nimport type { VariantType, PackageManager, FrameworkType } from '../types/index.js';\nimport { FRAMEWORKS, VARIANT_DISPLAY_NAMES } from '../constants.js';\n\nexport async function promptVariant(framework: FrameworkType): Promise<VariantType> {\n const meta = FRAMEWORKS[framework];\n\n if (meta.variants.length <= 1) {\n return 'base';\n }\n\n const { variant } = await inquirer.prompt([\n {\n type: 'list',\n name: 'variant',\n message: 'Select a template variant:',\n choices: meta.variants.map((v) => ({\n name: VARIANT_DISPLAY_NAMES[v],\n value: v,\n })),\n },\n ]);\n\n return variant;\n}\n\nexport async function promptPackageManager(): Promise<PackageManager> {\n const { packageManager } = await inquirer.prompt([\n {\n type: 'list',\n name: 'packageManager',\n message: 'Package manager?',\n choices: [\n { name: 'npm', value: 'npm' },\n { name: 'yarn', value: 'yarn' },\n { name: 'pnpm', value: 'pnpm' },\n ],\n },\n ]);\n\n return packageManager;\n}\n\nexport async function promptGitInit(): Promise<boolean> {\n const { gitInit } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'gitInit',\n message: 'Initialize git repository?',\n default: true,\n },\n ]);\n\n return gitInit;\n}\n","import type { FrameworkMeta, FrameworkType, VariantType } from './types/index.js';\n\nexport const FRAMEWORKS: Record<FrameworkType, FrameworkMeta> = {\n react: {\n name: 'react',\n displayName: 'React + Vite',\n description: 'React 19 with Vite and TypeScript',\n type: 'frontend',\n variants: ['base', 'with-tailwind', 'with-shadcn'],\n language: 'typescript',\n },\n nextjs: {\n name: 'nextjs',\n displayName: 'Next.js (App Router)',\n description: 'Next.js 15 with App Router and TypeScript',\n type: 'frontend',\n variants: ['base', 'with-auth', 'with-prisma'],\n language: 'typescript',\n },\n angular: {\n name: 'angular',\n displayName: 'Angular 18',\n description: 'Angular 18 standalone with TypeScript',\n type: 'frontend',\n variants: ['base'],\n language: 'typescript',\n },\n express: {\n name: 'express',\n displayName: 'Express.js',\n description: 'Express 5 with TypeScript',\n type: 'backend',\n variants: ['base', 'with-prisma'],\n language: 'typescript',\n },\n fastapi: {\n name: 'fastapi',\n displayName: 'FastAPI',\n description: 'FastAPI with Pydantic and Python',\n type: 'backend',\n variants: ['base'],\n language: 'python',\n },\n};\n\nexport const FRONTEND_FRAMEWORKS: FrameworkType[] = ['react', 'nextjs', 'angular'];\nexport const BACKEND_FRAMEWORKS: FrameworkType[] = ['express', 'fastapi'];\n\nexport const VARIANT_DISPLAY_NAMES: Record<VariantType, string> = {\n base: 'Base (minimal setup)',\n 'with-tailwind': 'With Tailwind CSS',\n 'with-shadcn': 'With shadcn/ui',\n 'with-auth': 'With NextAuth.js',\n 'with-prisma': 'With Prisma ORM',\n};\n","import type { ProjectConfig } from '../types/index.js';\nimport { promptProjectType, promptFramework } from './framework.js';\nimport { promptFeatures } from './features.js';\nimport { promptVariant, promptPackageManager, promptGitInit } from './config.js';\n\nexport async function runPrompts(projectName: string): Promise<ProjectConfig> {\n const type = await promptProjectType();\n const framework = await promptFramework(type);\n const variant = await promptVariant(framework);\n const features = await promptFeatures(type, framework);\n\n const isPython = framework === 'fastapi';\n const packageManager = isPython ? 'npm' : await promptPackageManager();\n const gitInit = await promptGitInit();\n\n return {\n name: projectName,\n type,\n framework,\n variant,\n features,\n packageManager,\n gitInit,\n };\n}\n","import path from 'node:path';\nimport { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class ReactGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring React + Vite + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring React + Vite + TypeScript');\n\n if (this.config.features.testing) {\n success('Configuring Vitest');\n }\n }\n\n private getVariantPath(variant: string): string {\n return path.join(this.templatesDir, 'react', variant);\n }\n}\n","import path from 'node:path';\nimport fs from 'fs-extra';\nimport { execSync } from 'node:child_process';\nimport {\n createDir,\n renderAndWrite,\n getTemplatesDir,\n spinner,\n success,\n debug,\n} from '../utils/index.js';\nimport type { ProjectConfig } from '../types/index.js';\nimport type { GeneratorResult } from '../types/generator.js';\n\nexport abstract class BaseGenerator {\n protected config: ProjectConfig;\n protected targetDir: string;\n protected templatesDir: string;\n protected result: GeneratorResult = { filesCreated: [], warnings: [] };\n\n constructor(config: ProjectConfig, targetDir: string) {\n this.config = config;\n this.targetDir = targetDir;\n this.templatesDir = getTemplatesDir();\n }\n\n async generate(): Promise<GeneratorResult> {\n await this.createProjectDir();\n await this.generateBase();\n await this.applyVariant();\n await this.applySharedConfigs();\n await this.applyFeatures();\n await this.initGit();\n return this.result;\n }\n\n protected abstract generateBase(): Promise<void>;\n\n protected async applyVariant(): Promise<void> {\n if (this.config.variant === 'base') return;\n\n const variantDir = this.getVariantTemplateDir();\n if (await fs.pathExists(variantDir)) {\n const s = spinner(`Applying ${this.config.variant} variant`);\n s.start();\n await this.renderDirectory(variantDir, this.targetDir);\n s.stop();\n success(`Applied ${this.config.variant} variant`);\n }\n }\n\n protected async applySharedConfigs(): Promise<void> {\n const sharedDir = path.join(this.templatesDir, 'shared');\n\n if (this.config.features.eslint) {\n const s = spinner('Setting up ESLint + Prettier');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'eslint.config.js.hbs'),\n path.join(this.targetDir, 'eslint.config.js'),\n );\n await this.renderTemplateFile(\n path.join(sharedDir, 'prettier.config.js.hbs'),\n path.join(this.targetDir, '.prettierrc'),\n );\n s.stop();\n success('Setting up ESLint + Prettier');\n }\n\n // Gitignore\n await this.renderTemplateFile(\n path.join(sharedDir, 'gitignore.hbs'),\n path.join(this.targetDir, '.gitignore'),\n );\n\n // README\n const s2 = spinner('Generating README.md');\n s2.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'readme.md.hbs'),\n path.join(this.targetDir, 'README.md'),\n );\n s2.stop();\n success('Generating README.md');\n\n // License\n await this.renderTemplateFile(\n path.join(sharedDir, 'license.hbs'),\n path.join(this.targetDir, 'LICENSE'),\n );\n }\n\n protected async applyFeatures(): Promise<void> {\n const sharedDir = path.join(this.templatesDir, 'shared');\n\n if (this.config.features.githubActions) {\n const s = spinner('Creating GitHub Actions workflow');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'github-ci.yml.hbs'),\n path.join(this.targetDir, '.github', 'workflows', 'ci.yml'),\n );\n s.stop();\n success('Creating GitHub Actions workflow');\n }\n\n if (this.config.features.docker) {\n const s = spinner('Adding Docker configuration');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'docker', 'Dockerfile.hbs'),\n path.join(this.targetDir, 'Dockerfile'),\n );\n await this.renderTemplateFile(\n path.join(sharedDir, 'docker', 'docker-compose.yml.hbs'),\n path.join(this.targetDir, 'docker-compose.yml'),\n );\n s.stop();\n success('Adding Docker configuration');\n }\n }\n\n protected async createProjectDir(): Promise<void> {\n const s = spinner('Creating project structure');\n s.start();\n await createDir(this.targetDir);\n s.stop();\n success('Creating project structure');\n }\n\n protected async initGit(): Promise<void> {\n if (!this.config.gitInit) return;\n\n const s = spinner('Initializing git repository');\n s.start();\n try {\n execSync('git init', { cwd: this.targetDir, stdio: 'pipe' });\n execSync('git add -A', { cwd: this.targetDir, stdio: 'pipe' });\n execSync('git commit -m \"Initial commit from scaffold\"', {\n cwd: this.targetDir,\n stdio: 'pipe',\n });\n s.stop();\n success('Initializing git repository');\n } catch {\n s.stop();\n debug('Git initialization skipped (git not available)');\n }\n }\n\n protected async renderTemplateFile(templatePath: string, outputPath: string): Promise<void> {\n const templateData = this.getTemplateData();\n\n if (await fs.pathExists(templatePath)) {\n await renderAndWrite(templatePath, outputPath, templateData);\n this.result.filesCreated.push(outputPath);\n } else {\n debug(`Template not found: ${templatePath}`);\n }\n }\n\n protected async renderDirectory(srcDir: string, destDir: string): Promise<void> {\n const entries = await fs.readdir(srcDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(srcDir, entry.name);\n const destName = entry.name.replace(/\\.hbs$/, '');\n const destPath = path.join(destDir, destName);\n\n if (entry.isDirectory()) {\n await this.renderDirectory(srcPath, destPath);\n } else if (entry.name.endsWith('.hbs')) {\n await this.renderTemplateFile(srcPath, destPath);\n } else {\n await fs.ensureDir(path.dirname(destPath));\n await fs.copy(srcPath, destPath);\n this.result.filesCreated.push(destPath);\n }\n }\n }\n\n protected getTemplateData(): object {\n return {\n ...this.config,\n year: new Date().getFullYear(),\n };\n }\n\n protected getBaseTemplateDir(): string {\n return path.join(this.templatesDir, this.config.framework, 'base');\n }\n\n protected getVariantTemplateDir(): string {\n return path.join(this.templatesDir, this.config.framework, this.config.variant);\n }\n}\n","import chalk from 'chalk';\nimport ora, { type Ora } from 'ora';\n\nlet verbose = false;\n\nexport function setVerbose(value: boolean): void {\n verbose = value;\n}\n\nexport function info(message: string): void {\n console.log(chalk.blue('ℹ'), message);\n}\n\nexport function success(message: string): void {\n console.log(chalk.green('✔'), message);\n}\n\nexport function warn(message: string): void {\n console.log(chalk.yellow('⚠'), message);\n}\n\nexport function error(message: string): void {\n console.log(chalk.red('✖'), message);\n}\n\nexport function debug(message: string): void {\n if (verbose) {\n console.log(chalk.gray('⬥'), chalk.gray(message));\n }\n}\n\nexport function spinner(text: string): Ora {\n return ora({ text, color: 'cyan' });\n}\n\nexport function newLine(): void {\n console.log();\n}\n\nexport function banner(text: string): void {\n console.log(chalk.bold.cyan(text));\n}\n","import path from 'node:path';\nimport fs from 'fs-extra';\nimport Handlebars from 'handlebars';\nimport { fileURLToPath } from 'node:url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Register custom Handlebars helpers\nHandlebars.registerHelper('eq', (a: unknown, b: unknown) => a === b);\nHandlebars.registerHelper('neq', (a: unknown, b: unknown) => a !== b);\nHandlebars.registerHelper('or', (a: unknown, b: unknown) => a || b);\nHandlebars.registerHelper('and', (a: unknown, b: unknown) => a && b);\nHandlebars.registerHelper('lowercase', (str: string) => str?.toLowerCase());\nHandlebars.registerHelper('year', () => new Date().getFullYear());\nHandlebars.registerHelper('join', (arr: string[], sep: string) =>\n Array.isArray(arr) ? arr.join(typeof sep === 'string' ? sep : ', ') : '',\n);\n\nexport function getTemplatesDir(): string {\n // When bundled with tsup, __dirname is dist/ → go up 1 level to project root\n // When running unbundled (vitest), __dirname is src/utils/ → go up 2 levels to project root\n // In both cases, templates live at <projectRoot>/src/templates\n const isBundled =\n __dirname.replace(/\\\\/g, '/').endsWith('/dist') ||\n __dirname.replace(/\\\\/g, '/').includes('/dist/');\n const projectRoot = isBundled\n ? path.resolve(__dirname, '..')\n : path.resolve(__dirname, '..', '..');\n return path.resolve(projectRoot, 'src', 'templates');\n}\n\nexport async function createDir(dirPath: string): Promise<void> {\n await fs.ensureDir(dirPath);\n}\n\nexport async function writeFile(filePath: string, content: string): Promise<void> {\n await fs.ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content, 'utf-8');\n}\n\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await fs.ensureDir(path.dirname(dest));\n await fs.copy(src, dest);\n}\n\nexport async function renderTemplate(templatePath: string, data: object): Promise<string> {\n const templateContent = await fs.readFile(templatePath, 'utf-8');\n const template = Handlebars.compile(templateContent, { noEscape: true });\n return template(data);\n}\n\nexport async function renderAndWrite(\n templatePath: string,\n outputPath: string,\n data: object,\n): Promise<void> {\n const content = await renderTemplate(templatePath, data);\n await writeFile(outputPath, content);\n}\n\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isFile();\n } catch {\n return false;\n }\n}\n\nexport function getOutputFileName(templateName: string): string {\n return templateName.replace(/\\.hbs$/, '');\n}\n","import { directoryExists } from './file.js';\n\nconst VALID_PROJECT_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/;\n\nexport function validateProjectName(name: string): string | true {\n if (!name || name.trim().length === 0) {\n return 'Project name is required.';\n }\n\n if (!VALID_PROJECT_NAME.test(name)) {\n return 'Project name must be a valid npm package name (lowercase, no spaces, can use hyphens and dots).';\n }\n\n if (name.length > 214) {\n return 'Project name must be less than 214 characters.';\n }\n\n return true;\n}\n\nexport async function validateDirectory(dirPath: string): Promise<string | true> {\n if (await directoryExists(dirPath)) {\n return `Directory \"${dirPath}\" already exists. Use --force to overwrite.`;\n }\n return true;\n}\n\nexport function checkNodeVersion(minMajor: number = 20): string | true {\n const version = process.version;\n const major = parseInt(version.slice(1).split('.')[0], 10);\n\n if (major < minMajor) {\n return `Node.js ${minMajor}+ is required. You are using ${version}.`;\n }\n\n return true;\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class NextjsGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring Next.js + App Router + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring Next.js + App Router + TypeScript');\n\n if (this.config.features.testing) {\n success('Configuring Vitest');\n }\n }\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class AngularGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring Angular 18 + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring Angular 18 + TypeScript');\n }\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class ExpressGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring Express 5 + TypeScript');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring Express 5 + TypeScript');\n\n if (this.config.features.testing) {\n success('Configuring Vitest');\n }\n }\n}\n","import { BaseGenerator } from './base.js';\nimport { spinner, success } from '../utils/index.js';\n\nexport class FastAPIGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const baseDir = this.getBaseTemplateDir();\n\n const s = spinner('Configuring FastAPI + Pydantic');\n s.start();\n await this.renderDirectory(baseDir, this.targetDir);\n s.stop();\n success('Configuring FastAPI + Pydantic');\n\n if (this.config.features.testing) {\n success('Configuring Pytest');\n }\n }\n\n protected override async applySharedConfigs(): Promise<void> {\n // Python projects don't use ESLint/Prettier/tsconfig\n const path = await import('node:path');\n const sharedDir = path.join(this.templatesDir, 'shared');\n\n // Still apply gitignore, readme, license\n await this.renderTemplateFile(\n path.join(sharedDir, 'gitignore.hbs'),\n path.join(this.targetDir, '.gitignore'),\n );\n\n const s = spinner('Generating README.md');\n s.start();\n await this.renderTemplateFile(\n path.join(sharedDir, 'readme.md.hbs'),\n path.join(this.targetDir, 'README.md'),\n );\n s.stop();\n success('Generating README.md');\n\n await this.renderTemplateFile(\n path.join(sharedDir, 'license.hbs'),\n path.join(this.targetDir, 'LICENSE'),\n );\n }\n}\n","import path from 'node:path';\nimport { BaseGenerator } from './base.js';\nimport { ReactGenerator } from './react.js';\nimport { NextjsGenerator } from './nextjs.js';\nimport { AngularGenerator } from './angular.js';\nimport { ExpressGenerator } from './express.js';\nimport { spinner, success, createDir, writeFile } from '../utils/index.js';\nimport type { ProjectConfig } from '../types/index.js';\n\nexport class FullstackGenerator extends BaseGenerator {\n protected async generateBase(): Promise<void> {\n const s = spinner('Setting up full-stack monorepo');\n s.start();\n\n // Create apps directories\n const webDir = path.join(this.targetDir, 'apps', 'web');\n const apiDir = path.join(this.targetDir, 'apps', 'api');\n await createDir(webDir);\n await createDir(apiDir);\n\n s.stop();\n success('Setting up full-stack monorepo');\n\n // Generate frontend in apps/web\n const frontendConfig: ProjectConfig = {\n ...this.config,\n type: 'frontend',\n gitInit: false,\n };\n\n const frontendGen = this.createFrontendGenerator(frontendConfig, webDir);\n await frontendGen.generate();\n\n // Generate backend in apps/api\n const backendConfig: ProjectConfig = {\n ...this.config,\n type: 'backend',\n framework: 'express',\n gitInit: false,\n };\n\n const backendGen = new ExpressGenerator(backendConfig, apiDir);\n await backendGen.generate();\n\n // Write root package.json for workspaces\n const rootPkgPath = path.join(this.targetDir, 'package.json');\n const rootPkg = {\n name: this.config.name,\n private: true,\n workspaces: ['apps/*'],\n scripts: {\n dev: 'npm run --workspaces dev',\n build: 'npm run --workspaces build',\n lint: 'npm run --workspaces lint',\n test: 'npm run --workspaces test',\n },\n };\n await writeFile(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\\n');\n }\n\n private createFrontendGenerator(config: ProjectConfig, targetDir: string): BaseGenerator {\n switch (config.framework) {\n case 'nextjs':\n return new NextjsGenerator(config, targetDir);\n case 'angular':\n return new AngularGenerator(config, targetDir);\n case 'react':\n default:\n return new ReactGenerator(config, targetDir);\n }\n }\n}\n","import type { ProjectConfig } from '../types/index.js';\nimport { BaseGenerator } from './base.js';\nimport { ReactGenerator } from './react.js';\nimport { NextjsGenerator } from './nextjs.js';\nimport { AngularGenerator } from './angular.js';\nimport { ExpressGenerator } from './express.js';\nimport { FastAPIGenerator } from './fastapi.js';\nimport { FullstackGenerator } from './fullstack.js';\n\nexport function createGenerator(config: ProjectConfig, targetDir: string): BaseGenerator {\n if (config.type === 'fullstack') {\n return new FullstackGenerator(config, targetDir);\n }\n\n switch (config.framework) {\n case 'react':\n return new ReactGenerator(config, targetDir);\n case 'nextjs':\n return new NextjsGenerator(config, targetDir);\n case 'angular':\n return new AngularGenerator(config, targetDir);\n case 'express':\n return new ExpressGenerator(config, targetDir);\n case 'fastapi':\n return new FastAPIGenerator(config, targetDir);\n default:\n throw new Error(`Unknown framework: ${config.framework}`);\n }\n}\n\nexport { BaseGenerator } from './base.js';\n","export type ProjectType = 'frontend' | 'backend' | 'fullstack';\n\nexport type FrameworkType = 'react' | 'nextjs' | 'angular' | 'express' | 'fastapi';\n\nexport type VariantType = 'base' | 'with-tailwind' | 'with-shadcn' | 'with-auth' | 'with-prisma';\n\nexport type PackageManager = 'npm' | 'yarn' | 'pnpm';\n\nexport interface FeatureFlags {\n typescript: boolean;\n eslint: boolean;\n prettier: boolean;\n tailwind: boolean;\n shadcn: boolean;\n testing: boolean;\n githubActions: boolean;\n docker: boolean;\n husky: boolean;\n envExample: boolean;\n}\n\nexport interface ProjectConfig {\n name: string;\n type: ProjectType;\n framework: FrameworkType;\n variant: VariantType;\n features: FeatureFlags;\n packageManager: PackageManager;\n gitInit: boolean;\n}\n\nexport interface FrameworkMeta {\n name: string;\n displayName: string;\n description: string;\n type: ProjectType;\n variants: VariantType[];\n language: 'typescript' | 'python';\n}\n\nexport const DEFAULT_FEATURES: FeatureFlags = {\n typescript: true,\n eslint: true,\n prettier: true,\n tailwind: false,\n shadcn: false,\n testing: true,\n githubActions: false,\n docker: false,\n husky: false,\n envExample: false,\n};\n\nexport const DEFAULT_CONFIG: ProjectConfig = {\n name: '',\n type: 'frontend',\n framework: 'react',\n variant: 'base',\n features: { ...DEFAULT_FEATURES },\n packageManager: 'npm',\n gitInit: true,\n};\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { FRAMEWORKS, VARIANT_DISPLAY_NAMES } from '../constants.js';\nimport type { FrameworkType } from '../types/index.js';\nimport { newLine } from '../utils/logger.js';\n\nexport function listCommand(): Command {\n return new Command('list').description('List all available templates').action(() => {\n newLine();\n console.log(chalk.bold.cyan('Available Templates'));\n console.log(chalk.gray('─'.repeat(60)));\n newLine();\n\n const categories = {\n Frontend: ['react', 'nextjs', 'angular'] as FrameworkType[],\n Backend: ['express', 'fastapi'] as FrameworkType[],\n };\n\n for (const [category, frameworks] of Object.entries(categories)) {\n console.log(chalk.bold.white(` ${category}`));\n newLine();\n\n for (const fw of frameworks) {\n const meta = FRAMEWORKS[fw];\n console.log(\n ` ${chalk.green(meta.displayName.padEnd(25))} ${chalk.gray(meta.description)}`,\n );\n\n for (const variant of meta.variants) {\n const label = VARIANT_DISPLAY_NAMES[variant];\n console.log(` ${chalk.gray('•')} ${label}`);\n }\n newLine();\n }\n }\n\n console.log(chalk.gray('─'.repeat(60)));\n console.log(\n chalk.gray(' Use'),\n chalk.cyan('scaffold create <name> --<framework>'),\n chalk.gray('for quick setup'),\n );\n newLine();\n });\n}\n","import { Command } from 'commander';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\nimport { newLine } from '../utils/logger.js';\n\ninterface Check {\n name: string;\n command: string;\n versionFlag?: string;\n required: boolean;\n}\n\nconst CHECKS: Check[] = [\n { name: 'Node.js', command: 'node', versionFlag: '--version', required: true },\n { name: 'npm', command: 'npm', versionFlag: '--version', required: false },\n { name: 'yarn', command: 'yarn', versionFlag: '--version', required: false },\n { name: 'pnpm', command: 'pnpm', versionFlag: '--version', required: false },\n { name: 'git', command: 'git', versionFlag: '--version', required: true },\n { name: 'Python', command: 'python3', versionFlag: '--version', required: false },\n];\n\nexport function doctorCommand(): Command {\n return new Command('doctor').description('Check system dependencies').action(() => {\n newLine();\n console.log(chalk.bold.cyan('System Check'));\n console.log(chalk.gray('─'.repeat(40)));\n newLine();\n\n let allGood = true;\n\n for (const check of CHECKS) {\n try {\n const version = execSync(`${check.command} ${check.versionFlag ?? '--version'}`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const versionStr = version.split('\\n')[0];\n console.log(` ${chalk.green('✔')} ${check.name.padEnd(12)} ${chalk.gray(versionStr)}`);\n } catch {\n if (check.required) {\n console.log(\n ` ${chalk.red('✖')} ${check.name.padEnd(12)} ${chalk.red('not found (required)')}`,\n );\n allGood = false;\n } else {\n console.log(\n ` ${chalk.yellow('–')} ${check.name.padEnd(12)} ${chalk.gray('not found (optional)')}`,\n );\n }\n }\n }\n\n newLine();\n\n if (allGood) {\n console.log(chalk.green(' All required dependencies are installed!'));\n } else {\n console.log(chalk.red(' Some required dependencies are missing.'));\n }\n\n newLine();\n });\n}\n","import { createCli } from './cli.js';\n\nconst program = createCli();\nprogram.parse();\n"],"mappings":"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","names":["Command","path","fs","framework","inquirer","inquirer","inquirer","path","path","fs","__filename","__dirname","fs","path","path","path","path","path","path","fs","Command","chalk","Command","chalk","Command","execSync","chalk","Command","chalk","execSync","program","Command"]}
|
package/package.json
CHANGED
package/dist/chunk-2A65KFCS.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/utils/file.ts
|
|
4
|
-
import path from "path";
|
|
5
|
-
import fs from "fs-extra";
|
|
6
|
-
import Handlebars from "handlebars";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
var __dirname = path.dirname(__filename);
|
|
10
|
-
Handlebars.registerHelper("eq", (a, b) => a === b);
|
|
11
|
-
Handlebars.registerHelper("neq", (a, b) => a !== b);
|
|
12
|
-
Handlebars.registerHelper("or", (a, b) => a || b);
|
|
13
|
-
Handlebars.registerHelper("and", (a, b) => a && b);
|
|
14
|
-
Handlebars.registerHelper("lowercase", (str) => str?.toLowerCase());
|
|
15
|
-
Handlebars.registerHelper("year", () => (/* @__PURE__ */ new Date()).getFullYear());
|
|
16
|
-
Handlebars.registerHelper(
|
|
17
|
-
"join",
|
|
18
|
-
(arr, sep) => Array.isArray(arr) ? arr.join(typeof sep === "string" ? sep : ", ") : ""
|
|
19
|
-
);
|
|
20
|
-
function getTemplatesDir() {
|
|
21
|
-
const isBundled = __dirname.replace(/\\/g, "/").endsWith("/dist") || __dirname.replace(/\\/g, "/").includes("/dist/");
|
|
22
|
-
const projectRoot = isBundled ? path.resolve(__dirname, "..") : path.resolve(__dirname, "..", "..");
|
|
23
|
-
return path.resolve(projectRoot, "src", "templates");
|
|
24
|
-
}
|
|
25
|
-
async function createDir(dirPath) {
|
|
26
|
-
await fs.ensureDir(dirPath);
|
|
27
|
-
}
|
|
28
|
-
async function writeFile(filePath, content) {
|
|
29
|
-
await fs.ensureDir(path.dirname(filePath));
|
|
30
|
-
await fs.writeFile(filePath, content, "utf-8");
|
|
31
|
-
}
|
|
32
|
-
async function copyFile(src, dest) {
|
|
33
|
-
await fs.ensureDir(path.dirname(dest));
|
|
34
|
-
await fs.copy(src, dest);
|
|
35
|
-
}
|
|
36
|
-
async function renderTemplate(templatePath, data) {
|
|
37
|
-
const templateContent = await fs.readFile(templatePath, "utf-8");
|
|
38
|
-
const template = Handlebars.compile(templateContent, { noEscape: true });
|
|
39
|
-
return template(data);
|
|
40
|
-
}
|
|
41
|
-
async function renderAndWrite(templatePath, outputPath, data) {
|
|
42
|
-
const content = await renderTemplate(templatePath, data);
|
|
43
|
-
await writeFile(outputPath, content);
|
|
44
|
-
}
|
|
45
|
-
async function directoryExists(dirPath) {
|
|
46
|
-
try {
|
|
47
|
-
const stat = await fs.stat(dirPath);
|
|
48
|
-
return stat.isDirectory();
|
|
49
|
-
} catch {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
async function fileExists(filePath) {
|
|
54
|
-
try {
|
|
55
|
-
const stat = await fs.stat(filePath);
|
|
56
|
-
return stat.isFile();
|
|
57
|
-
} catch {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function getOutputFileName(templateName) {
|
|
62
|
-
return templateName.replace(/\.hbs$/, "");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export {
|
|
66
|
-
getTemplatesDir,
|
|
67
|
-
createDir,
|
|
68
|
-
writeFile,
|
|
69
|
-
copyFile,
|
|
70
|
-
renderTemplate,
|
|
71
|
-
renderAndWrite,
|
|
72
|
-
directoryExists,
|
|
73
|
-
fileExists,
|
|
74
|
-
getOutputFileName
|
|
75
|
-
};
|
|
76
|
-
//# sourceMappingURL=chunk-2A65KFCS.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/file.ts"],"sourcesContent":["import path from 'node:path';\nimport fs from 'fs-extra';\nimport Handlebars from 'handlebars';\nimport { fileURLToPath } from 'node:url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Register custom Handlebars helpers\nHandlebars.registerHelper('eq', (a: unknown, b: unknown) => a === b);\nHandlebars.registerHelper('neq', (a: unknown, b: unknown) => a !== b);\nHandlebars.registerHelper('or', (a: unknown, b: unknown) => a || b);\nHandlebars.registerHelper('and', (a: unknown, b: unknown) => a && b);\nHandlebars.registerHelper('lowercase', (str: string) => str?.toLowerCase());\nHandlebars.registerHelper('year', () => new Date().getFullYear());\nHandlebars.registerHelper('join', (arr: string[], sep: string) =>\n Array.isArray(arr) ? arr.join(typeof sep === 'string' ? sep : ', ') : '',\n);\n\nexport function getTemplatesDir(): string {\n // When bundled with tsup, __dirname is dist/ → go up 1 level to project root\n // When running unbundled (vitest), __dirname is src/utils/ → go up 2 levels to project root\n // In both cases, templates live at <projectRoot>/src/templates\n const isBundled = __dirname.replace(/\\\\/g, '/').endsWith('/dist') ||\n __dirname.replace(/\\\\/g, '/').includes('/dist/');\n const projectRoot = isBundled\n ? path.resolve(__dirname, '..')\n : path.resolve(__dirname, '..', '..');\n return path.resolve(projectRoot, 'src', 'templates');\n}\n\nexport async function createDir(dirPath: string): Promise<void> {\n await fs.ensureDir(dirPath);\n}\n\nexport async function writeFile(filePath: string, content: string): Promise<void> {\n await fs.ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content, 'utf-8');\n}\n\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await fs.ensureDir(path.dirname(dest));\n await fs.copy(src, dest);\n}\n\nexport async function renderTemplate(templatePath: string, data: object): Promise<string> {\n const templateContent = await fs.readFile(templatePath, 'utf-8');\n const template = Handlebars.compile(templateContent, { noEscape: true });\n return template(data);\n}\n\nexport async function renderAndWrite(\n templatePath: string,\n outputPath: string,\n data: object,\n): Promise<void> {\n const content = await renderTemplate(templatePath, data);\n await writeFile(outputPath, content);\n}\n\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isFile();\n } catch {\n return false;\n }\n}\n\nexport function getOutputFileName(templateName: string): string {\n return templateName.replace(/\\.hbs$/, '');\n}\n"],"mappings":";;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,gBAAgB;AACvB,SAAS,qBAAqB;AAE9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,KAAK,QAAQ,UAAU;AAGzC,WAAW,eAAe,MAAM,CAAC,GAAY,MAAe,MAAM,CAAC;AACnE,WAAW,eAAe,OAAO,CAAC,GAAY,MAAe,MAAM,CAAC;AACpE,WAAW,eAAe,MAAM,CAAC,GAAY,MAAe,KAAK,CAAC;AAClE,WAAW,eAAe,OAAO,CAAC,GAAY,MAAe,KAAK,CAAC;AACnE,WAAW,eAAe,aAAa,CAAC,QAAgB,KAAK,YAAY,CAAC;AAC1E,WAAW,eAAe,QAAQ,OAAM,oBAAI,KAAK,GAAE,YAAY,CAAC;AAChE,WAAW;AAAA,EAAe;AAAA,EAAQ,CAAC,KAAe,QAChD,MAAM,QAAQ,GAAG,IAAI,IAAI,KAAK,OAAO,QAAQ,WAAW,MAAM,IAAI,IAAI;AACxE;AAEO,SAAS,kBAA0B;AAIxC,QAAM,YAAY,UAAU,QAAQ,OAAO,GAAG,EAAE,SAAS,OAAO,KAC9D,UAAU,QAAQ,OAAO,GAAG,EAAE,SAAS,QAAQ;AACjD,QAAM,cAAc,YAChB,KAAK,QAAQ,WAAW,IAAI,IAC5B,KAAK,QAAQ,WAAW,MAAM,IAAI;AACtC,SAAO,KAAK,QAAQ,aAAa,OAAO,WAAW;AACrD;AAEA,eAAsB,UAAU,SAAgC;AAC9D,QAAM,GAAG,UAAU,OAAO;AAC5B;AAEA,eAAsB,UAAU,UAAkB,SAAgC;AAChF,QAAM,GAAG,UAAU,KAAK,QAAQ,QAAQ,CAAC;AACzC,QAAM,GAAG,UAAU,UAAU,SAAS,OAAO;AAC/C;AAEA,eAAsB,SAAS,KAAa,MAA6B;AACvE,QAAM,GAAG,UAAU,KAAK,QAAQ,IAAI,CAAC;AACrC,QAAM,GAAG,KAAK,KAAK,IAAI;AACzB;AAEA,eAAsB,eAAe,cAAsB,MAA+B;AACxF,QAAM,kBAAkB,MAAM,GAAG,SAAS,cAAc,OAAO;AAC/D,QAAM,WAAW,WAAW,QAAQ,iBAAiB,EAAE,UAAU,KAAK,CAAC;AACvE,SAAO,SAAS,IAAI;AACtB;AAEA,eAAsB,eACpB,cACA,YACA,MACe;AACf,QAAM,UAAU,MAAM,eAAe,cAAc,IAAI;AACvD,QAAM,UAAU,YAAY,OAAO;AACrC;AAEA,eAAsB,gBAAgB,SAAmC;AACvE,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,KAAK,OAAO;AAClC,WAAO,KAAK,YAAY;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,WAAW,UAAoC;AACnE,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,KAAK,QAAQ;AACnC,WAAO,KAAK,OAAO;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,cAA8B;AAC9D,SAAO,aAAa,QAAQ,UAAU,EAAE;AAC1C;","names":[]}
|
package/dist/file-5IKT7CEX.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
copyFile,
|
|
4
|
-
createDir,
|
|
5
|
-
directoryExists,
|
|
6
|
-
fileExists,
|
|
7
|
-
getOutputFileName,
|
|
8
|
-
getTemplatesDir,
|
|
9
|
-
renderAndWrite,
|
|
10
|
-
renderTemplate,
|
|
11
|
-
writeFile
|
|
12
|
-
} from "./chunk-2A65KFCS.js";
|
|
13
|
-
export {
|
|
14
|
-
copyFile,
|
|
15
|
-
createDir,
|
|
16
|
-
directoryExists,
|
|
17
|
-
fileExists,
|
|
18
|
-
getOutputFileName,
|
|
19
|
-
getTemplatesDir,
|
|
20
|
-
renderAndWrite,
|
|
21
|
-
renderTemplate,
|
|
22
|
-
writeFile
|
|
23
|
-
};
|
|
24
|
-
//# sourceMappingURL=file-5IKT7CEX.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|