create-morax 1.0.2 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import pc9 from "picocolors";
4
+ import pc18 from "picocolors";
5
5
 
6
6
  // src/core/runner.ts
7
- import path6 from "path";
7
+ import path15 from "path";
8
+ import { spinner as spinner11, multiselect } from "@clack/prompts";
9
+ import pc17 from "picocolors";
8
10
 
9
11
  // src/core/startcli.ts
10
12
  import gradient from "gradient-string";
@@ -53,21 +55,21 @@ function handleCancel(input) {
53
55
  }
54
56
 
55
57
  // src/core/workspaceName.ts
56
- async function promptWorkspaceName() {
58
+ async function promptWorkspaceName(isMonorepo = true) {
57
59
  const nameInput = await text({
58
- message: "What is the name of your new monorepo workspace?",
59
- placeholder: "morax-workspace",
60
+ message: isMonorepo ? "What is the name of your new monorepo workspace?" : "What is the name of your new project?",
61
+ placeholder: isMonorepo ? "morax-workspace" : "my-app",
60
62
  validate(value) {
61
63
  if (value && value.includes(" "))
62
- return "Workspace name cannot contain spaces!";
64
+ return isMonorepo ? "Workspace name cannot contain spaces!" : "Project name cannot contain spaces!";
63
65
  }
64
66
  });
65
67
  handleCancel(nameInput);
66
- return String(nameInput).trim() || "morax-workspace";
68
+ return String(nameInput).trim() || (isMonorepo ? "morax-workspace" : "my-app");
67
69
  }
68
70
 
69
71
  // src/tasks/directories.ts
70
- import { multiselect, spinner } from "@clack/prompts";
72
+ import { spinner } from "@clack/prompts";
71
73
  import fsPromises2 from "fs/promises";
72
74
  import path2 from "path";
73
75
  import pc4 from "picocolors";
@@ -82,6 +84,7 @@ import { promisify } from "util";
82
84
  import pc3 from "picocolors";
83
85
  var execAsync = promisify(exec);
84
86
  async function runCommand(command, options = {}) {
87
+ options.silent = true;
85
88
  if (!options.silent) {
86
89
  console.log(pc3.cyan(`> ${command}`));
87
90
  }
@@ -159,36 +162,13 @@ async function createDirectories(directories, projectPath) {
159
162
  }
160
163
  }
161
164
  async function makeDirectories(name, projectPath) {
162
- const directories = await multiselect({
163
- message: "Which directories do you want to include in your workspace?",
164
- options: [
165
- {
166
- value: "apps",
167
- label: "apps/*",
168
- hint: "For frontend apps and backend services"
169
- },
170
- {
171
- value: "packages",
172
- label: "packages/*",
173
- hint: "For shared components, configs, and utilities"
174
- }
175
- ],
176
- required: true
177
- });
178
- handleCancel(directories);
165
+ const directories = ["apps", "packages"];
179
166
  const s = spinner();
180
167
  console.log("\n");
181
168
  s.start("Generating workspace configs...");
182
169
  try {
183
- await generateWorkspaceConfig(
184
- name,
185
- directories,
186
- projectPath
187
- );
188
- await createDirectories(
189
- directories,
190
- projectPath
191
- );
170
+ await generateWorkspaceConfig(name, directories, projectPath);
171
+ await createDirectories(directories, projectPath);
192
172
  s.stop(pc4.green("\u2714 Success: Generated Workspace Root & Folder Structures"));
193
173
  } catch (error) {
194
174
  s.stop(pc4.red("\u2716 Failed: Workspace generation failed"));
@@ -200,13 +180,607 @@ Error details: ${error.message || error}`));
200
180
  return directories;
201
181
  }
202
182
 
203
- // src/tasks/prettier.ts
204
- import { confirm, spinner as spinner2 } from "@clack/prompts";
183
+ // src/core/react-runner.ts
184
+ import path6 from "path";
185
+ import { spinner as spinner4 } from "@clack/prompts";
186
+ import pc8 from "picocolors";
187
+
188
+ // src/tasks/react.ts
205
189
  import pc5 from "picocolors";
206
190
  import fsPromises3 from "fs/promises";
207
191
  import path3 from "path";
192
+ import { spawn } from "child_process";
193
+ import { confirm, spinner as spinner2, text as text2 } from "@clack/prompts";
194
+ function runInteractiveCommand(command, args, cwd) {
195
+ return new Promise((resolve, reject) => {
196
+ const fullCommand = args.length > 0 ? `${command} ${args.join(" ")}` : command;
197
+ const child = spawn(fullCommand, [], {
198
+ cwd,
199
+ stdio: "inherit",
200
+ shell: true,
201
+ env: { ...process.env, NODE_NO_WARNINGS: "1" }
202
+ });
203
+ child.on("close", (code) => {
204
+ if (code === 0) {
205
+ resolve();
206
+ } else {
207
+ reject(
208
+ new Error(
209
+ `Command "${command} ${args.join(" ")}" exited with code ${code}`
210
+ )
211
+ );
212
+ }
213
+ });
214
+ child.on("error", (err) => {
215
+ reject(err);
216
+ });
217
+ });
218
+ }
219
+ async function setupReact(projectPath, isMonorepo = true) {
220
+ const appsDir = isMonorepo ? path3.join(projectPath, "apps") : path3.dirname(projectPath);
221
+ console.log(pc5.magentaBright("\n\u{1F537} IMPORTANT INSTRUCTIONS:"));
222
+ console.log(
223
+ pc5.magentaBright(
224
+ "During the Vite interactive configuration prompts, please select:"
225
+ )
226
+ );
227
+ console.log(
228
+ pc5.magentaBright(
229
+ ` 1. Install & Start: Choose ${pc5.bold("No")} when asked "Install with npm/pnpm and start now?"`
230
+ )
231
+ );
232
+ console.log(
233
+ pc5.magentaBright(
234
+ "This allows Morax CLI to continue automatically configuring your workspace (ESLint, Prettier, Tailwind, packages, etc.)!"
235
+ )
236
+ );
237
+ console.log(
238
+ pc5.cyan("\nStarting interactive Vite React application setup...")
239
+ );
240
+ let beforeDirs = [];
241
+ try {
242
+ beforeDirs = await fsPromises3.readdir(appsDir);
243
+ } catch {
244
+ }
245
+ try {
246
+ await runInteractiveCommand("pnpm", ["create", "vite@latest"], appsDir);
247
+ let afterDirs = [];
248
+ try {
249
+ afterDirs = await fsPromises3.readdir(appsDir);
250
+ } catch {
251
+ }
252
+ const newDirs = afterDirs.filter((d) => !beforeDirs.includes(d));
253
+ let createdDirName = "web";
254
+ if (newDirs.length > 0) {
255
+ createdDirName = newDirs[0];
256
+ }
257
+ const webDir = path3.join(appsDir, createdDirName);
258
+ try {
259
+ await fsPromises3.access(webDir);
260
+ } catch {
261
+ throw new Error(
262
+ `Vite React application directory "apps/${createdDirName}" was not found. Please ensure you complete the Vite installation.`
263
+ );
264
+ }
265
+ const aliasSpinner = spinner2();
266
+ aliasSpinner.start("Configuring import aliases and Node types...");
267
+ try {
268
+ const pkgPath = path3.join(webDir, "package.json");
269
+ try {
270
+ const pkgRaw = await fsPromises3.readFile(pkgPath, "utf8");
271
+ const pkg = JSON.parse(pkgRaw);
272
+ pkg.devDependencies = pkg.devDependencies || {};
273
+ pkg.devDependencies["@types/node"] = "^20.11.0";
274
+ await fsPromises3.writeFile(
275
+ pkgPath,
276
+ JSON.stringify(pkg, null, 2),
277
+ "utf8"
278
+ );
279
+ } catch {
280
+ }
281
+ const tsconfigPaths = [
282
+ path3.join(webDir, "tsconfig.json"),
283
+ path3.join(webDir, "tsconfig.app.json")
284
+ ];
285
+ for (const tsPath of tsconfigPaths) {
286
+ try {
287
+ let content = await fsPromises3.readFile(tsPath, "utf8");
288
+ if (content.includes('"compilerOptions"')) {
289
+ if (!content.includes('"paths"')) {
290
+ content = content.replace(
291
+ /("compilerOptions"\s*:\s*\{)/,
292
+ `$1
293
+ "baseUrl": ".",
294
+ "paths": {
295
+ "@/*": ["./src/*"]
296
+ },`
297
+ );
298
+ await fsPromises3.writeFile(tsPath, content, "utf8");
299
+ }
300
+ } else {
301
+ content = content.replace(
302
+ /^(\s*\{)/,
303
+ `$1
304
+ "compilerOptions": {
305
+ "baseUrl": ".",
306
+ "paths": {
307
+ "@/*": ["./src/*"]
308
+ }
309
+ },`
310
+ );
311
+ await fsPromises3.writeFile(tsPath, content, "utf8");
312
+ }
313
+ } catch {
314
+ }
315
+ }
316
+ const viteConfigPath = path3.join(webDir, "vite.config.ts");
317
+ try {
318
+ let content = await fsPromises3.readFile(viteConfigPath, "utf8");
319
+ if (!content.includes("import path from 'path'")) {
320
+ content = "import path from 'path';\n" + content;
321
+ }
322
+ if (!content.includes("resolve:")) {
323
+ content = content.replace(
324
+ /(plugins:\s*\[[^\]]*\]),?/,
325
+ `$1,
326
+ resolve: {
327
+ alias: {
328
+ "@": path.resolve(__dirname, "./src"),
329
+ },
330
+ }`
331
+ );
332
+ }
333
+ await fsPromises3.writeFile(viteConfigPath, content, "utf8");
334
+ } catch {
335
+ }
336
+ aliasSpinner.stop(
337
+ pc5.green("\u2714 Success: Import aliases and Node types configured")
338
+ );
339
+ } catch (err) {
340
+ aliasSpinner.stop(pc5.red("\u2716 Failed: Import alias configuration failed"));
341
+ }
342
+ console.log(
343
+ pc5.green(
344
+ isMonorepo ? `
345
+ \u2714 Success: Vite React frontend configured in apps/${createdDirName}
346
+ ` : `
347
+ \u2714 Success: Vite React frontend configured at root
348
+ `
349
+ )
350
+ );
351
+ const setupTailwindPrompt = await confirm({
352
+ message: "Do you want to install and configure Tailwind CSS v4?",
353
+ initialValue: true
354
+ });
355
+ handleCancel(setupTailwindPrompt);
356
+ const setupShadcnPrompt = await confirm({
357
+ message: `Do you want to setup shadcn UI in your Vite React website (apps/${createdDirName})?`,
358
+ initialValue: true
359
+ });
360
+ handleCancel(setupShadcnPrompt);
361
+ const needsTailwind = setupTailwindPrompt || setupShadcnPrompt;
362
+ if (needsTailwind) {
363
+ const s = spinner2();
364
+ s.start(
365
+ "Installing Tailwind CSS v4, Vite integration, and Node types..."
366
+ );
367
+ try {
368
+ const pkgPath = path3.join(webDir, "package.json");
369
+ try {
370
+ const pkgRaw = await fsPromises3.readFile(pkgPath, "utf8");
371
+ const pkg = JSON.parse(pkgRaw);
372
+ pkg.dependencies = pkg.dependencies || {};
373
+ pkg.devDependencies = pkg.devDependencies || {};
374
+ pkg.dependencies["tailwindcss"] = "^4.0.0";
375
+ pkg.devDependencies["@tailwindcss/vite"] = "^4.0.0";
376
+ pkg.devDependencies["@types/node"] = "^20.11.0";
377
+ await fsPromises3.writeFile(
378
+ pkgPath,
379
+ JSON.stringify(pkg, null, 2),
380
+ "utf8"
381
+ );
382
+ } catch {
383
+ }
384
+ await runCommand("pnpm install", { cwd: webDir, silent: true });
385
+ s.message("Configuring Vite plugins and Tailwind CSS imports...");
386
+ const viteConfigPath = path3.join(webDir, "vite.config.ts");
387
+ try {
388
+ let content = await fsPromises3.readFile(viteConfigPath, "utf8");
389
+ if (!content.includes("@tailwindcss/vite")) {
390
+ content = "import tailwindcss from '@tailwindcss/vite';\n" + content;
391
+ if (content.includes("plugins: [react()]")) {
392
+ content = content.replace(
393
+ "plugins: [react()]",
394
+ "plugins: [react(), tailwindcss()]"
395
+ );
396
+ } else if (content.includes("plugins: [react(),]")) {
397
+ content = content.replace(
398
+ "plugins: [react(),]",
399
+ "plugins: [react(), tailwindcss()]"
400
+ );
401
+ } else {
402
+ content = content.replace(
403
+ /(plugins:\s*\[\s*react\(\),?\s*)(\])/g,
404
+ "$1\n tailwindcss(),\n $2"
405
+ );
406
+ }
407
+ await fsPromises3.writeFile(viteConfigPath, content, "utf8");
408
+ }
409
+ } catch {
410
+ }
411
+ const indexCssPath = path3.join(webDir, "src", "index.css");
412
+ try {
413
+ await fsPromises3.writeFile(
414
+ indexCssPath,
415
+ '@import "tailwindcss";\n',
416
+ "utf8"
417
+ );
418
+ } catch {
419
+ }
420
+ s.stop(
421
+ pc5.green("\u2714 Success: Tailwind CSS v4 configured successfully\n")
422
+ );
423
+ } catch (err) {
424
+ s.stop(pc5.red("\u2716 Failed: Tailwind CSS v4 setup failed"));
425
+ throw err;
426
+ }
427
+ }
428
+ if (setupShadcnPrompt) {
429
+ const hasPreset = await confirm({
430
+ message: "Do you have a custom shadcn UI preset code?",
431
+ initialValue: false
432
+ });
433
+ handleCancel(hasPreset);
434
+ let presetCode = "";
435
+ if (hasPreset) {
436
+ const enteredPreset = await text2({
437
+ message: "Enter your custom shadcn UI preset code:",
438
+ placeholder: "e.g. 123456",
439
+ validate: (val) => {
440
+ if (!val || !val.trim()) return "Preset code cannot be empty";
441
+ return;
442
+ }
443
+ });
444
+ handleCancel(enteredPreset);
445
+ presetCode = enteredPreset.trim();
446
+ }
447
+ const args = ["dlx", "shadcn@latest", "init"];
448
+ if (presetCode) {
449
+ args.push("--preset", presetCode);
450
+ }
451
+ args.push("--template", "vite");
452
+ console.log(
453
+ pc5.cyan("\nStarting interactive shadcn UI initialization...")
454
+ );
455
+ await runInteractiveCommand("pnpm", args, webDir);
456
+ console.log(
457
+ pc5.green(
458
+ isMonorepo ? `
459
+ \u2714 Success: shadcn UI successfully initialized in apps/${createdDirName}
460
+ ` : `
461
+ \u2714 Success: shadcn UI successfully initialized at root
462
+ `
463
+ )
464
+ );
465
+ }
466
+ return webDir;
467
+ } catch (error) {
468
+ console.error(pc5.red(`
469
+ \u2716 Failed: Vite React frontend setup failed`));
470
+ console.error(pc5.red(`Error details: ${error.message || error}
471
+ `));
472
+ process.exit(1);
473
+ }
474
+ }
475
+
476
+ // src/tasks/git.ts
477
+ import { confirm as confirm2, spinner as spinner3 } from "@clack/prompts";
478
+ import pc6 from "picocolors";
479
+ import fsPromises4 from "fs/promises";
480
+ import path4 from "path";
481
+ async function promptGit() {
482
+ return await confirm2({
483
+ message: "Do you want to initialize a local Git repository?",
484
+ initialValue: true
485
+ });
486
+ }
487
+ async function gitInit(projectPath) {
488
+ await runCommand("git init", { cwd: projectPath, silent: true });
489
+ const gitignore = [
490
+ "# Dependency directories",
491
+ "node_modules/",
492
+ "jspm_packages/",
493
+ "web_modules/",
494
+ "",
495
+ "# Build and output outputs",
496
+ "dist/",
497
+ "build/",
498
+ ".next/",
499
+ "out/",
500
+ ".turbo/",
501
+ "",
502
+ "# Environments",
503
+ ".env",
504
+ ".env.local",
505
+ ".env.development.local",
506
+ ".env.test.local",
507
+ ".env.production.local",
508
+ "",
509
+ "# Logs",
510
+ "npm-debug.log*",
511
+ "yarn-debug.log*",
512
+ "yarn-error.log*",
513
+ "pnpm-debug.log*",
514
+ "*.log",
515
+ "",
516
+ "# OS Metadata",
517
+ ".DS_Store",
518
+ "Thumbs.db",
519
+ ""
520
+ ].join("\n");
521
+ await fsPromises4.writeFile(
522
+ path4.join(projectPath, ".gitignore"),
523
+ gitignore,
524
+ "utf8"
525
+ );
526
+ }
527
+ async function runGitSetup(projectPath) {
528
+ const gitPrompt = await promptGit();
529
+ handleCancel(gitPrompt);
530
+ let gitInitialized = false;
531
+ if (gitPrompt) {
532
+ const s = spinner3();
533
+ console.log("\n");
534
+ s.start("Initializing Git repository...");
535
+ try {
536
+ await gitInit(projectPath);
537
+ s.stop(pc6.green("\u2714 Success: Git repository initialized"));
538
+ gitInitialized = true;
539
+ } catch (error) {
540
+ s.stop(pc6.red("\u2716 Failed: Git initialization failed"));
541
+ console.error(pc6.red(`
542
+ Error details: ${error.message || error}`));
543
+ process.exit(1);
544
+ }
545
+ console.log("\n");
546
+ }
547
+ return gitInitialized;
548
+ }
549
+
550
+ // src/tasks/addreadme.ts
551
+ import fsPromises5 from "fs/promises";
552
+ import path5 from "path";
553
+ async function setupReadme(projectPath, workspaceName) {
554
+ const readmePath = path5.join(projectPath, "README.md");
555
+ const readmeContent = [
556
+ `# Morax `,
557
+ "",
558
+ "Welcome to your next-generation, high-performance monorepo workspace generated by **Morax**.",
559
+ "",
560
+ "## Workspace Structure",
561
+ "",
562
+ "- **`apps/`** \u2014 Frontend applications, backend servers, and user-facing services.",
563
+ "- **`packages/`** \u2014 Shared modules, TypeScript configurations, ESLint rule sets, and common utility libraries.",
564
+ "",
565
+ "## Key Commands",
566
+ "",
567
+ "Run the following commands from the root directory of your workspace:",
568
+ "",
569
+ "### Development",
570
+ "Start all dev servers and hot-reloading configurations concurrently:",
571
+ "```bash",
572
+ "pnpm dev",
573
+ "```",
574
+ "",
575
+ "### Linting",
576
+ "Run static analysis across all workspaces using your shared ESLint rules:",
577
+ "```bash",
578
+ "pnpm lint",
579
+ "```",
580
+ "",
581
+ "### Formatting",
582
+ "Automatically format all codebase files using Prettier:",
583
+ "```bash",
584
+ "pnpm format",
585
+ "```",
586
+ "",
587
+ "---",
588
+ "",
589
+ "*Generated with love by [Morax Scaffolder CLI](https://github.com/Elitedv/morax).*",
590
+ ""
591
+ ].join("\n");
592
+ await fsPromises5.writeFile(readmePath, readmeContent, "utf8");
593
+ }
594
+
595
+ // src/core/endcli.ts
596
+ import pc7 from "picocolors";
597
+ import { outro } from "@clack/prompts";
598
+ function endCli(name, projectPath) {
599
+ outro(pc7.yellow("Morax scaffolding completed successfully!"));
600
+ console.log(
601
+ `\u26A1 ${pc7.bold("Workspace ready:")} ${pc7.yellow(name)}
602
+ ${pc7.bold("To start developing:")}
603
+ 1. ${pc7.cyan(`cd ${name}`)}
604
+ 2. ${pc7.cyan("pnpm dev")}`
605
+ );
606
+ }
607
+
608
+ // src/core/react-runner.ts
609
+ async function runReactScaffolder(name, projectPath) {
610
+ const finalProjectPath = await setupReact(projectPath, false);
611
+ await setupReadme(finalProjectPath, path6.basename(finalProjectPath));
612
+ await runGitSetup(finalProjectPath);
613
+ const installSpinner = spinner4();
614
+ console.log("\n");
615
+ installSpinner.start("Running final package installation...");
616
+ try {
617
+ await runCommand("pnpm install", { cwd: finalProjectPath, silent: true });
618
+ installSpinner.stop(
619
+ pc8.green("\u2714 Success: Dependencies configured successfully")
620
+ );
621
+ } catch (error) {
622
+ installSpinner.stop(pc8.red("\u2716 Warning: Final package installation failed"));
623
+ console.error(pc8.red(`
624
+ Error details: ${error.message || error}`));
625
+ }
626
+ console.log("\n");
627
+ endCli(path6.basename(finalProjectPath), finalProjectPath);
628
+ }
629
+
630
+ // src/core/next-runner.ts
631
+ import path9 from "path";
632
+ import { spinner as spinner5 } from "@clack/prompts";
633
+ import pc11 from "picocolors";
634
+
635
+ // src/tasks/nextjs.ts
636
+ import { confirm as confirm3 } from "@clack/prompts";
637
+ import pc9 from "picocolors";
638
+ import fsPromises6 from "fs/promises";
639
+ import path7 from "path";
640
+ import { spawn as spawn2 } from "child_process";
641
+ function runInteractiveCommand2(command, args, cwd) {
642
+ return new Promise((resolve, reject) => {
643
+ const child = spawn2(command, args, {
644
+ cwd,
645
+ stdio: "inherit",
646
+ shell: true,
647
+ env: { ...process.env, NODE_NO_WARNINGS: "1" }
648
+ });
649
+ child.on("close", (code) => {
650
+ if (code === 0) {
651
+ resolve();
652
+ } else {
653
+ reject(
654
+ new Error(
655
+ `Command "${command} ${args.join(" ")}" exited with code ${code}`
656
+ )
657
+ );
658
+ }
659
+ });
660
+ child.on("error", (err) => {
661
+ reject(err);
662
+ });
663
+ });
664
+ }
665
+ async function setupNextjs(projectPath, isMonorepo = true) {
666
+ const appsDir = isMonorepo ? path7.join(projectPath, "apps") : path7.dirname(projectPath);
667
+ const targetName = isMonorepo ? "web" : path7.basename(projectPath);
668
+ const webDir = path7.join(appsDir, targetName);
669
+ try {
670
+ await fsPromises6.rm(webDir, { recursive: true, force: true });
671
+ } catch {
672
+ }
673
+ console.log(pc9.cyan("\nStarting interactive Next.js application setup..."));
674
+ await runInteractiveCommand2(
675
+ "npx",
676
+ ["create-next-app@latest", targetName],
677
+ appsDir
678
+ );
679
+ return webDir;
680
+ }
681
+
682
+ // src/tasks/shadcn.ts
683
+ import { confirm as confirm4 } from "@clack/prompts";
684
+ import pc10 from "picocolors";
685
+ import fsPromises7 from "fs/promises";
686
+ import path8 from "path";
687
+ import { spawn as spawn3 } from "child_process";
688
+ async function promptShadcn() {
689
+ return await confirm4({
690
+ message: "Do you want to setup shadcn UI in your Next.js website (apps/web)?",
691
+ initialValue: true
692
+ });
693
+ }
694
+ function runInteractiveCommand3(command, args, cwd) {
695
+ return new Promise((resolve, reject) => {
696
+ const child = spawn3(command, args, {
697
+ cwd,
698
+ stdio: "inherit",
699
+ shell: true,
700
+ env: { ...process.env, NODE_NO_WARNINGS: "1" }
701
+ });
702
+ child.on("close", (code) => {
703
+ if (code === 0) {
704
+ resolve();
705
+ } else {
706
+ reject(
707
+ new Error(
708
+ `Command "${command} ${args.join(" ")}" exited with code ${code}`
709
+ )
710
+ );
711
+ }
712
+ });
713
+ child.on("error", (err) => {
714
+ reject(err);
715
+ });
716
+ });
717
+ }
718
+ async function setupShadcn(projectPath, isMonorepo = true) {
719
+ const webDir = isMonorepo ? path8.join(projectPath, "apps", "web") : projectPath;
720
+ console.log(pc10.cyan("\nStarting interactive shadcn UI initialization..."));
721
+ await runInteractiveCommand3(
722
+ "pnpm",
723
+ ["dlx", "shadcn@latest", "init", "--template", "next"],
724
+ webDir
725
+ );
726
+ }
727
+ async function runShadcnSetup(projectPath, isMonorepo = true) {
728
+ const webDir = isMonorepo ? path8.join(projectPath, "apps", "web") : projectPath;
729
+ try {
730
+ await fsPromises7.access(webDir);
731
+ } catch {
732
+ return;
733
+ }
734
+ const shadcnPrompt = await promptShadcn();
735
+ handleCancel(shadcnPrompt);
736
+ if (shadcnPrompt) {
737
+ try {
738
+ await setupShadcn(projectPath, isMonorepo);
739
+ console.log(
740
+ pc10.green(
741
+ isMonorepo ? "\n\u2714 Success: shadcn UI successfully initialized in apps/web\n" : "\n\u2714 Success: shadcn UI successfully initialized at root\n"
742
+ )
743
+ );
744
+ } catch (error) {
745
+ console.error(pc10.red(`
746
+ \u2716 Failed: shadcn UI setup failed`));
747
+ console.error(pc10.red(`Error details: ${error.message || error}
748
+ `));
749
+ process.exit(1);
750
+ }
751
+ }
752
+ }
753
+
754
+ // src/core/next-runner.ts
755
+ async function runNextScaffolder(name, projectPath) {
756
+ const finalProjectPath = await setupNextjs(projectPath, false);
757
+ await runShadcnSetup(finalProjectPath, false);
758
+ await setupReadme(finalProjectPath, path9.basename(finalProjectPath));
759
+ await runGitSetup(finalProjectPath);
760
+ const installSpinner = spinner5();
761
+ console.log("\n");
762
+ installSpinner.start("Running final package installation...");
763
+ try {
764
+ await runCommand("pnpm install", { cwd: finalProjectPath, silent: true });
765
+ installSpinner.stop(
766
+ pc11.green("\u2714 Success: Dependencies configured successfully")
767
+ );
768
+ } catch (error) {
769
+ installSpinner.stop(pc11.red("\u2716 Warning: Final package installation failed"));
770
+ console.error(pc11.red(`
771
+ Error details: ${error.message || error}`));
772
+ }
773
+ console.log("\n");
774
+ endCli(path9.basename(finalProjectPath), finalProjectPath);
775
+ }
776
+
777
+ // src/tasks/prettier.ts
778
+ import { confirm as confirm5, spinner as spinner6 } from "@clack/prompts";
779
+ import pc12 from "picocolors";
780
+ import fsPromises8 from "fs/promises";
781
+ import path10 from "path";
208
782
  async function promptPrettier() {
209
- return await confirm({
783
+ return await confirm5({
210
784
  message: "Do you want to setup Prettier code formatting?",
211
785
  initialValue: true
212
786
  });
@@ -220,8 +794,8 @@ async function setupPrettier(projectPath) {
220
794
  trailingComma: "all",
221
795
  printWidth: 100
222
796
  };
223
- await fsPromises3.writeFile(
224
- path3.join(projectPath, ".prettierrc"),
797
+ await fsPromises8.writeFile(
798
+ path10.join(projectPath, ".prettierrc"),
225
799
  JSON.stringify(prettierrc, null, 2),
226
800
  "utf8"
227
801
  );
@@ -246,19 +820,19 @@ async function setupPrettier(projectPath) {
246
820
  ".env.*",
247
821
  ""
248
822
  ].join("\n");
249
- await fsPromises3.writeFile(
250
- path3.join(projectPath, ".prettierignore"),
823
+ await fsPromises8.writeFile(
824
+ path10.join(projectPath, ".prettierignore"),
251
825
  prettierignore,
252
826
  "utf8"
253
827
  );
254
- const rootPackagePath = path3.join(projectPath, "package.json");
255
- const rootPackageContent = await fsPromises3.readFile(rootPackagePath, "utf8");
828
+ const rootPackagePath = path10.join(projectPath, "package.json");
829
+ const rootPackageContent = await fsPromises8.readFile(rootPackagePath, "utf8");
256
830
  const pkg = JSON.parse(rootPackageContent);
257
831
  pkg.scripts = {
258
832
  ...pkg.scripts || {},
259
833
  format: "prettier --write ."
260
834
  };
261
- await fsPromises3.writeFile(
835
+ await fsPromises8.writeFile(
262
836
  rootPackagePath,
263
837
  JSON.stringify(pkg, null, 2),
264
838
  "utf8"
@@ -269,15 +843,15 @@ async function runPrettierSetup(projectPath) {
269
843
  const prettierPrompt = await promptPrettier();
270
844
  handleCancel(prettierPrompt);
271
845
  if (prettierPrompt) {
272
- const s = spinner2();
846
+ const s = spinner6();
273
847
  console.log("\n");
274
848
  s.start("Setting up Prettier auto-formatting...");
275
849
  try {
276
850
  await setupPrettier(projectPath);
277
- s.stop(pc5.green("\u2714 Success: Prettier formatting configured"));
851
+ s.stop(pc12.green("\u2714 Success: Prettier formatting configured"));
278
852
  } catch (error) {
279
- s.stop(pc5.red("\u2716 Failed: Prettier setup failed"));
280
- console.error(pc5.red(`
853
+ s.stop(pc12.red("\u2716 Failed: Prettier setup failed"));
854
+ console.error(pc12.red(`
281
855
  Error details: ${error.message || error}`));
282
856
  process.exit(1);
283
857
  }
@@ -285,87 +859,328 @@ Error details: ${error.message || error}`));
285
859
  }
286
860
  }
287
861
 
288
- // src/tasks/git.ts
289
- import { confirm as confirm2, spinner as spinner3 } from "@clack/prompts";
290
- import pc6 from "picocolors";
291
- import fsPromises4 from "fs/promises";
292
- import path4 from "path";
293
- async function promptGit() {
294
- return await confirm2({
295
- message: "Do you want to initialize a local Git repository?",
862
+ // src/tasks/eslint.ts
863
+ import { spinner as spinner7 } from "@clack/prompts";
864
+ import pc13 from "picocolors";
865
+ import fsPromises9 from "fs/promises";
866
+ import path11 from "path";
867
+ async function setupEslint(projectPath) {
868
+ const eslintDir = path11.join(projectPath, "packages", "eslint");
869
+ await fsPromises9.mkdir(eslintDir, { recursive: true });
870
+ await runCommand("pnpm init", { cwd: eslintDir, silent: true });
871
+ const eslintPackagePath = path11.join(eslintDir, "package.json");
872
+ const eslintPackageRaw = await fsPromises9.readFile(eslintPackagePath, "utf8");
873
+ const eslintPkg = JSON.parse(eslintPackageRaw);
874
+ eslintPkg.name = "@config/eslint";
875
+ eslintPkg.version = "1.0.0";
876
+ eslintPkg.private = true;
877
+ eslintPkg.type = "module";
878
+ eslintPkg.main = "eslint.config.ts";
879
+ eslintPkg.exports = {
880
+ ".": "./eslint.config.ts"
881
+ };
882
+ eslintPkg.scripts = {
883
+ lint: "eslint ."
884
+ };
885
+ delete eslintPkg.keywords;
886
+ delete eslintPkg.author;
887
+ delete eslintPkg.license;
888
+ await fsPromises9.writeFile(
889
+ eslintPackagePath,
890
+ JSON.stringify(eslintPkg, null, 2),
891
+ "utf8"
892
+ );
893
+ await runCommand(
894
+ "pnpm add -D eslint @eslint/js @eslint/json globals typescript-eslint",
895
+ { cwd: eslintDir }
896
+ );
897
+ const eslintConfigContent = [
898
+ 'import js from "@eslint/js";',
899
+ 'import globals from "globals";',
900
+ 'import tseslint from "typescript-eslint";',
901
+ 'import eslintJson from "@eslint/json";',
902
+ "",
903
+ "export default tseslint.config(",
904
+ " {",
905
+ ' ignores: ["**/dist/**", "**/node_modules/**"],',
906
+ " },",
907
+ " js.configs.recommended,",
908
+ " ...tseslint.configs.recommended,",
909
+ " {",
910
+ " languageOptions: {",
911
+ " globals: {",
912
+ " ...globals.node,",
913
+ " },",
914
+ " },",
915
+ " rules: {",
916
+ ' "no-unused-vars": "off",',
917
+ ' "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],',
918
+ " },",
919
+ " },",
920
+ " {",
921
+ ' files: ["**/*.json"],',
922
+ ' language: "json/json",',
923
+ " ...eslintJson.configs.recommended,",
924
+ " }",
925
+ ");",
926
+ ""
927
+ ].join("\n");
928
+ await fsPromises9.writeFile(
929
+ path11.join(eslintDir, "eslint.config.ts"),
930
+ eslintConfigContent,
931
+ "utf8"
932
+ );
933
+ const rootPackagePath = path11.join(projectPath, "package.json");
934
+ const rootPackageContent = await fsPromises9.readFile(rootPackagePath, "utf8");
935
+ const rootPkg = JSON.parse(rootPackageContent);
936
+ rootPkg.scripts = {
937
+ ...rootPkg.scripts || {},
938
+ lint: "pnpm --filter @config/eslint lint"
939
+ };
940
+ await fsPromises9.writeFile(
941
+ rootPackagePath,
942
+ JSON.stringify(rootPkg, null, 2),
943
+ "utf8"
944
+ );
945
+ }
946
+ async function runEslintSetup(projectPath) {
947
+ const packagesDir = path11.join(projectPath, "packages");
948
+ try {
949
+ await fsPromises9.access(packagesDir);
950
+ } catch {
951
+ return;
952
+ }
953
+ const s = spinner7();
954
+ console.log("\n");
955
+ s.start("Setting up modular ESLint in packages/eslint...");
956
+ try {
957
+ await setupEslint(projectPath);
958
+ s.stop(pc13.green("\u2714 Success: Modular ESLint configured in packages/eslint"));
959
+ } catch (error) {
960
+ s.stop(pc13.red("\u2716 Failed: ESLint setup failed"));
961
+ console.error(pc13.red(`
962
+ Error details: ${error.message || error}`));
963
+ process.exit(1);
964
+ }
965
+ console.log("\n");
966
+ }
967
+
968
+ // src/tasks/typescript.ts
969
+ import { spinner as spinner8 } from "@clack/prompts";
970
+ import pc14 from "picocolors";
971
+ import fsPromises10 from "fs/promises";
972
+ import path12 from "path";
973
+ async function setupTypescript(projectPath) {
974
+ const tsDir = path12.join(projectPath, "packages", "typescript");
975
+ await fsPromises10.mkdir(tsDir, { recursive: true });
976
+ await runCommand("pnpm init", { cwd: tsDir, silent: true });
977
+ const tsPackagePath = path12.join(tsDir, "package.json");
978
+ const tsPackageRaw = await fsPromises10.readFile(tsPackagePath, "utf8");
979
+ const tsPkg = JSON.parse(tsPackageRaw);
980
+ tsPkg.name = "@config/typescript";
981
+ tsPkg.version = "1.0.0";
982
+ tsPkg.private = true;
983
+ tsPkg.type = "module";
984
+ tsPkg.exports = {
985
+ "./tsconfig.json": "./tsconfig.json"
986
+ };
987
+ delete tsPkg.keywords;
988
+ delete tsPkg.author;
989
+ delete tsPkg.license;
990
+ delete tsPkg.main;
991
+ delete tsPkg.scripts;
992
+ await fsPromises10.writeFile(
993
+ tsPackagePath,
994
+ JSON.stringify(tsPkg, null, 2),
995
+ "utf8"
996
+ );
997
+ await runCommand(
998
+ "cd ../.. && pnpm add -D typescript -w && cd packages/typescript",
999
+ { cwd: tsDir, silent: true }
1000
+ );
1001
+ await runCommand("npx tsc --init", { cwd: tsDir });
1002
+ }
1003
+ async function runTypescriptSetup(projectPath) {
1004
+ const packagesDir = path12.join(projectPath, "packages");
1005
+ try {
1006
+ await fsPromises10.access(packagesDir);
1007
+ } catch {
1008
+ return;
1009
+ }
1010
+ const s = spinner8();
1011
+ console.log("\n");
1012
+ s.start("Setting up modular TypeScript config in packages/typescript...");
1013
+ try {
1014
+ await setupTypescript(projectPath);
1015
+ s.stop(
1016
+ pc14.green(
1017
+ "\u2714 Success: Modular TypeScript config configured in packages/typescript"
1018
+ )
1019
+ );
1020
+ } catch (error) {
1021
+ s.stop(pc14.red("\u2716 Failed: TypeScript config setup failed"));
1022
+ console.error(pc14.red(`
1023
+ Error details: ${error.message || error}`));
1024
+ process.exit(1);
1025
+ }
1026
+ console.log("\n");
1027
+ }
1028
+
1029
+ // src/tasks/server.ts
1030
+ import { confirm as confirm6, spinner as spinner9 } from "@clack/prompts";
1031
+ import pc15 from "picocolors";
1032
+ import fsPromises11 from "fs/promises";
1033
+ import path13 from "path";
1034
+ async function promptServer() {
1035
+ return await confirm6({
1036
+ message: "Do you want to setup a Express backend in apps/server?",
296
1037
  initialValue: true
297
1038
  });
298
1039
  }
299
- async function gitInit(projectPath) {
300
- await runCommand("git init", { cwd: projectPath, silent: true });
301
- const gitignore = [
302
- "# Dependency directories",
303
- "node_modules/",
304
- "jspm_packages/",
305
- "web_modules/",
1040
+ async function setupServer(projectPath) {
1041
+ const serverDir = path13.join(projectPath, "apps", "server");
1042
+ const srcDir = path13.join(serverDir, "src");
1043
+ await fsPromises11.mkdir(srcDir, { recursive: true });
1044
+ await runCommand("pnpm init", { cwd: serverDir, silent: true });
1045
+ const serverPackagePath = path13.join(serverDir, "package.json");
1046
+ const serverPackageRaw = await fsPromises11.readFile(serverPackagePath, "utf8");
1047
+ const serverPkg = JSON.parse(serverPackageRaw);
1048
+ const eslintDir = path13.join(projectPath, "packages", "eslint");
1049
+ let hasEslint = false;
1050
+ try {
1051
+ await fsPromises11.access(eslintDir);
1052
+ hasEslint = true;
1053
+ } catch {
1054
+ }
1055
+ serverPkg.name = "server";
1056
+ serverPkg.version = "1.0.0";
1057
+ serverPkg.private = true;
1058
+ serverPkg.type = "module";
1059
+ serverPkg.main = "dist/index.js";
1060
+ serverPkg.scripts = {
1061
+ dev: "tsx watch src/index.ts",
1062
+ build: "tsc",
1063
+ start: "node dist/index.js"
1064
+ };
1065
+ serverPkg.devDependencies = {
1066
+ "@config/typescript": "workspace:*"
1067
+ };
1068
+ if (hasEslint) {
1069
+ serverPkg.devDependencies["@config/eslint"] = "workspace:*";
1070
+ serverPkg.scripts["lint"] = "eslint .";
1071
+ }
1072
+ delete serverPkg.keywords;
1073
+ delete serverPkg.author;
1074
+ delete serverPkg.license;
1075
+ await fsPromises11.writeFile(
1076
+ serverPackagePath,
1077
+ JSON.stringify(serverPkg, null, 2),
1078
+ "utf8"
1079
+ );
1080
+ const tsconfigContent = {
1081
+ extends: "@config/typescript/tsconfig.json",
1082
+ compilerOptions: {
1083
+ rootDir: "src",
1084
+ outDir: "dist"
1085
+ },
1086
+ include: ["src/**/*"]
1087
+ };
1088
+ await fsPromises11.writeFile(
1089
+ path13.join(serverDir, "tsconfig.json"),
1090
+ JSON.stringify(tsconfigContent, null, 2),
1091
+ "utf8"
1092
+ );
1093
+ if (hasEslint) {
1094
+ const eslintConfigContent = [
1095
+ "import baseConfig from '@config/eslint';",
1096
+ "",
1097
+ "export default [",
1098
+ " ...baseConfig,",
1099
+ " {",
1100
+ " // Add server-specific overrides if necessary",
1101
+ " },",
1102
+ "];",
1103
+ ""
1104
+ ].join("\n");
1105
+ await fsPromises11.writeFile(
1106
+ path13.join(serverDir, "eslint.config.ts"),
1107
+ eslintConfigContent,
1108
+ "utf8"
1109
+ );
1110
+ }
1111
+ await runCommand("pnpm add express cors", { cwd: serverDir, silent: false });
1112
+ await runCommand("pnpm add -D @types/express @types/cors tsx typescript", {
1113
+ cwd: serverDir,
1114
+ silent: false
1115
+ });
1116
+ const indexTsContent = [
1117
+ "import express from 'express';",
1118
+ "import cors from 'cors';",
306
1119
  "",
307
- "# Build and output outputs",
308
- "dist/",
309
- "build/",
310
- ".next/",
311
- "out/",
312
- ".turbo/",
1120
+ "const app = express();",
1121
+ "const port = process.env.PORT || 3001;",
313
1122
  "",
314
- "# Environments",
315
- ".env",
316
- ".env.local",
317
- ".env.development.local",
318
- ".env.test.local",
319
- ".env.production.local",
1123
+ "// Enable CORS and parsing of JSON request bodies",
1124
+ "app.use(cors());",
1125
+ "app.use(express.json());",
320
1126
  "",
321
- "# Logs",
322
- "npm-debug.log*",
323
- "yarn-debug.log*",
324
- "yarn-error.log*",
325
- "pnpm-debug.log*",
326
- "*.log",
1127
+ "// Root API health and welcome route",
1128
+ "app.get('/api', (req, res) => {",
1129
+ " res.json({",
1130
+ " message: 'Welcome to the Morax High-Performance Backend API!',",
1131
+ " timestamp: new Date().toISOString(),",
1132
+ " status: 'healthy',",
1133
+ " });",
1134
+ "});",
327
1135
  "",
328
- "# OS Metadata",
329
- ".DS_Store",
330
- "Thumbs.db",
1136
+ "app.listen(port, () => {",
1137
+ " console.log(`\u{1F680} Server is running on http://localhost:${port}`);",
1138
+ "});",
331
1139
  ""
332
1140
  ].join("\n");
333
- await fsPromises4.writeFile(
334
- path4.join(projectPath, ".gitignore"),
335
- gitignore,
1141
+ await fsPromises11.writeFile(
1142
+ path13.join(srcDir, "index.ts"),
1143
+ indexTsContent,
336
1144
  "utf8"
337
1145
  );
338
1146
  }
339
- async function runGitSetup(projectPath) {
340
- const gitPrompt = await promptGit();
341
- handleCancel(gitPrompt);
342
- let gitInitialized = false;
343
- if (gitPrompt) {
344
- const s = spinner3();
1147
+ async function runServerSetup(projectPath) {
1148
+ const appsDir = path13.join(projectPath, "apps");
1149
+ try {
1150
+ await fsPromises11.access(appsDir);
1151
+ } catch {
1152
+ return;
1153
+ }
1154
+ const serverPrompt = await promptServer();
1155
+ handleCancel(serverPrompt);
1156
+ if (serverPrompt) {
1157
+ const s = spinner9();
345
1158
  console.log("\n");
346
- s.start("Initializing Git repository...");
1159
+ s.start("Setting up modular Express backend in apps/server...");
347
1160
  try {
348
- await gitInit(projectPath);
349
- s.stop(pc6.green("\u2714 Success: Git repository initialized"));
350
- gitInitialized = true;
1161
+ await setupServer(projectPath);
1162
+ s.stop(
1163
+ pc15.green(
1164
+ "\u2714 Success: Modular Express backend configured in apps/server"
1165
+ )
1166
+ );
351
1167
  } catch (error) {
352
- s.stop(pc6.red("\u2716 Failed: Git initialization failed"));
353
- console.error(pc6.red(`
1168
+ s.stop(pc15.red("\u2716 Failed: Express backend setup failed"));
1169
+ console.error(pc15.red(`
354
1170
  Error details: ${error.message || error}`));
355
1171
  process.exit(1);
356
1172
  }
357
1173
  console.log("\n");
358
1174
  }
359
- return gitInitialized;
360
1175
  }
361
1176
 
362
1177
  // src/tasks/husky.ts
363
- import { confirm as confirm3, spinner as spinner4 } from "@clack/prompts";
364
- import pc7 from "picocolors";
365
- import fsPromises5 from "fs/promises";
366
- import path5 from "path";
1178
+ import { confirm as confirm7, spinner as spinner10 } from "@clack/prompts";
1179
+ import pc16 from "picocolors";
1180
+ import fsPromises12 from "fs/promises";
1181
+ import path14 from "path";
367
1182
  async function promptHusky() {
368
- return await confirm3({
1183
+ return await confirm7({
369
1184
  message: "Do you want to setup Husky pre-commit hooks?",
370
1185
  initialValue: true
371
1186
  });
@@ -373,10 +1188,10 @@ async function promptHusky() {
373
1188
  async function setupHusky(projectPath) {
374
1189
  await runCommand("pnpm add -D -E husky -w", { cwd: projectPath });
375
1190
  await runCommand("pnpm exec husky init", { cwd: projectPath });
376
- const preCommitPath = path5.join(projectPath, ".husky", "pre-commit");
1191
+ const preCommitPath = path14.join(projectPath, ".husky", "pre-commit");
377
1192
  let hookContent = "";
378
1193
  try {
379
- hookContent = await fsPromises5.readFile(preCommitPath, "utf8");
1194
+ hookContent = await fsPromises12.readFile(preCommitPath, "utf8");
380
1195
  } catch {
381
1196
  hookContent = "pnpm test";
382
1197
  }
@@ -384,9 +1199,9 @@ async function setupHusky(projectPath) {
384
1199
  if (!hookContent.includes("git add .")) {
385
1200
  hookContent = hookContent.trim() + "\ngit add .\n";
386
1201
  }
387
- await fsPromises5.writeFile(preCommitPath, hookContent, "utf8");
1202
+ await fsPromises12.writeFile(preCommitPath, hookContent, "utf8");
388
1203
  try {
389
- await fsPromises5.chmod(preCommitPath, 493);
1204
+ await fsPromises12.chmod(preCommitPath, 493);
390
1205
  } catch {
391
1206
  }
392
1207
  }
@@ -395,15 +1210,15 @@ async function runHuskySetup(projectPath, gitInitialized) {
395
1210
  const huskyPrompt = await promptHusky();
396
1211
  handleCancel(huskyPrompt);
397
1212
  if (huskyPrompt) {
398
- const s = spinner4();
1213
+ const s = spinner10();
399
1214
  console.log("\n");
400
1215
  s.start("Setting up Husky hooks...");
401
1216
  try {
402
1217
  await setupHusky(projectPath);
403
- s.stop(pc7.green("\u2714 Success: Husky pre-commit hooks configured"));
1218
+ s.stop(pc16.green("\u2714 Success: Husky pre-commit hooks configured"));
404
1219
  } catch (error) {
405
- s.stop(pc7.red("\u2716 Failed: Husky setup failed"));
406
- console.error(pc7.red(`
1220
+ s.stop(pc16.red("\u2716 Failed: Husky setup failed"));
1221
+ console.error(pc16.red(`
407
1222
  Error details: ${error.message || error}`));
408
1223
  process.exit(1);
409
1224
  }
@@ -411,52 +1226,167 @@ Error details: ${error.message || error}`));
411
1226
  }
412
1227
  }
413
1228
 
414
- // src/core/endcli.ts
415
- import pc8 from "picocolors";
416
- import { outro } from "@clack/prompts";
417
- import boxen2 from "boxen";
418
- function endCli(name, projectPath) {
419
- outro(pc8.yellow("Morax scaffolding completed successfully!"));
420
- console.log(
421
- boxen2(
422
- `\u26A1 ${pc8.bold("Workspace ready:")} ${pc8.yellow(name)}
423
- Location: ${pc8.cyan(projectPath)}
424
-
425
- ${pc8.bold("To start developing:")}
426
- 1. ${pc8.cyan(`cd ${name}`)}
427
- 2. ${pc8.cyan("pnpm dev")}
428
-
429
- \u{1F4A1} Code formatting & git orchestration is fully active.`,
1229
+ // src/core/web.ts
1230
+ import { select } from "@clack/prompts";
1231
+ async function addweb(projectPath) {
1232
+ const framework = await select({
1233
+ message: "Which frontend website setup would you like to include in apps/web?",
1234
+ options: [
430
1235
  {
431
- padding: 0,
432
- margin: 0,
433
- borderStyle: "round",
434
- borderColor: "yellow",
435
- title: pc8.black(pc8.bold(" Setup Complete ")),
436
- titleAlignment: "center"
1236
+ value: "nextjs",
1237
+ label: "Next.js",
1238
+ hint: "The React Framework for the Web"
1239
+ },
1240
+ {
1241
+ value: "react",
1242
+ label: "React (Vite)",
1243
+ hint: "Vite React Starter Template"
1244
+ },
1245
+ {
1246
+ value: "skip",
1247
+ label: "Skip",
1248
+ hint: "Skip setting up a frontend website"
437
1249
  }
438
- )
439
- );
1250
+ ],
1251
+ initialValue: "nextjs"
1252
+ });
1253
+ handleCancel(framework);
1254
+ if (framework === "nextjs") {
1255
+ await setupNextjs(projectPath);
1256
+ await runShadcnSetup(projectPath);
1257
+ } else if (framework === "react") {
1258
+ await setupReact(projectPath);
1259
+ }
440
1260
  }
441
1261
 
442
1262
  // src/core/runner.ts
443
- async function runWorkspaceScaffolder() {
1263
+ async function runWorkspaceScaffolder(options = {}) {
444
1264
  startCli();
445
- const name = await promptWorkspaceName();
446
- const projectPath = path6.join(process.cwd(), name);
1265
+ const isMonorepo = !options.react && !options.next;
1266
+ const name = await promptWorkspaceName(isMonorepo);
1267
+ const projectPath = path15.join(process.cwd(), name);
1268
+ if (options.react) {
1269
+ await runReactScaffolder(name, projectPath);
1270
+ return;
1271
+ }
1272
+ if (options.next) {
1273
+ await runNextScaffolder(name, projectPath);
1274
+ return;
1275
+ }
447
1276
  await makeDirectories(name, projectPath);
1277
+ await setupReadme(projectPath, name);
448
1278
  const gitInitialized = await runGitSetup(projectPath);
449
- await runPrettierSetup(projectPath);
450
- await runHuskySetup(projectPath, gitInitialized);
1279
+ const selectedApps = await askServerorFrontendInclue();
1280
+ const selectedTools = await askWhichToolToInclude(
1281
+ selectedApps.includes("server")
1282
+ );
1283
+ if (selectedTools.includes("prettier")) {
1284
+ await runPrettierSetup(projectPath);
1285
+ }
1286
+ if (selectedTools.includes("husky")) {
1287
+ await runHuskySetup(projectPath, gitInitialized);
1288
+ }
1289
+ if (selectedTools.includes("eslint")) {
1290
+ await runEslintSetup(projectPath);
1291
+ }
1292
+ if (selectedTools.includes("typescript")) {
1293
+ await runTypescriptSetup(projectPath);
1294
+ }
1295
+ if (selectedApps.includes("server")) {
1296
+ await runServerSetup(projectPath);
1297
+ }
1298
+ if (selectedApps.includes("web")) {
1299
+ await addweb(projectPath);
1300
+ }
1301
+ const s = spinner11();
1302
+ console.log("\n");
1303
+ s.start("Running final workspace package installation...");
1304
+ try {
1305
+ await runCommand("pnpm install", { cwd: projectPath, silent: true });
1306
+ s.stop(
1307
+ pc17.green(
1308
+ "\u2714 Success: Workspace dependencies and symlinks configured successfully"
1309
+ )
1310
+ );
1311
+ } catch (error) {
1312
+ s.stop(pc17.red("\u2716 Warning: Final workspace installation failed"));
1313
+ console.error(pc17.red(`
1314
+ Error details: ${error.message || error}`));
1315
+ }
1316
+ console.log("\n");
451
1317
  endCli(name, projectPath);
452
1318
  }
1319
+ async function askWhichToolToInclude(hasServer) {
1320
+ const options = [
1321
+ {
1322
+ value: "prettier",
1323
+ label: "Prettier",
1324
+ hint: "Code formatter rules & auto-formatting script"
1325
+ },
1326
+ {
1327
+ value: "husky",
1328
+ label: "Husky",
1329
+ hint: "Git pre-commit hooks setup (requires Git)"
1330
+ }
1331
+ ];
1332
+ if (!hasServer) {
1333
+ options.splice(
1334
+ 1,
1335
+ 0,
1336
+ {
1337
+ value: "eslint",
1338
+ label: "ESLint",
1339
+ hint: "Modern Flat Config linter configurations"
1340
+ },
1341
+ {
1342
+ value: "typescript",
1343
+ label: "TypeScript",
1344
+ hint: "Extendable shared TypeScript configurations"
1345
+ }
1346
+ );
1347
+ }
1348
+ const tools = await multiselect({
1349
+ message: hasServer ? "ESLint & TypeScript are required for the Express backend. Select additional configurations/tools:" : "Which configurations/linter tools would you like to set up?",
1350
+ options,
1351
+ required: false
1352
+ });
1353
+ handleCancel(tools);
1354
+ const selectedTools = tools;
1355
+ if (hasServer) {
1356
+ if (!selectedTools.includes("eslint")) selectedTools.push("eslint");
1357
+ if (!selectedTools.includes("typescript")) selectedTools.push("typescript");
1358
+ }
1359
+ return selectedTools;
1360
+ }
1361
+ async function askServerorFrontendInclue() {
1362
+ const apps = await multiselect({
1363
+ message: "Which backend/frontend applications would you like to set up?",
1364
+ options: [
1365
+ {
1366
+ value: "web",
1367
+ label: "Frontend Web App",
1368
+ hint: "Scaffold Next.js or React (Vite) frontend in apps/web"
1369
+ },
1370
+ {
1371
+ value: "server",
1372
+ label: "Backend Server",
1373
+ hint: "Scaffold Express backend in apps/server"
1374
+ }
1375
+ ],
1376
+ required: false
1377
+ });
1378
+ handleCancel(apps);
1379
+ return apps;
1380
+ }
453
1381
 
454
1382
  // src/index.ts
455
1383
  async function main() {
456
- await runWorkspaceScaffolder();
1384
+ const isReactOnly = process.argv.includes("--react") || process.argv.includes("-r");
1385
+ const isNextOnly = process.argv.includes("--nextjs") || process.argv.includes("--next") || process.argv.includes("-n");
1386
+ await runWorkspaceScaffolder({ react: isReactOnly, next: isNextOnly });
457
1387
  }
458
1388
  main().catch((err) => {
459
- console.error(pc9.red("Fatal Error during execution:"), err);
1389
+ console.error(pc18.red("Fatal Error during execution:"), err);
460
1390
  process.exit(1);
461
1391
  });
462
1392
  //# sourceMappingURL=index.js.map