create-morax 1.0.4 → 1.0.15

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,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import pc16 from "picocolors";
4
+ import pc18 from "picocolors";
5
5
 
6
6
  // src/core/runner.ts
7
- import path13 from "path";
8
- import { spinner as spinner9 } from "@clack/prompts";
9
- import pc15 from "picocolors";
7
+ import path15 from "path";
8
+ import { spinner as spinner11, multiselect } from "@clack/prompts";
9
+ import pc17 from "picocolors";
10
10
 
11
11
  // src/core/startcli.ts
12
12
  import gradient from "gradient-string";
@@ -55,21 +55,21 @@ function handleCancel(input) {
55
55
  }
56
56
 
57
57
  // src/core/workspaceName.ts
58
- async function promptWorkspaceName() {
58
+ async function promptWorkspaceName(isMonorepo = true) {
59
59
  const nameInput = await text({
60
- message: "What is the name of your new monorepo workspace?",
61
- 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",
62
62
  validate(value) {
63
63
  if (value && value.includes(" "))
64
- return "Workspace name cannot contain spaces!";
64
+ return isMonorepo ? "Workspace name cannot contain spaces!" : "Project name cannot contain spaces!";
65
65
  }
66
66
  });
67
67
  handleCancel(nameInput);
68
- return String(nameInput).trim() || "morax-workspace";
68
+ return String(nameInput).trim() || (isMonorepo ? "morax-workspace" : "my-app");
69
69
  }
70
70
 
71
71
  // src/tasks/directories.ts
72
- import { multiselect, spinner } from "@clack/prompts";
72
+ import { spinner } from "@clack/prompts";
73
73
  import fsPromises2 from "fs/promises";
74
74
  import path2 from "path";
75
75
  import pc4 from "picocolors";
@@ -162,36 +162,13 @@ async function createDirectories(directories, projectPath) {
162
162
  }
163
163
  }
164
164
  async function makeDirectories(name, projectPath) {
165
- const directories = await multiselect({
166
- message: "Which directories do you want to include in your workspace?",
167
- options: [
168
- {
169
- value: "apps",
170
- label: "apps/*",
171
- hint: "For frontend apps and backend services"
172
- },
173
- {
174
- value: "packages",
175
- label: "packages/*",
176
- hint: "For shared components, configs, and utilities"
177
- }
178
- ],
179
- required: true
180
- });
181
- handleCancel(directories);
165
+ const directories = ["apps", "packages"];
182
166
  const s = spinner();
183
167
  console.log("\n");
184
168
  s.start("Generating workspace configs...");
185
169
  try {
186
- await generateWorkspaceConfig(
187
- name,
188
- directories,
189
- projectPath
190
- );
191
- await createDirectories(
192
- directories,
193
- projectPath
194
- );
170
+ await generateWorkspaceConfig(name, directories, projectPath);
171
+ await createDirectories(directories, projectPath);
195
172
  s.stop(pc4.green("\u2714 Success: Generated Workspace Root & Folder Structures"));
196
173
  } catch (error) {
197
174
  s.stop(pc4.red("\u2716 Failed: Workspace generation failed"));
@@ -203,116 +180,704 @@ Error details: ${error.message || error}`));
203
180
  return directories;
204
181
  }
205
182
 
206
- // src/tasks/prettier.ts
207
- 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
208
189
  import pc5 from "picocolors";
209
190
  import fsPromises3 from "fs/promises";
210
191
  import path3 from "path";
211
- async function promptPrettier() {
212
- return await confirm({
213
- message: "Do you want to setup Prettier code formatting?",
214
- initialValue: true
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
+ });
215
217
  });
216
218
  }
217
- async function setupPrettier(projectPath) {
218
- await runCommand("pnpm add -D -E prettier -w", { cwd: projectPath });
219
- const prettierrc = {
220
- semi: true,
221
- singleQuote: true,
222
- tabWidth: 2,
223
- trailingComma: "all",
224
- printWidth: 100
225
- };
226
- await fsPromises3.writeFile(
227
- path3.join(projectPath, ".prettierrc"),
228
- JSON.stringify(prettierrc, null, 2),
229
- "utf8"
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
+ )
230
226
  );
231
- const prettierignore = [
232
- "# Dependencies",
233
- "node_modules/",
234
- "jspm_packages/",
235
- "web_modules/",
236
- "",
237
- "# Build and outputs",
238
- "dist/",
239
- "build/",
240
- ".next/",
241
- "out/",
242
- ".turbo/",
243
- "",
244
- "# Configuration locks",
245
- "pnpm-lock.yaml",
246
- "",
247
- "# Environment files",
248
- ".env",
249
- ".env.*",
250
- ""
251
- ].join("\n");
252
- await fsPromises3.writeFile(
253
- path3.join(projectPath, ".prettierignore"),
254
- prettierignore,
255
- "utf8"
227
+ console.log(
228
+ pc5.magentaBright(
229
+ ` 1. Install & Start: Choose ${pc5.bold("No")} when asked "Install with npm/pnpm and start now?"`
230
+ )
256
231
  );
257
- const rootPackagePath = path3.join(projectPath, "package.json");
258
- const rootPackageContent = await fsPromises3.readFile(rootPackagePath, "utf8");
259
- const pkg = JSON.parse(rootPackageContent);
260
- pkg.scripts = {
261
- ...pkg.scripts || {},
262
- format: "prettier --write ."
263
- };
264
- await fsPromises3.writeFile(
265
- rootPackagePath,
266
- JSON.stringify(pkg, null, 2),
267
- "utf8"
232
+ console.log(
233
+ pc5.magentaBright(
234
+ "This allows Morax CLI to continue automatically configuring your workspace (ESLint, Prettier, Tailwind, packages, etc.)!"
235
+ )
268
236
  );
269
- await runCommand("pnpm format", { cwd: projectPath });
270
- }
271
- async function runPrettierSetup(projectPath) {
272
- const prettierPrompt = await promptPrettier();
273
- handleCancel(prettierPrompt);
274
- if (prettierPrompt) {
275
- const s = spinner2();
276
- console.log("\n");
277
- s.start("Setting up Prettier auto-formatting...");
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 = [];
278
248
  try {
279
- await setupPrettier(projectPath);
280
- s.stop(pc5.green("\u2714 Success: Prettier formatting configured"));
281
- } catch (error) {
282
- s.stop(pc5.red("\u2716 Failed: Prettier setup failed"));
283
- console.error(pc5.red(`
284
- Error details: ${error.message || error}`));
285
- process.exit(1);
249
+ afterDirs = await fsPromises3.readdir(appsDir);
250
+ } catch {
286
251
  }
287
- console.log("\n");
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);
288
473
  }
289
474
  }
290
475
 
291
- // src/tasks/eslint.ts
476
+ // src/tasks/git.ts
292
477
  import { confirm as confirm2, spinner as spinner3 } from "@clack/prompts";
293
478
  import pc6 from "picocolors";
294
479
  import fsPromises4 from "fs/promises";
295
480
  import path4 from "path";
296
- async function promptEslint() {
481
+ async function promptGit() {
297
482
  return await confirm2({
298
- message: "Do you want to setup ESLint linting in packages/eslint?",
483
+ message: "Do you want to initialize a local Git repository?",
299
484
  initialValue: true
300
485
  });
301
486
  }
302
- async function setupEslint(projectPath) {
303
- const eslintDir = path4.join(projectPath, "packages", "eslint");
304
- await fsPromises4.mkdir(eslintDir, { recursive: true });
305
- await runCommand("pnpm init", { cwd: eslintDir, silent: true });
306
- const eslintPackagePath = path4.join(eslintDir, "package.json");
307
- const eslintPackageRaw = await fsPromises4.readFile(eslintPackagePath, "utf8");
308
- const eslintPkg = JSON.parse(eslintPackageRaw);
309
- eslintPkg.name = "@config/eslint";
310
- eslintPkg.version = "1.0.0";
311
- eslintPkg.private = true;
312
- eslintPkg.type = "module";
313
- eslintPkg.main = "eslint.config.ts";
314
- eslintPkg.exports = {
315
- ".": "./eslint.config.ts"
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";
782
+ async function promptPrettier() {
783
+ return await confirm5({
784
+ message: "Do you want to setup Prettier code formatting?",
785
+ initialValue: true
786
+ });
787
+ }
788
+ async function setupPrettier(projectPath) {
789
+ await runCommand("pnpm add -D -E prettier -w", { cwd: projectPath });
790
+ const prettierrc = {
791
+ semi: true,
792
+ singleQuote: true,
793
+ tabWidth: 2,
794
+ trailingComma: "all",
795
+ printWidth: 100
796
+ };
797
+ await fsPromises8.writeFile(
798
+ path10.join(projectPath, ".prettierrc"),
799
+ JSON.stringify(prettierrc, null, 2),
800
+ "utf8"
801
+ );
802
+ const prettierignore = [
803
+ "# Dependencies",
804
+ "node_modules/",
805
+ "jspm_packages/",
806
+ "web_modules/",
807
+ "",
808
+ "# Build and outputs",
809
+ "dist/",
810
+ "build/",
811
+ ".next/",
812
+ "out/",
813
+ ".turbo/",
814
+ "",
815
+ "# Configuration locks",
816
+ "pnpm-lock.yaml",
817
+ "",
818
+ "# Environment files",
819
+ ".env",
820
+ ".env.*",
821
+ ""
822
+ ].join("\n");
823
+ await fsPromises8.writeFile(
824
+ path10.join(projectPath, ".prettierignore"),
825
+ prettierignore,
826
+ "utf8"
827
+ );
828
+ const rootPackagePath = path10.join(projectPath, "package.json");
829
+ const rootPackageContent = await fsPromises8.readFile(rootPackagePath, "utf8");
830
+ const pkg = JSON.parse(rootPackageContent);
831
+ pkg.scripts = {
832
+ ...pkg.scripts || {},
833
+ format: "prettier --write ."
834
+ };
835
+ await fsPromises8.writeFile(
836
+ rootPackagePath,
837
+ JSON.stringify(pkg, null, 2),
838
+ "utf8"
839
+ );
840
+ await runCommand("pnpm format", { cwd: projectPath });
841
+ }
842
+ async function runPrettierSetup(projectPath) {
843
+ const prettierPrompt = await promptPrettier();
844
+ handleCancel(prettierPrompt);
845
+ if (prettierPrompt) {
846
+ const s = spinner6();
847
+ console.log("\n");
848
+ s.start("Setting up Prettier auto-formatting...");
849
+ try {
850
+ await setupPrettier(projectPath);
851
+ s.stop(pc12.green("\u2714 Success: Prettier formatting configured"));
852
+ } catch (error) {
853
+ s.stop(pc12.red("\u2716 Failed: Prettier setup failed"));
854
+ console.error(pc12.red(`
855
+ Error details: ${error.message || error}`));
856
+ process.exit(1);
857
+ }
858
+ console.log("\n");
859
+ }
860
+ }
861
+
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"
316
881
  };
317
882
  eslintPkg.scripts = {
318
883
  lint: "eslint ."
@@ -320,7 +885,7 @@ async function setupEslint(projectPath) {
320
885
  delete eslintPkg.keywords;
321
886
  delete eslintPkg.author;
322
887
  delete eslintPkg.license;
323
- await fsPromises4.writeFile(
888
+ await fsPromises9.writeFile(
324
889
  eslintPackagePath,
325
890
  JSON.stringify(eslintPkg, null, 2),
326
891
  "utf8"
@@ -360,69 +925,57 @@ async function setupEslint(projectPath) {
360
925
  ");",
361
926
  ""
362
927
  ].join("\n");
363
- await fsPromises4.writeFile(
364
- path4.join(eslintDir, "eslint.config.ts"),
928
+ await fsPromises9.writeFile(
929
+ path11.join(eslintDir, "eslint.config.ts"),
365
930
  eslintConfigContent,
366
931
  "utf8"
367
932
  );
368
- const rootPackagePath = path4.join(projectPath, "package.json");
369
- const rootPackageContent = await fsPromises4.readFile(rootPackagePath, "utf8");
933
+ const rootPackagePath = path11.join(projectPath, "package.json");
934
+ const rootPackageContent = await fsPromises9.readFile(rootPackagePath, "utf8");
370
935
  const rootPkg = JSON.parse(rootPackageContent);
371
936
  rootPkg.scripts = {
372
937
  ...rootPkg.scripts || {},
373
938
  lint: "pnpm --filter @config/eslint lint"
374
939
  };
375
- await fsPromises4.writeFile(
940
+ await fsPromises9.writeFile(
376
941
  rootPackagePath,
377
942
  JSON.stringify(rootPkg, null, 2),
378
943
  "utf8"
379
944
  );
380
945
  }
381
946
  async function runEslintSetup(projectPath) {
382
- const packagesDir = path4.join(projectPath, "packages");
947
+ const packagesDir = path11.join(projectPath, "packages");
383
948
  try {
384
- await fsPromises4.access(packagesDir);
949
+ await fsPromises9.access(packagesDir);
385
950
  } catch {
386
951
  return;
387
952
  }
388
- const eslintPrompt = await promptEslint();
389
- handleCancel(eslintPrompt);
390
- if (eslintPrompt) {
391
- const s = spinner3();
392
- console.log("\n");
393
- s.start("Setting up modular ESLint in packages/eslint...");
394
- try {
395
- await setupEslint(projectPath);
396
- s.stop(
397
- pc6.green("\u2714 Success: Modular ESLint configured in packages/eslint")
398
- );
399
- } catch (error) {
400
- s.stop(pc6.red("\u2716 Failed: ESLint setup failed"));
401
- console.error(pc6.red(`
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(`
402
962
  Error details: ${error.message || error}`));
403
- process.exit(1);
404
- }
405
- console.log("\n");
406
- }
407
- }
408
-
409
- // src/tasks/typescript.ts
410
- import { confirm as confirm3, spinner as spinner4 } from "@clack/prompts";
411
- import pc7 from "picocolors";
412
- import fsPromises5 from "fs/promises";
413
- import path5 from "path";
414
- async function promptTypescript() {
415
- return await confirm3({
416
- message: "Do you want to setup TypeScript config in packages/typescript?",
417
- initialValue: true
418
- });
963
+ process.exit(1);
964
+ }
965
+ console.log("\n");
419
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";
420
973
  async function setupTypescript(projectPath) {
421
- const tsDir = path5.join(projectPath, "packages", "typescript");
422
- await fsPromises5.mkdir(tsDir, { recursive: true });
974
+ const tsDir = path12.join(projectPath, "packages", "typescript");
975
+ await fsPromises10.mkdir(tsDir, { recursive: true });
423
976
  await runCommand("pnpm init", { cwd: tsDir, silent: true });
424
- const tsPackagePath = path5.join(tsDir, "package.json");
425
- const tsPackageRaw = await fsPromises5.readFile(tsPackagePath, "utf8");
977
+ const tsPackagePath = path12.join(tsDir, "package.json");
978
+ const tsPackageRaw = await fsPromises10.readFile(tsPackagePath, "utf8");
426
979
  const tsPkg = JSON.parse(tsPackageRaw);
427
980
  tsPkg.name = "@config/typescript";
428
981
  tsPkg.version = "1.0.0";
@@ -436,7 +989,7 @@ async function setupTypescript(projectPath) {
436
989
  delete tsPkg.license;
437
990
  delete tsPkg.main;
438
991
  delete tsPkg.scripts;
439
- await fsPromises5.writeFile(
992
+ await fsPromises10.writeFile(
440
993
  tsPackagePath,
441
994
  JSON.stringify(tsPkg, null, 2),
442
995
  "utf8"
@@ -448,58 +1001,54 @@ async function setupTypescript(projectPath) {
448
1001
  await runCommand("npx tsc --init", { cwd: tsDir });
449
1002
  }
450
1003
  async function runTypescriptSetup(projectPath) {
451
- const packagesDir = path5.join(projectPath, "packages");
1004
+ const packagesDir = path12.join(projectPath, "packages");
452
1005
  try {
453
- await fsPromises5.access(packagesDir);
1006
+ await fsPromises10.access(packagesDir);
454
1007
  } catch {
455
1008
  return;
456
1009
  }
457
- const tsPrompt = await promptTypescript();
458
- handleCancel(tsPrompt);
459
- if (tsPrompt) {
460
- const s = spinner4();
461
- console.log("\n");
462
- s.start("Setting up modular TypeScript config in packages/typescript...");
463
- try {
464
- await setupTypescript(projectPath);
465
- s.stop(
466
- pc7.green(
467
- "\u2714 Success: Modular TypeScript config configured in packages/typescript"
468
- )
469
- );
470
- } catch (error) {
471
- s.stop(pc7.red("\u2716 Failed: TypeScript config setup failed"));
472
- console.error(pc7.red(`
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(`
473
1023
  Error details: ${error.message || error}`));
474
- process.exit(1);
475
- }
476
- console.log("\n");
1024
+ process.exit(1);
477
1025
  }
1026
+ console.log("\n");
478
1027
  }
479
1028
 
480
1029
  // src/tasks/server.ts
481
- import { confirm as confirm4, spinner as spinner5 } from "@clack/prompts";
482
- import pc8 from "picocolors";
483
- import fsPromises6 from "fs/promises";
484
- import path6 from "path";
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";
485
1034
  async function promptServer() {
486
- return await confirm4({
487
- message: "Do you want to setup a basic Express backend in apps/server?",
1035
+ return await confirm6({
1036
+ message: "Do you want to setup a Express backend in apps/server?",
488
1037
  initialValue: true
489
1038
  });
490
1039
  }
491
1040
  async function setupServer(projectPath) {
492
- const serverDir = path6.join(projectPath, "apps", "server");
493
- const srcDir = path6.join(serverDir, "src");
494
- await fsPromises6.mkdir(srcDir, { recursive: true });
1041
+ const serverDir = path13.join(projectPath, "apps", "server");
1042
+ const srcDir = path13.join(serverDir, "src");
1043
+ await fsPromises11.mkdir(srcDir, { recursive: true });
495
1044
  await runCommand("pnpm init", { cwd: serverDir, silent: true });
496
- const serverPackagePath = path6.join(serverDir, "package.json");
497
- const serverPackageRaw = await fsPromises6.readFile(serverPackagePath, "utf8");
1045
+ const serverPackagePath = path13.join(serverDir, "package.json");
1046
+ const serverPackageRaw = await fsPromises11.readFile(serverPackagePath, "utf8");
498
1047
  const serverPkg = JSON.parse(serverPackageRaw);
499
- const eslintDir = path6.join(projectPath, "packages", "eslint");
1048
+ const eslintDir = path13.join(projectPath, "packages", "eslint");
500
1049
  let hasEslint = false;
501
1050
  try {
502
- await fsPromises6.access(eslintDir);
1051
+ await fsPromises11.access(eslintDir);
503
1052
  hasEslint = true;
504
1053
  } catch {
505
1054
  }
@@ -523,7 +1072,7 @@ async function setupServer(projectPath) {
523
1072
  delete serverPkg.keywords;
524
1073
  delete serverPkg.author;
525
1074
  delete serverPkg.license;
526
- await fsPromises6.writeFile(
1075
+ await fsPromises11.writeFile(
527
1076
  serverPackagePath,
528
1077
  JSON.stringify(serverPkg, null, 2),
529
1078
  "utf8"
@@ -536,8 +1085,8 @@ async function setupServer(projectPath) {
536
1085
  },
537
1086
  include: ["src/**/*"]
538
1087
  };
539
- await fsPromises6.writeFile(
540
- path6.join(serverDir, "tsconfig.json"),
1088
+ await fsPromises11.writeFile(
1089
+ path13.join(serverDir, "tsconfig.json"),
541
1090
  JSON.stringify(tsconfigContent, null, 2),
542
1091
  "utf8"
543
1092
  );
@@ -553,8 +1102,8 @@ async function setupServer(projectPath) {
553
1102
  "];",
554
1103
  ""
555
1104
  ].join("\n");
556
- await fsPromises6.writeFile(
557
- path6.join(serverDir, "eslint.config.ts"),
1105
+ await fsPromises11.writeFile(
1106
+ path13.join(serverDir, "eslint.config.ts"),
558
1107
  eslintConfigContent,
559
1108
  "utf8"
560
1109
  );
@@ -589,586 +1138,115 @@ async function setupServer(projectPath) {
589
1138
  "});",
590
1139
  ""
591
1140
  ].join("\n");
592
- await fsPromises6.writeFile(
593
- path6.join(srcDir, "index.ts"),
1141
+ await fsPromises11.writeFile(
1142
+ path13.join(srcDir, "index.ts"),
594
1143
  indexTsContent,
595
1144
  "utf8"
596
1145
  );
597
1146
  }
598
1147
  async function runServerSetup(projectPath) {
599
- const appsDir = path6.join(projectPath, "apps");
1148
+ const appsDir = path13.join(projectPath, "apps");
600
1149
  try {
601
- await fsPromises6.access(appsDir);
1150
+ await fsPromises11.access(appsDir);
602
1151
  } catch {
603
1152
  return;
604
1153
  }
605
1154
  const serverPrompt = await promptServer();
606
1155
  handleCancel(serverPrompt);
607
1156
  if (serverPrompt) {
608
- const s = spinner5();
1157
+ const s = spinner9();
609
1158
  console.log("\n");
610
1159
  s.start("Setting up modular Express backend in apps/server...");
611
1160
  try {
612
1161
  await setupServer(projectPath);
613
1162
  s.stop(
614
- pc8.green(
615
- "\u2714 Success: Modular Express backend configured in apps/server"
616
- )
617
- );
618
- } catch (error) {
619
- s.stop(pc8.red("\u2716 Failed: Express backend setup failed"));
620
- console.error(pc8.red(`
621
- Error details: ${error.message || error}`));
622
- process.exit(1);
623
- }
624
- console.log("\n");
625
- }
626
- }
627
-
628
- // src/tasks/git.ts
629
- import { confirm as confirm5, spinner as spinner6 } from "@clack/prompts";
630
- import pc9 from "picocolors";
631
- import fsPromises7 from "fs/promises";
632
- import path7 from "path";
633
- async function promptGit() {
634
- return await confirm5({
635
- message: "Do you want to initialize a local Git repository?",
636
- initialValue: true
637
- });
638
- }
639
- async function gitInit(projectPath) {
640
- await runCommand("git init", { cwd: projectPath, silent: true });
641
- const gitignore = [
642
- "# Dependency directories",
643
- "node_modules/",
644
- "jspm_packages/",
645
- "web_modules/",
646
- "",
647
- "# Build and output outputs",
648
- "dist/",
649
- "build/",
650
- ".next/",
651
- "out/",
652
- ".turbo/",
653
- "",
654
- "# Environments",
655
- ".env",
656
- ".env.local",
657
- ".env.development.local",
658
- ".env.test.local",
659
- ".env.production.local",
660
- "",
661
- "# Logs",
662
- "npm-debug.log*",
663
- "yarn-debug.log*",
664
- "yarn-error.log*",
665
- "pnpm-debug.log*",
666
- "*.log",
667
- "",
668
- "# OS Metadata",
669
- ".DS_Store",
670
- "Thumbs.db",
671
- ""
672
- ].join("\n");
673
- await fsPromises7.writeFile(
674
- path7.join(projectPath, ".gitignore"),
675
- gitignore,
676
- "utf8"
677
- );
678
- }
679
- async function runGitSetup(projectPath) {
680
- const gitPrompt = await promptGit();
681
- handleCancel(gitPrompt);
682
- let gitInitialized = false;
683
- if (gitPrompt) {
684
- const s = spinner6();
685
- console.log("\n");
686
- s.start("Initializing Git repository...");
687
- try {
688
- await gitInit(projectPath);
689
- s.stop(pc9.green("\u2714 Success: Git repository initialized"));
690
- gitInitialized = true;
691
- } catch (error) {
692
- s.stop(pc9.red("\u2716 Failed: Git initialization failed"));
693
- console.error(pc9.red(`
694
- Error details: ${error.message || error}`));
695
- process.exit(1);
696
- }
697
- console.log("\n");
698
- }
699
- return gitInitialized;
700
- }
701
-
702
- // src/tasks/husky.ts
703
- import { confirm as confirm6, spinner as spinner7 } from "@clack/prompts";
704
- import pc10 from "picocolors";
705
- import fsPromises8 from "fs/promises";
706
- import path8 from "path";
707
- async function promptHusky() {
708
- return await confirm6({
709
- message: "Do you want to setup Husky pre-commit hooks?",
710
- initialValue: true
711
- });
712
- }
713
- async function setupHusky(projectPath) {
714
- await runCommand("pnpm add -D -E husky -w", { cwd: projectPath });
715
- await runCommand("pnpm exec husky init", { cwd: projectPath });
716
- const preCommitPath = path8.join(projectPath, ".husky", "pre-commit");
717
- let hookContent = "";
718
- try {
719
- hookContent = await fsPromises8.readFile(preCommitPath, "utf8");
720
- } catch {
721
- hookContent = "pnpm test";
722
- }
723
- hookContent = hookContent.replace("pnpm test", "pnpm format");
724
- if (!hookContent.includes("git add .")) {
725
- hookContent = hookContent.trim() + "\ngit add .\n";
726
- }
727
- await fsPromises8.writeFile(preCommitPath, hookContent, "utf8");
728
- try {
729
- await fsPromises8.chmod(preCommitPath, 493);
730
- } catch {
731
- }
732
- }
733
- async function runHuskySetup(projectPath, gitInitialized) {
734
- if (!gitInitialized) return;
735
- const huskyPrompt = await promptHusky();
736
- handleCancel(huskyPrompt);
737
- if (huskyPrompt) {
738
- const s = spinner7();
739
- console.log("\n");
740
- s.start("Setting up Husky hooks...");
741
- try {
742
- await setupHusky(projectPath);
743
- s.stop(pc10.green("\u2714 Success: Husky pre-commit hooks configured"));
744
- } catch (error) {
745
- s.stop(pc10.red("\u2716 Failed: Husky setup failed"));
746
- console.error(pc10.red(`
747
- Error details: ${error.message || error}`));
748
- process.exit(1);
749
- }
750
- console.log("\n");
751
- }
752
- }
753
-
754
- // src/core/endcli.ts
755
- import pc11 from "picocolors";
756
- import { outro } from "@clack/prompts";
757
- import boxen2 from "boxen";
758
- function endCli(name, projectPath) {
759
- outro(pc11.yellow("Morax scaffolding completed successfully!"));
760
- console.log(
761
- boxen2(
762
- `\u26A1 ${pc11.bold("Workspace ready:")} ${pc11.yellow(name)}
763
- Location: ${pc11.cyan(projectPath)}
764
-
765
- ${pc11.bold("To start developing:")}
766
- 1. ${pc11.cyan(`cd ${name}`)}
767
- 2. ${pc11.cyan("pnpm dev")}
768
-
769
- \u{1F4A1} Code formatting & git orchestration is fully active.`,
770
- {
771
- padding: 0,
772
- margin: 0,
773
- borderStyle: "round",
774
- borderColor: "yellow",
775
- title: pc11.black(pc11.bold(" Setup Complete ")),
776
- titleAlignment: "center"
777
- }
778
- )
779
- );
780
- }
781
-
782
- // src/tasks/addreadme.ts
783
- import fsPromises9 from "fs/promises";
784
- import path9 from "path";
785
- async function setupReadme(projectPath, workspaceName) {
786
- const readmePath = path9.join(projectPath, "README.md");
787
- const readmeContent = [
788
- `# Morax `,
789
- "",
790
- "Welcome to your next-generation, high-performance monorepo workspace generated by **Morax**.",
791
- "",
792
- "## Workspace Structure",
793
- "",
794
- "- **`apps/`** \u2014 Frontend applications, backend servers, and user-facing services.",
795
- "- **`packages/`** \u2014 Shared modules, TypeScript configurations, ESLint rule sets, and common utility libraries.",
796
- "",
797
- "## Key Commands",
798
- "",
799
- "Run the following commands from the root directory of your workspace:",
800
- "",
801
- "### Development",
802
- "Start all dev servers and hot-reloading configurations concurrently:",
803
- "```bash",
804
- "pnpm dev",
805
- "```",
806
- "",
807
- "### Linting",
808
- "Run static analysis across all workspaces using your shared ESLint rules:",
809
- "```bash",
810
- "pnpm lint",
811
- "```",
812
- "",
813
- "### Formatting",
814
- "Automatically format all codebase files using Prettier:",
815
- "```bash",
816
- "pnpm format",
817
- "```",
818
- "",
819
- "---",
820
- "",
821
- "*Generated with love by [Morax Scaffolder CLI](https://github.com/Elitedv/morax).*",
822
- ""
823
- ].join("\n");
824
- await fsPromises9.writeFile(readmePath, readmeContent, "utf8");
825
- }
826
-
827
- // src/core/web.ts
828
- import { select } from "@clack/prompts";
829
-
830
- // src/tasks/shadcn.ts
831
- import { confirm as confirm7 } from "@clack/prompts";
832
- import pc12 from "picocolors";
833
- import fsPromises10 from "fs/promises";
834
- import path10 from "path";
835
- import { spawn } from "child_process";
836
- async function promptShadcn() {
837
- return await confirm7({
838
- message: "Do you want to setup shadcn UI in your Next.js website (apps/web)?",
839
- initialValue: true
840
- });
841
- }
842
- function runInteractiveCommand(command, args, cwd) {
843
- return new Promise((resolve, reject) => {
844
- const child = spawn(command, args, {
845
- cwd,
846
- stdio: "inherit",
847
- shell: true,
848
- env: { ...process.env, NODE_NO_WARNINGS: "1" }
849
- });
850
- child.on("close", (code) => {
851
- if (code === 0) {
852
- resolve();
853
- } else {
854
- reject(new Error(`Command "${command} ${args.join(" ")}" exited with code ${code}`));
855
- }
856
- });
857
- child.on("error", (err) => {
858
- reject(err);
859
- });
860
- });
861
- }
862
- async function setupShadcn(projectPath) {
863
- const webDir = path10.join(projectPath, "apps", "web");
864
- console.log(pc12.cyan("\nStarting interactive shadcn UI initialization..."));
865
- await runInteractiveCommand("pnpm", ["dlx", "shadcn@latest", "init", "--template", "next"], webDir);
866
- }
867
- async function runShadcnSetup(projectPath) {
868
- const webDir = path10.join(projectPath, "apps", "web");
869
- try {
870
- await fsPromises10.access(webDir);
871
- } catch {
872
- return;
873
- }
874
- const shadcnPrompt = await promptShadcn();
875
- handleCancel(shadcnPrompt);
876
- if (shadcnPrompt) {
877
- try {
878
- await setupShadcn(projectPath);
879
- console.log(pc12.green("\n\u2714 Success: shadcn UI successfully initialized in apps/web\n"));
1163
+ pc15.green(
1164
+ "\u2714 Success: Modular Express backend configured in apps/server"
1165
+ )
1166
+ );
880
1167
  } catch (error) {
881
- console.error(pc12.red(`
882
- \u2716 Failed: shadcn UI setup failed`));
883
- console.error(pc12.red(`Error details: ${error.message || error}
884
- `));
1168
+ s.stop(pc15.red("\u2716 Failed: Express backend setup failed"));
1169
+ console.error(pc15.red(`
1170
+ Error details: ${error.message || error}`));
885
1171
  process.exit(1);
886
1172
  }
1173
+ console.log("\n");
887
1174
  }
888
1175
  }
889
1176
 
890
- // src/tasks/nextjs.ts
891
- import { confirm as confirm8 } from "@clack/prompts";
892
- import pc13 from "picocolors";
893
- import fsPromises11 from "fs/promises";
894
- import path11 from "path";
895
- import { spawn as spawn2 } from "child_process";
896
- function runInteractiveCommand2(command, args, cwd) {
897
- return new Promise((resolve, reject) => {
898
- const child = spawn2(command, args, {
899
- cwd,
900
- stdio: "inherit",
901
- shell: true,
902
- env: { ...process.env, NODE_NO_WARNINGS: "1" }
903
- });
904
- child.on("close", (code) => {
905
- if (code === 0) {
906
- resolve();
907
- } else {
908
- reject(
909
- new Error(
910
- `Command "${command} ${args.join(" ")}" exited with code ${code}`
911
- )
912
- );
913
- }
914
- });
915
- child.on("error", (err) => {
916
- reject(err);
917
- });
1177
+ // src/tasks/husky.ts
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";
1182
+ async function promptHusky() {
1183
+ return await confirm7({
1184
+ message: "Do you want to setup Husky pre-commit hooks?",
1185
+ initialValue: true
918
1186
  });
919
1187
  }
920
- async function setupNextjs(projectPath) {
921
- const appsDir = path11.join(projectPath, "apps");
922
- const webDir = path11.join(appsDir, "web");
1188
+ async function setupHusky(projectPath) {
1189
+ await runCommand("pnpm add -D -E husky -w", { cwd: projectPath });
1190
+ await runCommand("pnpm exec husky init", { cwd: projectPath });
1191
+ const preCommitPath = path14.join(projectPath, ".husky", "pre-commit");
1192
+ let hookContent = "";
923
1193
  try {
924
- await fsPromises11.rm(webDir, { recursive: true, force: true });
1194
+ hookContent = await fsPromises12.readFile(preCommitPath, "utf8");
925
1195
  } catch {
1196
+ hookContent = "pnpm test";
926
1197
  }
927
- console.log(pc13.cyan("\nStarting interactive Next.js application setup..."));
928
- await runInteractiveCommand2(
929
- "npx",
930
- ["create-next-app@latest", "web"],
931
- appsDir
932
- );
933
- }
934
-
935
- // src/tasks/react.ts
936
- import pc14 from "picocolors";
937
- import fsPromises12 from "fs/promises";
938
- import path12 from "path";
939
- import { spawn as spawn3 } from "child_process";
940
- import { confirm as confirm9, spinner as spinner8, text as text2 } from "@clack/prompts";
941
- function runInteractiveCommand3(command, args, cwd) {
942
- return new Promise((resolve, reject) => {
943
- const child = spawn3(command, args, {
944
- cwd,
945
- stdio: "inherit",
946
- shell: true,
947
- env: { ...process.env, NODE_NO_WARNINGS: "1" }
948
- });
949
- child.on("close", (code) => {
950
- if (code === 0) {
951
- resolve();
952
- } else {
953
- reject(
954
- new Error(
955
- `Command "${command} ${args.join(" ")}" exited with code ${code}`
956
- )
957
- );
958
- }
959
- });
960
- child.on("error", (err) => {
961
- reject(err);
962
- });
963
- });
964
- }
965
- async function setupReact(projectPath) {
966
- const appsDir = path12.join(projectPath, "apps");
967
- console.log(pc14.magentaBright("\n\u{1F537} IMPORTANT INSTRUCTIONS:"));
968
- console.log(pc14.magentaBright("During the Vite interactive configuration prompts, please select:"));
969
- console.log(pc14.magentaBright(` 1. Install & Start: Choose ${pc14.bold("No")} when asked "Install with npm/pnpm and start now?"`));
970
- console.log(pc14.magentaBright("This allows Morax CLI to continue automatically configuring your workspace (ESLint, Prettier, Tailwind, packages, etc.)!"));
971
- console.log(pc14.cyan("\nStarting interactive Vite React application setup..."));
972
- let beforeDirs = [];
1198
+ hookContent = hookContent.replace("pnpm test", "pnpm format");
1199
+ if (!hookContent.includes("git add .")) {
1200
+ hookContent = hookContent.trim() + "\ngit add .\n";
1201
+ }
1202
+ await fsPromises12.writeFile(preCommitPath, hookContent, "utf8");
973
1203
  try {
974
- beforeDirs = await fsPromises12.readdir(appsDir);
1204
+ await fsPromises12.chmod(preCommitPath, 493);
975
1205
  } catch {
976
1206
  }
977
- try {
978
- await runInteractiveCommand3(
979
- "pnpm",
980
- ["create", "vite@latest"],
981
- appsDir
982
- );
983
- let afterDirs = [];
984
- try {
985
- afterDirs = await fsPromises12.readdir(appsDir);
986
- } catch {
987
- }
988
- const newDirs = afterDirs.filter((d) => !beforeDirs.includes(d));
989
- let createdDirName = "web";
990
- if (newDirs.length > 0) {
991
- createdDirName = newDirs[0];
992
- }
993
- const webDir = path12.join(appsDir, createdDirName);
1207
+ }
1208
+ async function runHuskySetup(projectPath, gitInitialized) {
1209
+ if (!gitInitialized) return;
1210
+ const huskyPrompt = await promptHusky();
1211
+ handleCancel(huskyPrompt);
1212
+ if (huskyPrompt) {
1213
+ const s = spinner10();
1214
+ console.log("\n");
1215
+ s.start("Setting up Husky hooks...");
994
1216
  try {
995
- await fsPromises12.access(webDir);
996
- } catch {
997
- throw new Error(`Vite React application directory "apps/${createdDirName}" was not found. Please ensure you complete the Vite installation.`);
998
- }
999
- console.log(
1000
- pc14.green(`
1001
- \u2714 Success: Vite React frontend configured in apps/${createdDirName}
1002
- `)
1003
- );
1004
- const setupTailwindPrompt = await confirm9({
1005
- message: "Do you want to install and configure Tailwind CSS v4?",
1006
- initialValue: true
1007
- });
1008
- handleCancel(setupTailwindPrompt);
1009
- const setupShadcnPrompt = await confirm9({
1010
- message: `Do you want to setup shadcn UI in your Vite React website (apps/${createdDirName})?`,
1011
- initialValue: true
1012
- });
1013
- handleCancel(setupShadcnPrompt);
1014
- const needsTailwind = setupTailwindPrompt || setupShadcnPrompt;
1015
- if (needsTailwind) {
1016
- const s = spinner8();
1017
- s.start("Installing Tailwind CSS v4, Vite integration, and Node types...");
1018
- try {
1019
- const pkgPath = path12.join(webDir, "package.json");
1020
- try {
1021
- const pkgRaw = await fsPromises12.readFile(pkgPath, "utf8");
1022
- const pkg = JSON.parse(pkgRaw);
1023
- pkg.dependencies = pkg.dependencies || {};
1024
- pkg.devDependencies = pkg.devDependencies || {};
1025
- pkg.dependencies["tailwindcss"] = "^4.0.0";
1026
- pkg.devDependencies["@tailwindcss/vite"] = "^4.0.0";
1027
- pkg.devDependencies["@types/node"] = "^20.11.0";
1028
- await fsPromises12.writeFile(pkgPath, JSON.stringify(pkg, null, 2), "utf8");
1029
- } catch {
1030
- }
1031
- await runCommand("pnpm install", { cwd: webDir, silent: true });
1032
- s.message("Configuring Vite plugins and Tailwind CSS imports...");
1033
- const viteConfigPath = path12.join(webDir, "vite.config.ts");
1034
- try {
1035
- let content = await fsPromises12.readFile(viteConfigPath, "utf8");
1036
- if (!content.includes("@tailwindcss/vite")) {
1037
- content = "import tailwindcss from '@tailwindcss/vite';\n" + content;
1038
- if (content.includes("plugins: [react()]")) {
1039
- content = content.replace("plugins: [react()]", "plugins: [react(), tailwindcss()]");
1040
- } else if (content.includes("plugins: [react(),]")) {
1041
- content = content.replace("plugins: [react(),]", "plugins: [react(), tailwindcss()]");
1042
- } else {
1043
- content = content.replace(/(plugins:\s*\[\s*react\(\),?\s*)(\])/g, "$1\n tailwindcss(),\n $2");
1044
- }
1045
- await fsPromises12.writeFile(viteConfigPath, content, "utf8");
1046
- }
1047
- } catch {
1048
- }
1049
- const indexCssPath = path12.join(webDir, "src", "index.css");
1050
- try {
1051
- await fsPromises12.writeFile(indexCssPath, '@import "tailwindcss";\n', "utf8");
1052
- } catch {
1053
- }
1054
- s.stop(pc14.green("\u2714 Success: Tailwind CSS v4 configured successfully\n"));
1055
- } catch (err) {
1056
- s.stop(pc14.red("\u2716 Failed: Tailwind CSS v4 setup failed"));
1057
- throw err;
1058
- }
1059
- }
1060
- if (setupShadcnPrompt) {
1061
- const s = spinner8();
1062
- s.start("Configuring path aliases for shadcn UI preflight checks...");
1063
- try {
1064
- const tsconfigPaths = [
1065
- path12.join(webDir, "tsconfig.json"),
1066
- path12.join(webDir, "tsconfig.app.json")
1067
- ];
1068
- for (const tsPath of tsconfigPaths) {
1069
- try {
1070
- let content = await fsPromises12.readFile(tsPath, "utf8");
1071
- if (content.includes('"compilerOptions"')) {
1072
- if (!content.includes('"paths"')) {
1073
- content = content.replace(
1074
- /("compilerOptions"\s*:\s*\{)/,
1075
- `$1
1076
- "baseUrl": ".",
1077
- "paths": {
1078
- "@/*": ["./src/*"]
1079
- },`
1080
- );
1081
- await fsPromises12.writeFile(tsPath, content, "utf8");
1082
- }
1083
- } else {
1084
- content = content.replace(
1085
- /^(\s*\{)/,
1086
- `$1
1087
- "compilerOptions": {
1088
- "baseUrl": ".",
1089
- "paths": {
1090
- "@/*": ["./src/*"]
1091
- }
1092
- },`
1093
- );
1094
- await fsPromises12.writeFile(tsPath, content, "utf8");
1095
- }
1096
- } catch {
1097
- }
1098
- }
1099
- const viteConfigPath = path12.join(webDir, "vite.config.ts");
1100
- try {
1101
- let content = await fsPromises12.readFile(viteConfigPath, "utf8");
1102
- if (!content.includes("import path from 'path'")) {
1103
- content = "import path from 'path';\n" + content;
1104
- }
1105
- if (!content.includes("resolve:")) {
1106
- content = content.replace(
1107
- /(plugins:\s*\[[^\]]*\]),?/,
1108
- `$1,
1109
- resolve: {
1110
- alias: {
1111
- "@": path.resolve(__dirname, "./src"),
1112
- },
1113
- }`
1114
- );
1115
- }
1116
- await fsPromises12.writeFile(viteConfigPath, content, "utf8");
1117
- } catch {
1118
- }
1119
- s.stop(pc14.green("\u2714 Success: Path aliases successfully configured\n"));
1120
- } catch (err) {
1121
- s.stop(pc14.red("\u2716 Failed: Path alias configuration failed"));
1122
- throw err;
1123
- }
1124
- const hasPreset = await confirm9({
1125
- message: "Do you have a custom shadcn UI preset code?",
1126
- initialValue: false
1127
- });
1128
- handleCancel(hasPreset);
1129
- let presetCode = "";
1130
- if (hasPreset) {
1131
- const enteredPreset = await text2({
1132
- message: "Enter your custom shadcn UI preset code:",
1133
- placeholder: "e.g. 123456",
1134
- validate: (val) => {
1135
- if (!val || !val.trim()) return "Preset code cannot be empty";
1136
- return;
1137
- }
1138
- });
1139
- handleCancel(enteredPreset);
1140
- presetCode = enteredPreset.trim();
1141
- }
1142
- const args = ["dlx", "shadcn@latest", "init"];
1143
- if (presetCode) {
1144
- args.push("--preset", presetCode);
1145
- }
1146
- args.push("--template", "vite");
1147
- console.log(pc14.cyan("\nStarting interactive shadcn UI initialization..."));
1148
- await runInteractiveCommand3("pnpm", args, webDir);
1149
- console.log(
1150
- pc14.green(`
1151
- \u2714 Success: shadcn UI successfully initialized in apps/${createdDirName}
1152
- `)
1153
- );
1217
+ await setupHusky(projectPath);
1218
+ s.stop(pc16.green("\u2714 Success: Husky pre-commit hooks configured"));
1219
+ } catch (error) {
1220
+ s.stop(pc16.red("\u2716 Failed: Husky setup failed"));
1221
+ console.error(pc16.red(`
1222
+ Error details: ${error.message || error}`));
1223
+ process.exit(1);
1154
1224
  }
1155
- } catch (error) {
1156
- console.error(pc14.red(`
1157
- \u2716 Failed: Vite React frontend setup failed`));
1158
- console.error(pc14.red(`Error details: ${error.message || error}
1159
- `));
1160
- process.exit(1);
1225
+ console.log("\n");
1161
1226
  }
1162
1227
  }
1163
1228
 
1164
1229
  // src/core/web.ts
1230
+ import { select } from "@clack/prompts";
1165
1231
  async function addweb(projectPath) {
1166
1232
  const framework = await select({
1167
1233
  message: "Which frontend website setup would you like to include in apps/web?",
1168
1234
  options: [
1169
- { value: "nextjs", label: "Next.js", hint: "The React Framework for the Web" },
1170
- { value: "react", label: "React (Vite)", hint: "Vite React Starter Template" },
1171
- { value: "skip", label: "Skip", hint: "Skip setting up a frontend website" }
1235
+ {
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"
1249
+ }
1172
1250
  ],
1173
1251
  initialValue: "nextjs"
1174
1252
  });
@@ -1182,44 +1260,133 @@ async function addweb(projectPath) {
1182
1260
  }
1183
1261
 
1184
1262
  // src/core/runner.ts
1185
- async function runWorkspaceScaffolder() {
1263
+ async function runWorkspaceScaffolder(options = {}) {
1186
1264
  startCli();
1187
- const name = await promptWorkspaceName();
1188
- const projectPath = path13.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
+ }
1189
1276
  await makeDirectories(name, projectPath);
1190
1277
  await setupReadme(projectPath, name);
1191
1278
  const gitInitialized = await runGitSetup(projectPath);
1192
- await runPrettierSetup(projectPath);
1193
- await runEslintSetup(projectPath);
1194
- await runTypescriptSetup(projectPath);
1195
- await runHuskySetup(projectPath, gitInitialized);
1196
- await runServerSetup(projectPath);
1197
- await addweb(projectPath);
1198
- const s = spinner9();
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();
1199
1302
  console.log("\n");
1200
1303
  s.start("Running final workspace package installation...");
1201
1304
  try {
1202
1305
  await runCommand("pnpm install", { cwd: projectPath, silent: true });
1203
1306
  s.stop(
1204
- pc15.green(
1307
+ pc17.green(
1205
1308
  "\u2714 Success: Workspace dependencies and symlinks configured successfully"
1206
1309
  )
1207
1310
  );
1208
1311
  } catch (error) {
1209
- s.stop(pc15.red("\u2716 Warning: Final workspace installation failed"));
1210
- console.error(pc15.red(`
1312
+ s.stop(pc17.red("\u2716 Warning: Final workspace installation failed"));
1313
+ console.error(pc17.red(`
1211
1314
  Error details: ${error.message || error}`));
1212
1315
  }
1213
1316
  console.log("\n");
1214
1317
  endCli(name, projectPath);
1215
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
+ }
1216
1381
 
1217
1382
  // src/index.ts
1218
1383
  async function main() {
1219
- await runWorkspaceScaffolder();
1384
+ const isReactOnly = process.argv.includes("--react") || process.argv.includes("-r") || process.env.npm_config_react === "true" || process.env.npm_config_r === "true";
1385
+ const isNextOnly = process.argv.includes("--nextjs") || process.argv.includes("--next") || process.argv.includes("-n") || process.env.npm_config_nextjs === "true" || process.env.npm_config_next === "true" || process.env.npm_config_n === "true";
1386
+ await runWorkspaceScaffolder({ react: isReactOnly, next: isNextOnly });
1220
1387
  }
1221
1388
  main().catch((err) => {
1222
- console.error(pc16.red("Fatal Error during execution:"), err);
1389
+ console.error(pc18.red("Fatal Error during execution:"), err);
1223
1390
  process.exit(1);
1224
1391
  });
1225
1392
  //# sourceMappingURL=index.js.map